mirror of
https://github.com/Hopiu/django-model-utils.git
synced 2026-03-16 20:00:23 +00:00
Revert addition of UpdateOrCreateMixin; reaches 100% test coverage.
This commit is contained in:
parent
63a9f461fa
commit
246fb81813
5 changed files with 3 additions and 145 deletions
|
|
@ -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``.
|
||||
|
|
|
|||
31
README.rst
31
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()})
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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',)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue