-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use backport of Django 1.7's update_or_create.
Django 1.7.1 calls the built-in version by default on related managers, so move to using a verison that matches its behaviour more precisely.
- Loading branch information
Showing
10 changed files
with
109 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters