Ticket #5570: generic_relations_r6403.diff

File generic_relations_r6403.diff, 5.0 KB (added by Manuel Saelices, 17 years ago)

Patch that improves generic relations performance

  • django/contrib/contenttypes/generic.py

     
    1111from django.dispatch import dispatcher
    1212from django.utils.functional import curry
    1313
     14CONTENT_TYPE_CACHE = {}
     15
    1416class GenericForeignKey(object):
    1517    """
    1618    Provides a generic relation to any object through content-type/object-id
    1719    fields.
    1820    """
    19    
     21
    2022    def __init__(self, ct_field="content_type", fk_field="object_id"):
    2123        self.ct_field = ct_field
    2224        self.fk_field = fk_field
    23        
     25
    2426    def contribute_to_class(self, cls, name):
    2527        # Make sure the fields exist (these raise FieldDoesNotExist,
    2628        # which is a fine error to raise here)
    2729        self.name = name
    2830        self.model = cls
    2931        self.cache_attr = "_%s_cache" % name
    30        
     32
    3133        # For some reason I don't totally understand, using weakrefs here doesn't work.
    3234        dispatcher.connect(self.instance_pre_init, signal=signals.pre_init, sender=cls, weak=False)
    3335
     
    3638
    3739    def instance_pre_init(self, signal, sender, args, kwargs):
    3840        # Handle initalizing an object with the generic FK instaed of
    39         # content-type/object-id fields.       
     41        # content-type/object-id fields.
    4042        if self.name in kwargs:
    4143            value = kwargs.pop(self.name)
    4244            kwargs[self.ct_field] = self.get_content_type(value)
    4345            kwargs[self.fk_field] = value._get_pk_val()
    44            
     46
    4547    def get_content_type(self, obj):
    4648        # Convenience function using get_model avoids a circular import when using this model
    4749        ContentType = get_model("contenttypes", "contenttype")
    4850        return ContentType.objects.get_for_model(obj)
    49        
     51
    5052    def __get__(self, instance, instance_type=None):
    5153        if instance is None:
    5254            raise AttributeError, u"%s must be accessed via instance" % self.name
     
    5557            return getattr(instance, self.cache_attr)
    5658        except AttributeError:
    5759            rel_obj = None
    58             ct = getattr(instance, self.ct_field)
     60            ct_id_fieldname = instance._meta.get_field(self.ct_field).get_attname()
     61            ct_id = getattr(instance, ct_id_fieldname)
     62            try:
     63                ct = CONTENT_TYPE_CACHE[ct_id]
     64            except KeyError:
     65                ct = getattr(instance, self.ct_field)
     66                CONTENT_TYPE_CACHE[ct_id] = ct
    5967            if ct:
    6068                try:
    6169                    rel_obj = ct.get_object_for_this_type(pk=getattr(instance, self.fk_field))
     
    7785        setattr(instance, self.ct_field, ct)
    7886        setattr(instance, self.fk_field, fk)
    7987        setattr(instance, self.cache_attr, value)
    80    
     88
    8189class GenericRelation(RelatedField, Field):
    8290    """Provides an accessor to generic related objects (i.e. comments)"""
    8391
     
    9098                           
    9199        # Override content-type/object-id field names on the related class
    92100        self.object_id_field_name = kwargs.pop("object_id_field", "object_id")
    93         self.content_type_field_name = kwargs.pop("content_type_field", "content_type")               
    94        
     101        self.content_type_field_name = kwargs.pop("content_type_field", "content_type")
     102
    95103        kwargs['blank'] = True
    96104        kwargs['editable'] = False
    97105        kwargs['serialize'] = False
     
    116124
    117125    def m2m_column_name(self):
    118126        return self.object_id_field_name
    119        
     127
    120128    def m2m_reverse_name(self):
    121129        return self.object_id_field_name
    122130
     
    131139
    132140    def contribute_to_related_class(self, cls, related):
    133141        pass
    134        
     142
    135143    def set_attributes_from_rel(self):
    136144        pass
    137145
    138146    def get_internal_type(self):
    139147        return "ManyToManyField"
    140        
     148
    141149class ReverseGenericRelatedObjectsDescriptor(object):
    142150    """
    143151    This class provides the functionality that makes the related-object
     
    193201    Factory function for a manager that subclasses 'superclass' (which is a
    194202    Manager) and adds behavior for generic related objects.
    195203    """
    196    
     204
    197205    class GenericRelatedObjectManager(superclass):
    198206        def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
    199207                     join_table=None, source_col_name=None, target_col_name=None, content_type=None,
    200208                     content_type_field_name=None, object_id_field_name=None):
    201            
     209
    202210            super(GenericRelatedObjectManager, self).__init__()
    203211            self.core_filters = core_filters or {}
    204212            self.model = model
     
    212220            self.content_type_field_name = content_type_field_name
    213221            self.object_id_field_name = object_id_field_name
    214222            self.pk_val = self.instance._get_pk_val()
    215                        
     223
    216224        def get_query_set(self):
    217225            query = {
    218226                '%s__pk' % self.content_type_field_name : self.content_type.id,
Back to Top