Author: jacob
Date: 2008-09-02 10:26:00 -0500 (Tue, 02 Sep 2008)
New Revision: 8855
Modified:
django/trunk/django/contrib/contenttypes/generic.py
django/trunk/django/db/models/base.py
django/trunk/django/db/models/options.py
django/trunk/tests/modeltests/generic_relations/models.py
Log:
Fixed #8309: subclasses now inherit `GenericForeignKey` correctly. There's also
now an internal API so that other "virtual fields" like GFK can be inherited as
well. Thanks, msaelices.
Modified: django/trunk/django/contrib/contenttypes/generic.py
===================================================================
--- django/trunk/django/contrib/contenttypes/generic.py 2008-09-02 14:20:11 UTC
(rev 8854)
+++ django/trunk/django/contrib/contenttypes/generic.py 2008-09-02 15:26:00 UTC
(rev 8855)
@@ -24,11 +24,10 @@
self.fk_field = fk_field
def contribute_to_class(self, cls, name):
- # Make sure the fields exist (these raise FieldDoesNotExist,
- # which is a fine error to raise here)
self.name = name
self.model = cls
self.cache_attr = "_%s_cache" % name
+ cls._meta.add_virtual_field(self)
# For some reason I don't totally understand, using weakrefs here
doesn't work.
signals.pre_init.connect(self.instance_pre_init, sender=cls,
weak=False)
Modified: django/trunk/django/db/models/base.py
===================================================================
--- django/trunk/django/db/models/base.py 2008-09-02 14:20:11 UTC (rev
8854)
+++ django/trunk/django/db/models/base.py 2008-09-02 15:26:00 UTC (rev
8855)
@@ -87,6 +87,14 @@
# Things without _meta aren't functional models, so they're
# uninteresting parents.
continue
+
+ # All the fields of any type declared on this model
+ new_fields = new_class._meta.local_fields + \
+ new_class._meta.many_to_many + \
+ new_class._meta.virtual_fields
+ field_names = set([f.name for f in new_fields])
+
+ # Concrete classes...
if not base._meta.abstract:
if base in o2o_map:
field = o2o_map[base]
@@ -98,13 +106,17 @@
auto_created=True, parent_link=True)
new_class.add_to_class(attr_name, field)
new_class._meta.parents[base] = field
+
+ # .. and abstract ones.
else:
- # The abstract base class case.
- names = set([f.name for f in new_class._meta.local_fields +
new_class._meta.many_to_many])
- for field in base._meta.local_fields +
base._meta.local_many_to_many:
- if field.name in names:
- raise FieldError('Local field %r in class %r clashes
with field of similar name from abstract base class %r'
- % (field.name, name, base.__name__))
+ # Check for clashes between locally declared fields and those
on the ABC.
+ parent_fields = base._meta.local_fields +
base._meta.local_many_to_many
+ for field in parent_fields:
+ if field.name in field_names:
+ raise FieldError('Local field %r in class %r clashes '\
+ 'with field of similar name from '\
+ 'abstract base class %r' % \
+ (field.name, name, base.__name__))
new_class.add_to_class(field.name, copy.deepcopy(field))
# Inherit managers from the abstract base classes.
@@ -115,6 +127,16 @@
if not val or val is manager:
new_manager = manager._copy_to_model(new_class)
new_class.add_to_class(mgr_name, new_manager)
+
+ # Inherit virtual fields (like GenericForeignKey) from the parent
class
+ for field in base._meta.virtual_fields:
+ if base._meta.abstract and field.name in field_names:
+ raise FieldError('Local field %r in class %r clashes '\
+ 'with field of similar name from '\
+ 'abstract base class %r' % \
+ (field.name, name, base.__name__))
+ new_class.add_to_class(field.name, copy.deepcopy(field))
+
if abstract:
# Abstract base models can't be instantiated and don't appear in
# the list of models for an app. We do the final setup for them a
Modified: django/trunk/django/db/models/options.py
===================================================================
--- django/trunk/django/db/models/options.py 2008-09-02 14:20:11 UTC (rev
8854)
+++ django/trunk/django/db/models/options.py 2008-09-02 15:26:00 UTC (rev
8855)
@@ -26,6 +26,7 @@
class Options(object):
def __init__(self, meta, app_label=None):
self.local_fields, self.local_many_to_many = [], []
+ self.virtual_fields = []
self.module_name, self.verbose_name = None, None
self.verbose_name_plural = None
self.db_table = ''
@@ -155,6 +156,9 @@
if hasattr(self, '_name_map'):
del self._name_map
+ def add_virtual_field(self, field):
+ self.virtual_fields.append(field)
+
def setup_pk(self, field):
if not self.pk and field.primary_key:
self.pk = field
Modified: django/trunk/tests/modeltests/generic_relations/models.py
===================================================================
--- django/trunk/tests/modeltests/generic_relations/models.py 2008-09-02
14:20:11 UTC (rev 8854)
+++ django/trunk/tests/modeltests/generic_relations/models.py 2008-09-02
15:26:00 UTC (rev 8855)
@@ -27,6 +27,9 @@
def __unicode__(self):
return self.tag
+class ValuableTaggedItem(TaggedItem):
+ value = models.PositiveIntegerField()
+
class Comparison(models.Model):
"""
A model that tests having multiple GenericForeignKeys
@@ -204,6 +207,12 @@
>>> Comparison.objects.all()
[<Comparison: tiger is stronger than None>]
+# GenericForeignKey should work with subclasses (see #8309)
+>>> quartz = Mineral.objects.create(name="Quartz", hardness=7)
+>>> valuedtag = ValuableTaggedItem(content_object=quartz, tag="shiny",
value=10)
+>>> valuedtag.save()
+>>> valuedtag.content_object
+<Mineral: Quartz>
# GenericInlineFormSet tests ##################################################
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---