Ticket #14270: 14270-m2m-cleanup-r16522.diff

File 14270-m2m-cleanup-r16522.diff, 9.9 KB (added by Tai Lee, 13 years ago)

Updated to apply cleanly on r16522.

  • tests/regressiontests/m2m_regress/tests.py

     
    7373
    7474        self.assertQuerysetEqual(c1.tags.all(), ["<Tag: t1>", "<Tag: t2>"])
    7575        self.assertQuerysetEqual(t1.tag_collections.all(), ["<TagCollection: c1>"])
     76
     77    def test_manager_class(self):
     78        e1 = Entry.objects.create()
     79        e2 = Entry.objects.create()
     80        t1 = Tag.objects.create()
     81        t2 = Tag.objects.create()
     82        self.assertTrue(e1.topics.__class__ is e1.topics.__class__)
     83        self.assertTrue(e2.topics.__class__ is e2.topics.__class__)
     84
     85        self.assertTrue(t1.entry_set.__class__ is t1.entry_set.__class__)
     86        self.assertTrue(t1.entry_set.__class__ is t2.entry_set.__class__)
  • django/db/models/fields/related.py

     
    1111from django.utils.encoding import smart_unicode
    1212from django.utils.translation import (ugettext_lazy as _, string_concat,
    1313    ungettext, ugettext)
    14 from django.utils.functional import curry
     14from django.utils.functional import curry, cached_property, memoize
    1515from django.core import exceptions
    1616from django import forms
    1717
     
    387387    def __get__(self, instance, instance_type=None):
    388388        if instance is None:
    389389            return self
     390        manager = self.create_manager(
     391            self.related.model._default_manager.__class__
     392        )
     393        return manager(instance)
    390394
    391         return self.create_manager(instance,
    392                 self.related.model._default_manager.__class__)
    393 
    394395    def __set__(self, instance, value):
    395396        if instance is None:
    396397            raise AttributeError("Manager must be accessed via instance")
     
    408409        than the default manager, as returned by __get__). Used by
    409410        Model.delete().
    410411        """
    411         return self.create_manager(instance,
    412                 self.related.model._base_manager.__class__)
     412        manager = self.create_manager(self.related.model._base_manager.__class__)
     413        return manager(instance)
    413414
    414     def create_manager(self, instance, superclass):
     415    def create_manager(self, superclass):
    415416        """
    416417        Creates the managers used by other methods (__get__() and delete()).
    417418        """
     
    419420        rel_model = self.related.model
    420421
    421422        class RelatedManager(superclass):
     423            def __init__(self, instance):
     424                super(RelatedManager, self).__init__()
     425                attname = rel_field.rel.get_related_field().name
     426                self.instance = instance
     427                self.core_filters = {
     428                    '%s__%s' % (rel_field.name, attname): getattr(instance, attname)
     429                }
     430                self.model = rel_model
     431           
    422432            def get_query_set(self):
    423                 db = self._db or router.db_for_read(rel_model, instance=instance)
    424                 return superclass.get_query_set(self).using(db).filter(**(self.core_filters))
     433                db = self._db or router.db_for_read(
     434                    rel_model, instance=self.instance
     435                )
     436                return superclass.get_query_set(self).using(db).filter(**self.core_filters)
    425437
    426438            def add(self, *objs):
    427439                for obj in objs:
    428440                    if not isinstance(obj, self.model):
    429441                        raise TypeError("'%s' instance expected" % self.model._meta.object_name)
    430                     setattr(obj, rel_field.name, instance)
     442                    setattr(obj, rel_field.name, self.instance)
    431443                    obj.save()
    432444            add.alters_data = True
    433445
    434446            def create(self, **kwargs):
    435                 kwargs.update({rel_field.name: instance})
    436                 db = router.db_for_write(rel_model, instance=instance)
     447                kwargs.update({rel_field.name: self.instance})
     448                db = router.db_for_write(rel_model, instance=self.instance)
    437449                return super(RelatedManager, self.db_manager(db)).create(**kwargs)
    438450            create.alters_data = True
    439451
    440452            def get_or_create(self, **kwargs):
    441453                # Update kwargs with the related object that this
    442454                # ForeignRelatedObjectsDescriptor knows about.
    443                 kwargs.update({rel_field.name: instance})
    444                 db = router.db_for_write(rel_model, instance=instance)
     455                kwargs.update({rel_field.name: self.instance})
     456                db = router.db_for_write(rel_model, instance=self.instance)
    445457                return super(RelatedManager, self.db_manager(db)).get_or_create(**kwargs)
    446458            get_or_create.alters_data = True
    447459
    448             # remove() and clear() are only provided if the ForeignKey can have a value of null.
     460            # remove() and clear() are only provided if the ForeignKey can have
     461            # a value of null.
    449462            if rel_field.null:
    450463                def remove(self, *objs):
    451                     val = getattr(instance, rel_field.rel.get_related_field().attname)
     464                    val = getattr(self.instance, rel_field.rel.get_related_field().attname)
    452465                    for obj in objs:
    453466                        # Is obj actually part of this descriptor set?
    454467                        if getattr(obj, rel_field.attname) == val:
    455468                            setattr(obj, rel_field.name, None)
    456469                            obj.save()
    457470                        else:
    458                             raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, instance))
     471                            raise rel_field.rel.to.DoesNotExist("%r is not related to %r." % (obj, self.instance))
    459472                remove.alters_data = True
    460473
    461474                def clear(self):
    462475                    self.update(**{rel_field.name: None})
    463476                clear.alters_data = True
    464477
    465         manager = RelatedManager()
    466         attname = rel_field.rel.get_related_field().name
    467         manager.core_filters = {'%s__%s' % (rel_field.name, attname):
    468                 getattr(instance, attname)}
    469         manager.model = self.related.model
     478        return RelatedManager
     479    create_manager = memoize(create_manager, {}, 2)
    470480
    471         return manager
    472 
    473481def create_many_related_manager(superclass, rel=False):
    474482    """Creates a manager that subclasses 'superclass' (which is a Manager)
    475483    and adds behavior for many-to-many related objects."""
     
    657665    # ManyRelatedObjectsDescriptor instance.
    658666    def __init__(self, related):
    659667        self.related = related   # RelatedObject instance
     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        )
    660677
    661678    def __get__(self, instance, instance_type=None):
    662679        if instance is None:
    663680            return self
    664681
    665         # Dynamically create a class that subclasses the related
    666         # model's default manager.
    667682        rel_model = self.related.model
    668         superclass = rel_model._default_manager.__class__
    669         RelatedManager = create_many_related_manager(superclass, self.related.field.rel)
    670 
    671         manager = RelatedManager(
     683        manager = self.related_manager_cls(
    672684            model=rel_model,
    673685            core_filters={'%s__pk' % self.related.field.name: instance._get_pk_val()},
    674686            instance=instance,
     
    702714    # ReverseManyRelatedObjectsDescriptor instance.
    703715    def __init__(self, m2m_field):
    704716        self.field = m2m_field
     717   
     718    @cached_property
     719    def related_manager_cls(self):
     720        # Dynamically create a class that subclasses the related model's
     721        # default manager.
     722        return create_many_related_manager(
     723            self.field.rel.to._default_manager.__class__,
     724            self.field.rel
     725        )
    705726
    706727    def _through(self):
    707728        # through is provided so that you have easy access to the through
     
    713734    def __get__(self, instance, instance_type=None):
    714735        if instance is None:
    715736            return self
    716 
    717         # Dynamically create a class that subclasses the related
    718         # model's default manager.
    719         rel_model=self.field.rel.to
    720         superclass = rel_model._default_manager.__class__
    721         RelatedManager = create_many_related_manager(superclass, self.field.rel)
    722 
    723         manager = RelatedManager(
    724             model=rel_model,
     737       
     738        manager = self.related_manager_cls(
     739            model=self.field.rel.to,
    725740            core_filters={'%s__pk' % self.field.related_query_name(): instance._get_pk_val()},
    726741            instance=instance,
    727742            symmetrical=self.field.rel.symmetrical,
     
    10411056        if hasattr(self, cache_attr):
    10421057            return getattr(self, cache_attr)
    10431058        for f in self.rel.through._meta.fields:
    1044             if hasattr(f,'rel') and f.rel and f.rel.to == related.model:
     1059            if hasattr(f, 'rel') and f.rel and f.rel.to == related.model:
    10451060                setattr(self, cache_attr, getattr(f, attr))
    10461061                return getattr(self, cache_attr)
    10471062
  • django/utils/functional.py

     
    2828        return result
    2929    return wrapper
    3030
     31class cached_property(object):
     32    def __init__(self, func):
     33        self.func = func
     34   
     35    def __get__(self, instance, type):
     36        res = instance.__dict__[self.func.__name__] = self.func(instance)
     37        return res
     38
    3139class Promise(object):
    3240    """
    3341    This is just a base class for the proxy class created in
Back to Top