Skip to content

Unitary Councils

Will Roper edited this page Jan 28, 2026 · 6 revisions

How to make a new Unitary Council in EveryElection

There are going to be more of these, so I thought I'd keep track of what I did for the Surrey ones.

1. Sort the reg codes

Go to https://github.com/mysociety/uk_local_authority_names_and_codes/ and suggest some new reg codes. They should be three capital letters, and be unique. example issue

2. Make the organisation, organisation geography and elected role.

There is a command for this: every_election/apps/organisations/management/commands/create_unitary_authority.py

python manage.py create_unitary_authority \
      --official-name 'West Surrey Council' \
      --common-name 'West Surrey' \
      --identifier WSY \
      --constituent-councils E07000209 E07000212 E07000213 E07000214 E07000216 E07000217 \
      --slug west-surrey \
      --start-date 2026-07-01

3. Make the divisions

For the next command you will need to prepare a csv, and upload to s3. Example here: s3://ee.boundary-reviews.production/west-surrey/The Surrey (Structural Changes) Order 2026/divisions.csv To make this I copied the schedule from the legislation, and then csvified it in my editor.

You also need the id (as in db pk).

You can get this with:

uv run \
    manage.py \
    shell \
    -c 'from organisations.models.organisations import Organisation; print(Organisation.objects.get(official_identifier="WSY").pk)'

Then you can run something like::

uv run python manage.py create_divisions_for_new_org \
  -s 'west-surrey/The Surrey (Structural Changes) Order 2026/divisions.csv' \
  --organisation 532 \
  --division-type UTW \
  --short-title "The Surrey (Structural Changes) Order 2026"

At this point you should see the organisation and divisionset with divisions on the frontend.

The final step is to build some pmtiles for it:

python manage.py update_pmtiles --divset-ids 842

4. Set end dates

Set end dates on any organisations that are being replaced by the new Unitary Council.

2022 Unitaries

The code here is probably superseded by the code in the two commands used above.

It seems sensible to have a record of how these were made. I did this with a jupyter notebook, so I've copied the relevant bits here. The formatting is horrible, so might be easier to click edit and copy the source.

Somerset

Includes making divisions from the old county divisions, but also a couple of the new divisions had to be made from district wards.

somerset_county = Organisation.objects.get(official_identifier="SOM") somerset_county_divset = OrganisationDivisionSet.objects.get(id=368) somerset_county_divset.end_date = datetime.datetime.strptime( "2022-05-04", "%Y-%m-%d" ).date() somerset_county_divset.save()

if somerset_county.divisionset.latest().end_date is None: somerset_county.divisionset.latest().delete()

new_divset = OrganisationDivisionSet.objects.create( **{ "start_date": "2022-05-05", "organisation_id": somerset_county.id, "legislation_url": "TBC", "short_title": "The Somerset (Structural Changes) Order 2022", } ) new_divset.save()

somerset_divs = [ div for div in somerset_county_divset.divisions.all() if div.slug != "glastonbury-and-street" ] for div in somerset_divs: div.pk = None div.official_identifier = ":".join( [ new_divset.organisation.official_identifier, div.slug, ] ) div.seats_total = 2 div.divisionset = new_divset div.save()

copy the geographies

geographies = [ (div.slug, div.geography) for div in somerset_county_divset.divisions.all() if div.slug != "glastonbury-and-street" ] for slug, geog in geographies: div = OrganisationDivision.objects.get(slug=slug, divisionset=new_divset)

geog.pk = None
geog.division_id = div.id
geog.save()
# attach it to the target division
div.geography = geog
div.save()

make Street and Glastonbury

new_divisions = { "glastonbury": { "division": { "slug": "glastonbury", "official_identifier": "SOM:glastonbury", "division_type": "CED", "division_subtype": "County council electoral division", "name": "Glastonbury", "division_election_sub_type": "", "divisionset_id": new_divset.id, "seats_total": 2, }, "geographies": [ d.geography.geography for d in OrganisationDivision.objects.filter( official_identifier__in=[ "gss:E05006775", "gss:E05006777", "gss:E05006778", "gss:E05006776", ] ) ], }, "street": { "division": { "slug": "street", "official_identifier": "SOM:street", "division_type": "CED", "division_subtype": "County council electoral division", "name": "Street", "division_election_sub_type": "", "divisionset_id": new_divset.id, "seats_total": 2, }, "geographies": [ d.geography.geography for d in OrganisationDivision.objects.filter( official_identifier__in=[ "gss:E05006786", "gss:E05006787", "gss:E05006788", ] ) ], }, }

for value in new_divisions.values(): new_div = OrganisationDivision.objects.create(**value["division"]) new_div.save() geog = DivisionGeography.objects.create( division=new_div, geography=union_list(value["geographies"]), source="unkown", ) geog.save()

div.geography = geog
new_div.save()
Cumberland

Includes making divisions from the divisions.

cumbria_county = Organisation.objects.get(official_identifier="CMA") cumbria_county_divset = OrganisationDivisionSet.objects.get(id=123)

cumberland = Organisation.objects.create( **{ "official_identifier": "CBD", "organisation_type": "local-authority", "organisation_subtype": "UA", "official_name": "Cumberland Council", "common_name": "Cumberland", "slug": "cumberland", "territory_code": "ENG", "election_name": "Cumberland local election", "start_date": "2022-05-05", "legislation_url": "https://www.legislation.gov.uk/uksi/2022/331/made", } )

cumberland.save()

cumberland_divset = OrganisationDivisionSet.objects.create( **{ "start_date": "2022-05-05", "organisation_id": cumberland.id, "legislation_url": "https://www.legislation.gov.uk/uksi/2022/331/made", "short_title": "The Cumbria (Structural Changes) Order 2022", } ) cumberland_divset.save()

cumberland_div_names = [ "Aspatria", "Belah", "Belle Vue", "Botcherby", "Bothel and Wharrels", "Brampton", "Bransty", "Castle", "Cleator Moor East and Frizington", "Cleator Moor West", "Cockermouth North", "Cockermouth South", "Corby and Hayton", "Currock", "Dalston and Burgh", "Dearham and Broughton", "Denton Holme", "Egremont", "Egremont North and St Bees", "Gosforth", "Harraby North", "Harraby South", "Harrington", "Hillcrest and Hensingham", "Houghton and Irthington", "Howgate", "Kells and Sandwith", "Keswick", "Longtown", "Maryport North", "Maryport South", "Millom", "Millom Without", "Mirehouse", "Morton", "Moss Bay and Moorclose", "Seaton", "Solway Coast", "St Michael's", "St John's and Great Clifton", "Stanwix Urban", "Thursby", "Upperby", "Wetheral", "Wigton", "Yewdale", ]

cumberland_divs = [ div for div in cumbria_county_divset.divisions.all() if div.name in cumberland_div_names ]

print( [ n for n in cumberland_div_names if n not in [d.name for d in cumberland_divs] ] ) for div in cumberland_divs: old_div_id = div.pk div.pk = None div.official_identifier = ":".join( [ cumberland_divset.organisation.official_identifier, div.slug, ] ) div.seats_total = 1 div.divisionset = cumberland_divset div.division_type = "UTW" div.division_subtype = "" div.save()

geog = DivisionGeography.objects.get(division_id=old_div_id)
geog.pk = None
geog.division_id = div.pk
geog.save()
div.geography = geog
div.save()

cumberland_geographies = [ d.geography.geography for d in cumberland_divset.divisions.all() ] cumberland_geography = OrganisationGeography.objects.create( **{ "organisation_id": cumberland.id, "start_date": cumberland.start_date, "legislation_url": cumberland.legislation_url, "geography": union_list(cumberland_geographies), } ) cumberland_geography.save() cumberland.geography = cumberland_geography cumberland.save() #%%

Clone this wiki locally