Skip to content

Commit

Permalink
Use backport of Django 1.7's update_or_create.
Browse files Browse the repository at this point in the history
Django 1.7.1 calls the built-in version by default on related managers,
so move to using a version that matches its behaviour more precisely.
  • Loading branch information
dracos committed Nov 28, 2014
1 parent 8228192 commit e197f22
Show file tree
Hide file tree
Showing 10 changed files with 109 additions and 51 deletions.
4 changes: 2 additions & 2 deletions mapit/management/commands/mapit_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ def verbose(*args):

if options['commit']:
m.save()
m.names.update_or_create({ 'type': name_type }, { 'name': name })
m.names.update_or_create(type=name_type, defaults={ 'name': name })
if code:
m.codes.update_or_create({ 'type': code_type }, { 'code': code })
m.codes.update_or_create(type=code_type, defaults={ 'code': code })
save_polygons({ m.id : (m, poly) })

116 changes: 88 additions & 28 deletions mapit/managers.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,92 @@
import sys

import django
from django.contrib.gis.db import models
from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError
from django.utils import six

try:
# Django 1.5+
from django.db.models.constants import LOOKUP_SEP
except ImportError:
# Django <= 1.4
from django.db.models.sql.constants import LOOKUP_SEP


# A copy of Django 1.7's built-in function, except for the 1.6+ transaction
# aspects. And with create() used instead of self.model() & obj.save(), as in
# older versions that passes the related ID in correctly.

# Given unique look-up attributes, and extra data attributes,
# either updates the entry referred to if it exists, or
# creates it if it doesn't.
# Returns string describing what has happened.
def update_or_create(self, filter_attrs, attrs):
def _create_object_from_params(self, lookup, params):
"""
Tries to create an object using passed params.
Used by get_or_create and update_or_create
"""
try:
obj = self.get(**filter_attrs)
changed = False
for k, v in attrs.items():
if obj.__dict__[k] != v:
changed = True
obj.__dict__[k] = v
if changed:
obj.save()
return 'updated'
return 'unchanged'
except ObjectDoesNotExist:
attrs.update(filter_attrs)
self.create(**attrs)
return 'created'

class GeoManager(models.GeoManager):
def update_or_create(self, filter_attrs, attrs):
return update_or_create(self, filter_attrs, attrs)

class Manager(models.Manager):
def update_or_create(self, filter_attrs, attrs):
return update_or_create(self, filter_attrs, attrs)
obj = self.create(**params)
return obj, True
except IntegrityError:
exc_info = sys.exc_info()
try:
return self.get(**lookup), False
except self.model.DoesNotExist:
pass
six.reraise(*exc_info)


def _extract_model_params(self, defaults, **kwargs):
"""
Prepares `lookup` (kwargs that are valid model attributes), `params`
(for creating a model instance) based on given kwargs; for use by
get_or_create and update_or_create.
"""
defaults = defaults or {}
lookup = kwargs.copy()
for f in self.model._meta.fields:
if f.attname in lookup:
lookup[f.name] = lookup.pop(f.attname)
params = dict((k, v) for k, v in kwargs.items() if LOOKUP_SEP not in k)
params.update(defaults)
return lookup, params


def update_or_create(self, defaults=None, **kwargs):
"""
Looks up an object with the given kwargs, updating one with defaults
if it exists, otherwise creates a new one.
Returns a tuple (object, created), where created is a boolean
specifying whether an object was created.
"""
defaults = defaults or {}
lookup, params = _extract_model_params(self, defaults, **kwargs)
self._for_write = True
try:
obj = self.get(**lookup)
except self.model.DoesNotExist:
obj, created = _create_object_from_params(self, lookup, params)
if created:
return obj, created
for k, v in six.iteritems(defaults):
setattr(obj, k, v)

obj.save(using=self.db)
return obj, False


# Django 1.7 added a built-in update_or_create function.
if django.VERSION < (1, 7):
class GeoManager(models.GeoManager):
def update_or_create(self, defaults=None, **lookup):
return update_or_create(self, defaults, **lookup)

class Manager(models.Manager):
def update_or_create(self, defaults=None, **lookup):
return update_or_create(self, defaults, **lookup)

else:

class GeoManager(models.GeoManager):
pass

class Manager(models.Manager):
pass
4 changes: 2 additions & 2 deletions mapit_gb/management/commands/mapit_UK_fix_2012-05.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def handle_label(self, filename, **options):
)
if options['commit']:
m.save()
m.names.update_or_create({ 'type': name_type }, { 'name': name })
m.codes.update_or_create({ 'type': code_version }, { 'code': ons_code })
m.names.update_or_create(type=name_type, defaults={'name': name})
m.codes.update_or_create(type=code_version, defaults={'code': ons_code})
save_polygons({ ons_code: (m, [feat.geom]) })

4 changes: 2 additions & 2 deletions mapit_gb/management/commands/mapit_UK_fix_2013-10.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ def handle_label(self, filename, **options):
new_area = self.make_new_area(name, ons_code, area_code, code_version, 11, 20, country)
if new_area and options['commit']:
new_area.save()
new_area.names.update_or_create({ 'type': name_type }, { 'name': name })
new_area.codes.update_or_create({ 'type': code_version }, { 'code': ons_code })
new_area.names.update_or_create(type=name_type, defaults={ 'name': name })
new_area.codes.update_or_create(type=code_version, defaults={ 'code': ons_code })
save_polygons({ ons_code: (new_area, [feat.geom]) })

def make_new_area(self, name, ons_code, area_code, code_version, generation_low, generation_high, country):
Expand Down
4 changes: 2 additions & 2 deletions mapit_gb/management/commands/mapit_UK_import_2011_scotparl.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,11 @@ def handle_label(self, filename, **options):
poly = [ feat.geom ]

if options['commit']:
m.names.update_or_create({ 'type': name_type }, { 'name': name })
m.names.update_or_create(type=name_type, defaults={ 'name': name })
if ons_code:
self.ons_code_to_shape[ons_code] = (m, poly)
if options['commit']:
m.codes.update_or_create({ 'type': code_type }, { 'code': ons_code })
m.codes.update_or_create(type=code_type, defaults={ 'code': ons_code })

if options['commit']:
save_polygons(self.ons_code_to_shape)
Expand Down
6 changes: 3 additions & 3 deletions mapit_gb/management/commands/mapit_UK_import_boundary_line.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,15 +142,15 @@ def handle_label(self, filename, **options):
poly = [ feat.geom ]

if options['commit']:
m.names.update_or_create({ 'type': name_type }, { 'name': name })
m.names.update_or_create(type=name_type, defaults={ 'name': name })
if ons_code:
self.ons_code_to_shape[ons_code] = (m, poly)
if options['commit']:
m.codes.update_or_create({ 'type': code_version }, { 'code': ons_code })
m.codes.update_or_create(type=code_version, defaults={ 'code': ons_code })
if unit_id:
self.unit_id_to_shape[unit_id] = (m, poly)
if options['commit']:
m.codes.update_or_create({ 'type': code_type_os }, { 'code': unit_id })
m.codes.update_or_create(type=code_type_os, defaults={ 'code': unit_id })

if options['commit']:
save_polygons(self.unit_id_to_shape)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,11 @@ def handle_label(self, filename, **options):
poly = [ f ]

if options['commit']:
m.names.update_or_create({ 'type': name_type }, { 'name': name })
m.names.update_or_create(type=name_type, defaults={ 'name': name })
if ons_code:
self.ons_code_to_shape[ons_code] = (m, poly)
if options['commit']:
m.codes.update_or_create({ 'type': code_type }, { 'code': ons_code })
m.codes.update_or_create(type=code_type, defaults={ 'code': ons_code })

if options['commit']:
save_polygons(self.ons_code_to_shape)
Expand Down
4 changes: 2 additions & 2 deletions mapit_gb/management/commands/mapit_UK_import_soa.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ def handle_label(self, filename, **options):
generation_high = generation,
)
m.save()
m.names.update_or_create({ 'type': NameType.objects.get(code='S') }, { 'name': name })
m.codes.update_or_create({ 'type': CodeType.objects.get(code='ons') }, { 'code': lsoa_code })
m.names.update_or_create(type=NameType.objects.get(code='S'), defaults={ 'name': name })
m.codes.update_or_create(type=CodeType.objects.get(code='ons'), defaults={ 'code': lsoa_code })

p = feat.geom
if p.geom_name == 'POLYGON':
Expand Down
6 changes: 2 additions & 4 deletions mapit_global/management/commands/mapit_global_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,10 @@ def verbose(s):
old_lang_codes.discard(lang)

# Otherwise, make sure that a NameType for this language exists:
NameType.objects.update_or_create({'code': lang},
{'code': lang,
'description': language_name})
NameType.objects.update_or_create(code=lang, defaults={'description': language_name})
name_type = NameType.objects.get(code=lang)

m.names.update_or_create({ 'type': name_type }, { 'name': translated_name })
m.names.update_or_create(type=name_type, defaults={ 'name': translated_name })

if old_lang_codes:
verbose('Removing deleted languages codes: ' + ' '.join(old_lang_codes))
Expand Down
8 changes: 4 additions & 4 deletions mapit_no/management/commands/mapit_NO_import_osm.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,10 @@ def update_or_create():
m.save()
for k, v in kml_data.data[name].items():
if k in ('name:smi', 'name:fi'):
lang = 'N' + k[5:]
m.names.update_or_create({ 'type': NameType.objects.get(code=lang) }, { 'name': v })
m.codes.update_or_create({ 'type': code_type_n5000 }, { 'code': code_str })
m.codes.update_or_create({ 'type': code_type_osm }, { 'code': int(kml_data.data[name]['osm']) })
lang = 'N' + k[5:]
m.names.update_or_create(type=NameType.objects.get(code=lang), defaults={ 'name': v })
m.codes.update_or_create(type=code_type_n5000, defaults={ 'code': code_str })
m.codes.update_or_create(type=code_type_osm, defaults={ 'code': int(kml_data.data[name]['osm']) })
save_polygons({ code : (m, poly) })

update_or_create()
Expand Down

0 comments on commit e197f22

Please sign in to comment.