Source code for cities_light.models

import six
import re

from django.utils.encoding import python_2_unicode_compatible

from django.utils.encoding import force_text
from django.db.models import signals
from django.db import models
from django.utils.translation import ugettext_lazy as _

from unidecode import unidecode

import autoslug

from .settings import *

__all__ = ['Country', 'Region', 'City', 'CONTINENT_CHOICES', 'to_search',

ALPHA_REGEXP = re.compile('[\W_]+', re.UNICODE)

    ('OC', _('Oceania')),
    ('EU', _('Europe')),
    ('AF', _('Africa')),
    ('NA', _('North America')),
    ('AN', _('Antarctica')),
    ('SA', _('South America')),
    ('AS', _('Asia')),

def to_ascii(value):
    if not six.PY3 and isinstance(value, str):
        value = force_text(value)

    return unidecode(value)

def to_search(value):

    Signal reciever that sets instance.name_ascii from

    Ascii versions of names are often useful for autocompletes and search.
    name_ascii = to_ascii(

    if not name_ascii.strip():

    if name_ascii and not instance.name_ascii:
        instance.name_ascii = to_ascii(

def set_display_name(sender, instance=None, **kwargs):
    Set instance.display_name to instance.get_display_name(), avoid spawning
    queries during __str__().
    instance.display_name = instance.get_display_name()

class Base(models.Model):
    Base model with boilerplate for all models.

    name_ascii = models.CharField(max_length=200, blank=True, db_index=True)
    slug = autoslug.AutoSlugField(populate_from='name_ascii')
    geoname_id = models.IntegerField(null=True, blank=True, unique=True)
    alternate_names = models.TextField(null=True, blank=True, default='')

    class Meta:
        abstract = True
        ordering = ['name']

    def __str__(self):
        display_name = getattr(self, 'display_name', None)
        if display_name:
            return display_name

class Country(Base):
[docs] Country model. """ name = models.CharField(max_length=200, unique=True) code2 = models.CharField(max_length=2, null=True, blank=True, unique=True) code3 = models.CharField(max_length=3, null=True, blank=True, unique=True) continent = models.CharField(max_length=2, db_index=True, choices=CONTINENT_CHOICES) tld = models.CharField(max_length=5, blank=True, db_index=True) phone = models.CharField(max_length=20, null=True) class Meta(Base.Meta): verbose_name_plural = _('countries') signals.pre_save.connect(set_name_ascii, sender=Country)
class Region(Base): """
[docs] Region/State model. """ name = models.CharField(max_length=200, db_index=True) display_name = models.CharField(max_length=200) geoname_code = models.CharField(max_length=50, null=True, blank=True, db_index=True) country = models.ForeignKey(Country) class Meta(Base.Meta): unique_together = (('country', 'name'), ('country', 'slug')) verbose_name = _('region/state') verbose_name_plural = _('regions/states') def get_display_name(self): return '%s, %s' % (, signals.pre_save.connect(set_name_ascii, sender=Region) signals.pre_save.connect(set_display_name, sender=Region)
class ToSearchTextField(models.TextField): """ Trivial TextField subclass that passes values through to_search automatically. """ def get_prep_lookup(self, lookup_type, value): """ Return the value passed through to_search(). """ value = super(ToSearchTextField, self).get_prep_lookup(lookup_type, value) return to_search(value) def south_field_triple(self): "Returns a suitable description of this field for South." from south.modelsinspector import introspector field_class = self.__class__.__module__ + "." + self.__class__.__name__ args, kwargs = introspector(self) # That's our definition! return (field_class, args, kwargs) class City(Base): """
[docs] City model. """ name = models.CharField(max_length=200, db_index=True) display_name = models.CharField(max_length=200) search_names = ToSearchTextField(max_length=4000, db_index=INDEX_SEARCH_NAMES, blank=True, default='') latitude = models.DecimalField(max_digits=8, decimal_places=5, null=True, blank=True) longitude = models.DecimalField(max_digits=8, decimal_places=5, null=True, blank=True) region = models.ForeignKey(Region, blank=True, null=True) country = models.ForeignKey(Country) population = models.BigIntegerField(null=True, blank=True, db_index=True) feature_code = models.CharField(max_length=10, null=True, blank=True, db_index=True) class Meta(Base.Meta): unique_together = (('region', 'name'), ('region', 'slug')) verbose_name_plural = _('cities') def get_display_name(self): if self.region_id: return '%s, %s, %s' % (,, else: return '%s, %s' % (, signals.pre_save.connect(set_name_ascii, sender=City) signals.pre_save.connect(set_display_name, sender=City)
def city_country(sender, instance, **kwargs): if instance.region_id and not instance.country_id: = signals.pre_save.connect(city_country, sender=City) def city_search_names(sender, instance, **kwargs): search_names = [] country_names = [] if country_names +=',') city_names = [] if instance.alternate_names: city_names += instance.alternate_names.split(',') if instance.region_id: region_names = [] if instance.region.alternate_names: region_names += instance.region.alternate_names.split(',') for city_name in city_names: for country_name in country_names: name = to_search(city_name + country_name) if name not in search_names: search_names.append(name) if instance.region_id: for region_name in region_names: name = to_search(city_name + region_name + country_name) if name not in search_names: search_names.append(name) instance.search_names = ' '.join(search_names) signals.pre_save.connect(city_search_names, sender=City)