diff --git a/CHANGES.rst b/CHANGES.rst index 58396ae..988e571 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,9 +14,6 @@ tip (unreleased) - Removed deprecated ``ChoiceEnum``, ``InheritanceCastModel``, ``InheritanceCastManager``, and ``manager_from``. -- Added ``UpdateOrCreateMixin`` for custom queryset subclasses. Thanks Antti - Kaihola. - - Fixed pickling of ``PassThroughManager``. Thanks Rinat Shigapov. - Set ``use_for_related_fields = True`` on ``QueryManager``. diff --git a/README.rst b/README.rst index 1316766..8aad83e 100644 --- a/README.rst +++ b/README.rst @@ -389,34 +389,3 @@ directly on the manager:: Post.objects.by_author(user=request.user).unpublished() - -UpdateOrCreateMixin -=================== - -`Ticket #3182`_ in Django's bug tracker suggests -an ``update_or_create()`` method for managers and querysets. -The method works in a similar way as ``get_or_create()``, -but in addition to returning an object if it's found in the database, -it updates the object's fields according to the ``defaults`` keyword argument. - -.. _`Ticket #3182`: https://code.djangoproject.com/ticket/3182 - -Use this mixin in a custom ``QuerySet`` subclass, and create a corresponding -manager with ``PassThroughManager``:: - - from datetime import datetime - from django.db import models - from django.db.models.query import QuerySet - from model_utils.managers import UpdateOrCreateMixin - - class PostQuerySet(QuerySet, UpdateOrCreateMixin): - pass - - class Post(models.Model): - user = models.ForeignKey(User) - published = models.DateTimeField() - - objects = PassThroughManager.for_queryset_class(PostQuerySet)() - - Post.objects.update_or_create(user=request.user, - defaults={'published': datetime.now()}) diff --git a/model_utils/managers.py b/model_utils/managers.py index a2c3358..60aae40 100644 --- a/model_utils/managers.py +++ b/model_utils/managers.py @@ -153,42 +153,3 @@ def create_pass_through_manager_for_queryset_class(base, queryset_cls): def unpickle_pass_through_manager_for_queryset_class(base, queryset_cls): cls = create_pass_through_manager_for_queryset_class(base, queryset_cls) return cls.__new__(cls) - - -class UpdateOrCreateMixin(object): - def update_or_create(self, **kwargs): - """ - Looks up an object with the given kwargs, creating one if necessary. - If the object already exists, then its fields are updated with the - values passed in the defaults dictionary. - Returns a tuple of (object, created), where created is a boolean - specifying whether an object was created. - - See https://code.djangoproject.com/ticket/3182 - """ - assert kwargs, \ - 'update_or_create() must be passed at least one keyword argument' - defaults = kwargs.pop('defaults', {}) - lookup = kwargs.copy() - for f in self.model._meta.fields: - if f.attname in lookup: - lookup[f.name] = lookup.pop(f.attname) - self._for_write = True - sid = transaction.savepoint(using=self.db) - try: - obj = self.get(**lookup) - create = False - except self.model.DoesNotExist: - params = dict([(k, v) for k, v in kwargs.items() if '__' not in k]) - obj = self.model(**params) - create = True - for attname, value in defaults.items(): - setattr(obj, attname, value) - try: - obj.save(force_insert=create, using=self.db) - transaction.savepoint_commit(sid, using=self.db) - return obj, create - except IntegrityError: - transaction.savepoint_rollback(sid, using=self.db) - exc_info = sys.exc_info() - raise exc_info[1], None, exc_info[2] diff --git a/model_utils/tests/models.py b/model_utils/tests/models.py index 27fd4a1..881ed7d 100644 --- a/model_utils/tests/models.py +++ b/model_utils/tests/models.py @@ -2,7 +2,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils.models import TimeStampedModel, StatusModel, TimeFramedModel -from model_utils.managers import QueryManager, InheritanceManager, PassThroughManager, UpdateOrCreateMixin +from model_utils.managers import QueryManager, InheritanceManager, PassThroughManager from model_utils.fields import SplitField, MonitorField from model_utils import Choices @@ -215,22 +215,3 @@ class Spot(models.Model): owner = models.ForeignKey(Dude, related_name='spots_owned') objects = PassThroughManager.for_queryset_class(SpotQuerySet)() - - -class PersonQuerySet(models.query.QuerySet, UpdateOrCreateMixin): - pass - - -class Person(models.Model): - first_name = models.CharField(max_length=100) - last_name = models.CharField(max_length=100) - birthday = models.DateField() - - objects = PassThroughManager(PersonQuerySet) - - def __str__(self): - return '%s %s, Birthday: %s' % (self.first_name, self.last_name, - self.birthday) - - class Meta: - ordering = ('last_name',) diff --git a/model_utils/tests/tests.py b/model_utils/tests/tests.py index b049bd5..bf7637d 100644 --- a/model_utils/tests/tests.py +++ b/model_utils/tests/tests.py @@ -2,7 +2,7 @@ from __future__ import with_statement import pickle -from datetime import date, datetime, timedelta +from datetime import datetime, timedelta from django.db import models from django.db.models.fields import FieldDoesNotExist @@ -18,8 +18,7 @@ from model_utils.tests.models import ( InheritanceManagerTestParent, InheritanceManagerTestChild1, InheritanceManagerTestChild2, TimeStamp, Post, Article, Status, StatusPlainTuple, TimeFrame, Monitored, StatusManagerAdded, - TimeFrameManagerAdded, Dude, SplitFieldAbstractParent, Car, Spot, - Person) + TimeFrameManagerAdded, Dude, SplitFieldAbstractParent, Car, Spot) @@ -576,52 +575,3 @@ class CreatePassThroughManagerTests(TestCase): pickled_qs = pickle.dumps(qs) unpickled_qs = pickle.loads(pickled_qs) self.assertEqual(unpickled_qs.secured().count(), 1) - - -class UpdateOrCreateTests(TestCase): - def test_update_or_create(self): - Person.objects.create( - first_name='John', last_name='Lennon', birthday=date(1940, 10, 9) - ) - - p, created = Person.objects.update_or_create( - first_name='John', last_name='Lennon', defaults={ - 'birthday': date(1970, 10, 9) - } - ) - self.assertFalse(created) - self.assertEqual(Person.objects.count(), 1) - self.assertEqual(Person.objects.get().birthday, date(1970, 10, 9)) - - p, created = Person.objects.update_or_create( - first_name='George', last_name='Harrison', defaults={ - 'birthday': date(1943, 2, 25) - } - ) - self.assertTrue(created) - self.assertEqual(Person.objects.count(), 2) - - # If we execute the exact same statement, it won't create a Person. - p, created = Person.objects.update_or_create( - first_name='George', last_name='Harrison', defaults={ - 'birthday': date(1943, 2, 25) - } - ) - self.assertFalse(created) - self.assertEqual(Person.objects.count(), 2) - - # update_or_create() can take an empty 'defaults' parameter, but in - # this situation behaves exactly like get_or_create(). This is useful - # if you are building the 'defaults' dictionary dynamically. - p, created = Person.objects.update_or_create( - first_name='George', last_name='Harrison', defaults={} - ) - self.assertFalse(created) - - # A different name with an empty 'defaults'. - p, created = Person.objects.update_or_create( - first_name='John', last_name='Smith', birthday=date(1950, 2, 10), - defaults={} - ) - self.assertTrue(created) - self.assertEqual(Person.objects.count(), 3)