diff -r aeebc5cc7a4c django/db/models/fields/related.py
|
a
|
b
|
|
| 9 | 9 | from django.db.models.deletion import CASCADE |
| 10 | 10 | from django.utils.encoding import smart_unicode |
| 11 | 11 | from django.utils.translation import ugettext_lazy as _, string_concat |
| 12 | | from django.utils.functional import curry |
| | 12 | from django.utils.functional import curry, memoize, cached_property |
| 13 | 13 | from django.core import exceptions |
| 14 | 14 | from django import forms |
| 15 | 15 | |
| … |
… |
|
| 386 | 386 | if instance is None: |
| 387 | 387 | return self |
| 388 | 388 | |
| 389 | | return self.create_manager(instance, |
| 390 | | self.related.model._default_manager.__class__) |
| | 389 | manager = self.create_manager(self.related.model._default_manager.__class__) |
| | 390 | return manager(instance) |
| 391 | 391 | |
| 392 | 392 | def __set__(self, instance, value): |
| 393 | 393 | if instance is None: |
| … |
… |
|
| 406 | 406 | than the default manager, as returned by __get__). Used by |
| 407 | 407 | Model.delete(). |
| 408 | 408 | """ |
| 409 | | return self.create_manager(instance, |
| 410 | | self.related.model._base_manager.__class__) |
| | 409 | manager = self.create_manager(self.related.model._base_manager.__class__) |
| | 410 | return manager(instance) |
| 411 | 411 | |
| 412 | | def create_manager(self, instance, superclass): |
| | 412 | def create_manager(self, superclass): |
| 413 | 413 | """ |
| 414 | 414 | Creates the managers used by other methods (__get__() and delete()). |
| 415 | 415 | """ |
| | 416 | |
| | 417 | # We use closures for these values so that we only need to memoize this |
| | 418 | # function on the one argument of 'superclass', and the two places that |
| | 419 | # call create_manager simply need to pass instance to the manager |
| | 420 | # __init__ |
| 416 | 421 | rel_field = self.related.field |
| | 422 | rel_model = self.related.model |
| | 423 | attname = rel_field.rel.get_related_field().attname |
| | 424 | |
| 417 | 425 | class RelatedManager(superclass): |
| 418 | | def __init__(self, model=None, core_filters=None, instance=None, |
| 419 | | rel_field=None): |
| | 426 | def __init__(self, instance): |
| 420 | 427 | super(RelatedManager, self).__init__() |
| 421 | | self.model = model |
| 422 | | self.core_filters = core_filters |
| 423 | 428 | self.instance = instance |
| 424 | | self.rel_field = rel_field |
| | 429 | self.core_filters = { |
| | 430 | '%s__%s' % (rel_field.name, attname): getattr(instance, attname) |
| | 431 | } |
| | 432 | self.model = rel_model |
| 425 | 433 | |
| 426 | 434 | def get_query_set(self): |
| 427 | 435 | db = self._db or router.db_for_read(self.model, instance=self.instance) |
| … |
… |
|
| 431 | 439 | for obj in objs: |
| 432 | 440 | if not isinstance(obj, self.model): |
| 433 | 441 | raise TypeError("'%s' instance expected" % self.model._meta.object_name) |
| 434 | | setattr(obj, self.rel_field.name, self.instance) |
| | 442 | setattr(obj, rel_field.name, self.instance) |
| 435 | 443 | obj.save() |
| 436 | 444 | add.alters_data = True |
| 437 | 445 | |
| 438 | 446 | def create(self, **kwargs): |
| 439 | | kwargs[self.rel_field.name] = self.instance |
| | 447 | kwargs[rel_field.name] = self.instance |
| 440 | 448 | db = router.db_for_write(self.model, instance=self.instance) |
| 441 | 449 | return super(RelatedManager, self.db_manager(db)).create(**kwargs) |
| 442 | 450 | create.alters_data = True |
| … |
… |
|
| 444 | 452 | def get_or_create(self, **kwargs): |
| 445 | 453 | # Update kwargs with the related object that this |
| 446 | 454 | # ForeignRelatedObjectsDescriptor knows about. |
| 447 | | kwargs[self.rel_field.name] = self.instance |
| | 455 | kwargs[rel_field.name] = self.instance |
| 448 | 456 | db = router.db_for_write(self.model, instance=self.instance) |
| 449 | 457 | return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs) |
| 450 | 458 | get_or_create.alters_data = True |
| … |
… |
|
| 452 | 460 | # remove() and clear() are only provided if the ForeignKey can have a value of null. |
| 453 | 461 | if rel_field.null: |
| 454 | 462 | def remove(self, *objs): |
| 455 | | val = getattr(self.instance, self.rel_field.rel.get_related_field().attname) |
| | 463 | val = getattr(self.instance, attname) |
| 456 | 464 | for obj in objs: |
| 457 | 465 | # Is obj actually part of this descriptor set? |
| 458 | | if getattr(obj, self.rel_field.attname) == val: |
| 459 | | setattr(obj, self.rel_field.name, None) |
| | 466 | if getattr(obj, rel_field.attname) == val: |
| | 467 | setattr(obj, rel_field.name, None) |
| 460 | 468 | obj.save() |
| 461 | 469 | else: |
| 462 | | raise self.rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance)) |
| | 470 | raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance)) |
| 463 | 471 | remove.alters_data = True |
| 464 | 472 | |
| 465 | 473 | def clear(self): |
| 466 | | self.update(**{self.rel_field.name: None}) |
| | 474 | self.update(**{rel_field.name: None}) |
| 467 | 475 | clear.alters_data = True |
| 468 | 476 | |
| 469 | | attname = rel_field.rel.get_related_field().name |
| 470 | | return RelatedManager(model=self.related.model, |
| 471 | | core_filters = {'%s__%s' % (rel_field.name, attname): |
| 472 | | getattr(instance, attname)}, |
| 473 | | instance=instance, |
| 474 | | rel_field=rel_field, |
| 475 | | ) |
| | 477 | return RelatedManager |
| | 478 | create_manager = memoize(create_manager, {}, 2) |
| 476 | 479 | |
| 477 | 480 | |
| 478 | 481 | def create_many_related_manager(superclass, rel): |
| … |
… |
|
| 663 | 666 | def __init__(self, related): |
| 664 | 667 | self.related = related # RelatedObject instance |
| 665 | 668 | |
| | 669 | @cached_property |
| | 670 | def related_manager_cls(self): |
| | 671 | # Dynamically create a class that subclasses the related |
| | 672 | # model's default manager. |
| | 673 | return create_many_related_manager( |
| | 674 | self.related.model._default_manager.__class__, |
| | 675 | self.related.field.rel |
| | 676 | ) |
| | 677 | |
| 666 | 678 | def __get__(self, instance, instance_type=None): |
| 667 | 679 | if instance is None: |
| 668 | 680 | return self |
| 669 | 681 | |
| 670 | | # Dynamically create a class that subclasses the related |
| 671 | | # model's default manager. |
| 672 | 682 | rel_model = self.related.model |
| 673 | | superclass = rel_model._default_manager.__class__ |
| 674 | | RelatedManager = create_many_related_manager(superclass, self.related.field.rel) |
| 675 | 683 | |
| 676 | | manager = RelatedManager( |
| | 684 | manager = self.related_manager_cls( |
| 677 | 685 | model=rel_model, |
| 678 | 686 | core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()}, |
| 679 | 687 | instance=instance, |
| … |
… |
|
| 716 | 724 | # a property to ensure that the fully resolved value is returned. |
| 717 | 725 | return self.field.rel.through |
| 718 | 726 | |
| | 727 | @cached_property |
| | 728 | def related_manager_cls(self): |
| | 729 | # Dynamically create a class that subclasses the related model's |
| | 730 | # default manager. |
| | 731 | return create_many_related_manager( |
| | 732 | self.field.rel.to._default_manager.__class__, |
| | 733 | self.field.rel |
| | 734 | ) |
| | 735 | |
| 719 | 736 | def __get__(self, instance, instance_type=None): |
| 720 | 737 | if instance is None: |
| 721 | 738 | return self |
| 722 | 739 | |
| 723 | | # Dynamically create a class that subclasses the related |
| 724 | | # model's default manager. |
| 725 | | rel_model=self.field.rel.to |
| 726 | | superclass = rel_model._default_manager.__class__ |
| 727 | | RelatedManager = create_many_related_manager(superclass, self.field.rel) |
| 728 | | |
| 729 | | manager = RelatedManager( |
| 730 | | model=rel_model, |
| | 740 | manager = self.related_manager_cls( |
| | 741 | model=self.field.rel.to, |
| 731 | 742 | core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()}, |
| 732 | 743 | instance=instance, |
| 733 | 744 | symmetrical=self.field.rel.symmetrical, |
diff -r aeebc5cc7a4c django/utils/functional.py
|
a
|
b
|
|
| 28 | 28 | return result |
| 29 | 29 | return wrapper |
| 30 | 30 | |
| | 31 | class cached_property(object): |
| | 32 | """ |
| | 33 | Decorator that creates converts a method with a single |
| | 34 | self argument into a property cached on the instance. |
| | 35 | """ |
| | 36 | def __init__(self, func): |
| | 37 | self.func = func |
| | 38 | |
| | 39 | def __get__(self, instance, type): |
| | 40 | res = instance.__dict__[self.func.__name__] = self.func(instance) |
| | 41 | return res |
| | 42 | |
| 31 | 43 | class Promise(object): |
| 32 | 44 | """ |
| 33 | 45 | This is just a base class for the proxy class created in |
| … |
… |
|
| 288 | 300 | results = ([], []) |
| 289 | 301 | for item in values: |
| 290 | 302 | results[predicate(item)].append(item) |
| 291 | | return results |
| 292 | | No newline at end of file |
| | 303 | return results |
diff -r aeebc5cc7a4c tests/modeltests/many_to_one/tests.py
|
a
|
b
|
|
| 399 | 399 | self.assertEqual(repr(a3), |
| 400 | 400 | repr(Article.objects.get(reporter_id=self.r2.id, |
| 401 | 401 | pub_date=datetime(2011, 5, 7)))) |
| | 402 | |
| | 403 | def test_manager_class_caching(self): |
| | 404 | r1 = Reporter.objects.create(first_name='Mike') |
| | 405 | r2 = Reporter.objects.create(first_name='John') |
| | 406 | |
| | 407 | # Same twice |
| | 408 | self.assertTrue(r1.article_set.__class__ is r1.article_set.__class__) |
| | 409 | |
| | 410 | # Same as each other |
| | 411 | self.assertTrue(r1.article_set.__class__ is r2.article_set.__class__) |
diff -r aeebc5cc7a4c tests/regressiontests/m2m_regress/tests.py
|
a
|
b
|
|
| 73 | 73 | |
| 74 | 74 | self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"]) |
| 75 | 75 | self.assertQuerysetEqual(t1.tag_collections.all(), ["<TagCollection: c1>"]) |
| | 76 | |
| | 77 | def test_manager_class_caching(self): |
| | 78 | e1 = Entry.objects.create() |
| | 79 | e2 = Entry.objects.create() |
| | 80 | t1 = Tag.objects.create() |
| | 81 | t2 = Tag.objects.create() |
| | 82 | |
| | 83 | # Get same manager twice in a row: |
| | 84 | self.assertTrue(t1.entry_set.__class__ is t1.entry_set.__class__) |
| | 85 | self.assertTrue(e1.topics.__class__ is e1.topics.__class__) |
| | 86 | |
| | 87 | # Get same manager for different instances |
| | 88 | self.assertTrue(e1.topics.__class__ is e2.topics.__class__) |
| | 89 | self.assertTrue(t1.entry_set.__class__ is t2.entry_set.__class__) |