Ticket #14891: patch.diff
File patch.diff, 6.7 KB (added by , 11 years ago) |
---|
-
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 1e7e73d..c385446 100644
a b class ReverseSingleRelatedObjectDescriptor(six.with_metaclass(RenameRelatedObjec 251 251 # a single "remote" value, on the class that defines the related field. 252 252 # In the example "choice.poll", the poll attribute is a 253 253 # ReverseSingleRelatedObjectDescriptor instance. 254 def __init__(self, field_with_rel ):254 def __init__(self, field_with_rel, manager_class=None): 255 255 self.field = field_with_rel 256 256 self.cache_name = self.field.get_cache_name() 257 257 258 if manager_class is None: 259 self.manager = None 260 else: 261 self.manager = manager_class() 262 self.manager.model = self.field.rel.to 263 264 258 265 def is_cached(self, instance): 259 266 return hasattr(instance, self.cache_name) 260 267 261 268 def get_queryset(self, **db_hints): 262 269 db = router.db_for_read(self.field.rel.to, **db_hints) 263 rel_mgr = self.field.rel.to._default_manager 270 271 if self.manager is not None: 272 return self.manager.using(db) 273 264 274 # If the related manager indicates that it should be used for 265 275 # related fields, respect that. 276 rel_mgr = self.field.rel.to._default_manager 266 277 if getattr(rel_mgr, 'use_for_related_fields', False): 267 278 return rel_mgr.using(db) 268 279 else: … … class ForeignRelatedObjectsDescriptor(object): 368 379 # multiple "remote" values and have a ForeignKey pointed at them by 369 380 # some other model. In the example "poll.choice_set", the choice_set 370 381 # attribute is a ForeignRelatedObjectsDescriptor instance. 371 def __init__(self, related ):382 def __init__(self, related, manager_class=None): 372 383 self.related = related # RelatedObject instance 384 self.manager_class = manager_class 373 385 374 386 def __get__(self, instance, instance_type=None): 375 387 if instance is None: … … class ForeignRelatedObjectsDescriptor(object): 389 401 def related_manager_cls(self): 390 402 # Dynamically create a class that subclasses the related model's default 391 403 # manager. 392 superclass = self. related.model._default_manager.__class__404 superclass = self.manager_class or self.related.model._default_manager.__class__ 393 405 rel_field = self.related.field 394 406 rel_model = self.related.model 395 407 … … class ForeignObject(RelatedField): 939 951 parent_link=kwargs.pop('parent_link', False), 940 952 on_delete=kwargs.pop('on_delete', CASCADE), 941 953 ) 954 942 955 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 943 956 957 self.manager_class = kwargs.pop('manager_class', None) 958 self.reverse_manager_class = kwargs.pop('reverse_manager_class', None) 959 944 960 super(ForeignObject, self).__init__(**kwargs) 945 961 946 962 def resolve_related_fields(self): … … class ForeignObject(RelatedField): 1104 1120 1105 1121 def contribute_to_class(self, cls, name, virtual_only=False): 1106 1122 super(ForeignObject, self).contribute_to_class(cls, name, virtual_only=virtual_only) 1107 setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self)) 1123 1124 setattr(cls, self.name, ReverseSingleRelatedObjectDescriptor(self, self.manager_class)) 1108 1125 1109 1126 def contribute_to_related_class(self, cls, related): 1110 1127 # Internal FK's - i.e., those with a related name ending with '+' - 1111 1128 # and swapped models don't get a related descriptor. 1112 1129 if not self.rel.is_hidden() and not related.model._meta.swapped: 1113 setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) 1130 1131 setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related, self.reverse_manager_class)) 1114 1132 if self.rel.limit_choices_to: 1115 1133 cls._meta.related_fkey_lookups.append(self.rel.limit_choices_to) 1116 1134 -
new file tests/custom_field_managers/models.py
diff --git a/tests/custom_field_managers/__init__.py b/tests/custom_field_managers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/custom_field_managers/models.py b/tests/custom_field_managers/models.py new file mode 100644 index 0000000..30dbb94
- + 1 """ 2 Custom manager for relationship fields. 3 4 Using the ``manager`` and ``reverse_manager`` when creating a ForeignKey, you can 5 choose custom managers used for direct and reverse relationship. 6 7 Note: You will rarely need to define a custom ``manager`` for a ForeignKey, 8 execept when you want to filter out the queryset that will be 9 used to retrieve the related object. 10 11 """ 12 13 from __future__ import unicode_literals 14 15 from django.db import models 16 from django.utils.encoding import python_2_unicode_compatible 17 18 19 class ArticlesManager(models.Manager): 20 def get_articles_starting_with_a(self): 21 return self.filter(name__startswith='a') 22 23 class DeletedManager(models.Manager): 24 def get_queryset(self): 25 return super(DeletedManager, self).get_queryset().filter(deleted=False) 26 27 28 @python_2_unicode_compatible 29 class Author(models.Model): 30 name = models.CharField(max_length=30) 31 deleted = models.BooleanField(default=False) 32 33 34 @python_2_unicode_compatible 35 class Article(models.Model): 36 name = models.CharField(max_length=30) 37 author = models.ForeignKey(Author, manager_class=DeletedManager, reverse_manager_class=ArticlesManager, related_name='articles') 38 39 def __str__(self): 40 return self.name -
new file tests/custom_field_managers/tests.py
diff --git a/tests/custom_field_managers/tests.py b/tests/custom_field_managers/tests.py new file mode 100644 index 0000000..b2cc65f
- + 1 from __future__ import absolute_import 2 3 from django.test import TestCase 4 from django.utils import six 5 6 from .models import Author, Article, ArticlesManager 7 8 9 class CustomFieldsManagerTests(TestCase): 10 def test_manager(self): 11 author = Author.objects.create(name='The author') 12 article = Article.objects.create(name='The article', author=author) 13 14 # Let's see if we have our custom ArticlesManager, for the reverse relationship. 15 self.assertIsInstance(author.articles, ArticlesManager) 16 self.assertQuerysetEqual(author.articles.get_articles_starting_with_a(), []) 17 18 self.assertEqual(article.author, author) 19 20 author.deleted = True 21 author.save() 22 23 article = Article.objects.all()[0] # If we reuse the same article, the author is cached. 24 25 # Because our forward manager will mask a deleted author, this will raise an exception. 26 self.assertRaises(Author.DoesNotExist, lambda: article.author)