6

I'm using django-cities-light (lighter version of django-cities) with Django 1.8.x. It defines the abstract models of Country, Region/State and City, so that we can extend and add custom fields. For example, we can add timezone to city by writing a post_import signal handler as explained here.

Likewise I need to add the field capital to each country. I'm not much familiar in GeoDjango and I knew that django-cities app's Country has the capital field.

Babu
  • 2,548
  • 3
  • 30
  • 47

2 Answers2

6

You need to setup a custom Country model. Lets say you have an app 'mygeonames' with models.py:

import cities_light

from django.db import models

from cities_light.settings import ICountry
from cities_light.receivers import connect_default_signals
from cities_light.abstract_models import (AbstractCountry, AbstractRegion,
    AbstractCity)

class Country(AbstractCountry):
    capital = models.CharField(max_length=50)
connect_default_signals(Country)


class Region(AbstractRegion):
    pass
connect_default_signals(Region)


class City(AbstractCity):
    pass
connect_default_signals(City)


def process_country_import(sender, instance, items, **kwargs):
    instance.capital = items[ICountry.capital]

cities_light.signals.country_items_post_import.connect(process_country_import)

Then in settings.py you should specify CITIES_LIGHT_APP_NAME = 'mygeonames', and put both apps 'cities_light' and 'mygeonames' to INSTALLED_APPS

After that you can migrate your DB and run ./manage.py cities_light

At the end you should get something like this:

In [1]: from mygeonames.models import Country
In [2]: cc = Country.objects.all()
In [3]: cc[0].capital
Out[3]: u'Paris'

But you might want to link with Cities table instead.

irqed
  • 649
  • 4
  • 15
  • Perfect! `ICountry.capital` was the key I was looking for. And yes, I have to query for the City object with the capital name. – Babu Jun 22 '15 at 14:41
  • Thanks a lot! Great info, I've used the same approach to add .neighbours to countries and it works like a charm The only thing I'd like to add that at least for my case you have to populate the new table first with the process_country_import() commented – ElRey777 Apr 19 '20 at 13:30
2

here's an extended idea on @irqed answer:

class City(AbstractCity):
    is_capital = models.BooleanField()

class Country(AbstractCountry):
    def capital(self):
        return self.city_set.filter(is_capital=True)

*Note that I'm not familiar with that package (I'm just assuming they used city_set as a related name)

Why? well, to me capital seems to make more sense as an attribute for a city. It might also save you some time when trying to work with City objects (say you want to check if a city is a Capital - you don't need to do another query on a different table and compare the names, you just check an already fetched boolean field)

yuvi
  • 18,155
  • 8
  • 56
  • 93
  • To my requirement the answer by @irqed suits well. BTW, it would be useful if you update the answer to set `is_capital` to `True` or `False`. Is their a way to do that while populating the city model through the management command provided by city-light app? – Babu Jun 26 '15 at 05:48
  • I'm not familiar with the app but if you can prepopulate information there I'm sure you can do the same for a boolean field – yuvi Jun 26 '15 at 15:10