Ticket #10808: 10808b.diff

File 10808b.diff, 5.1 KB (added by lsaffre, 6 years ago)

contains 10808.diff + my suggestion

  • django/db/models/base.py

     
    273273        # Now we're left with the unprocessed fields that *must* come from
    274274        # keywords, or default.
    275275
     276        # In the case of diamond inheritance, where B and C inherit from A, and
     277        # D inherits from B and C, D will have "redundant" copies of each of
     278        # A's fields. As we iterate through all the fields, the second time we
     279        # see a field we run the risk of reassigning it the default value, so
     280        # if a field has already been seen in assigned_fields, we ignore it.
     281        assigned_fields = set()
    276282        for field in fields_iter:
     283            if field.attname in assigned_fields:
     284                continue
    277285            is_related_object = False
    278286            # This slightly odd construct is so that we can access any
    279287            # data-descriptor object (DeferredAttribute) without triggering its
     
    311319                setattr(self, field.name, rel_obj)
    312320            else:
    313321                setattr(self, field.attname, val)
     322            assigned_fields.add(field.attname)
    314323
    315324        if kwargs:
    316325            for prop in kwargs.keys():
  • django/forms/models.py

     
    793793    from django.db.models import ForeignKey
    794794    opts = model._meta
    795795    if fk_name:
    796         fks_to_parent = [f for f in opts.fields if f.name == fk_name]
    797         if len(fks_to_parent) == 1:
    798             fk = fks_to_parent[0]
    799             if not isinstance(fk, ForeignKey) or \
    800                     (fk.rel.to != parent_model and
    801                      fk.rel.to not in parent_model._meta.get_parent_list()):
    802                 raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
    803         elif len(fks_to_parent) == 0:
    804             raise Exception("%s has no field named '%s'" % (model, fk_name))
     796        fk = opts.get_field(fk_name,many_to_many=False)
     797        if not isinstance(fk, ForeignKey) or \
     798                (fk.rel.to != parent_model and
     799                 fk.rel.to not in parent_model._meta.get_parent_list()):
     800            raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
    805801    else:
    806802        # Try to discover what the ForeignKey from model to parent_model is
    807803        fks_to_parent = [
  • tests/modeltests/model_inheritance/models.py

     
    116116    def __unicode__(self):
    117117        return u"%s the parking lot" % self.name
    118118
     119#
     120# Diamond inheritance test
     121#
     122
     123class Owner(models.Model):
     124    name = models.CharField(max_length=255)
     125   
     126class FoodPlace(models.Model):
     127    name = models.CharField(max_length=255)
     128    owner = models.ForeignKey(Owner,blank=True,null=True)
     129
     130class Bar(FoodPlace):
     131    pass
     132
     133class Pizzeria(FoodPlace):
     134    pass
     135
     136class PizzeriaBar(Bar, Pizzeria):
     137    pizza_bar_specific_field = models.CharField(max_length=255)
     138
    119139__test__ = {'API_TESTS':"""
    120140# The Student and Worker models both have 'name' and 'age' fields on them and
    121141# inherit the __unicode__() method, just as with normal Python subclassing.
     
    3103303
    311331>>> settings.DEBUG = False
    312332
     333# Test of diamond inheritance __init__. If B and C inherit from A, and D inherits from B and C, we should be able to use __init__ for D to properly set all the fields, regardless of the redundant copies of A's fields that D inherits from B and C.
     334
     335>>> p = PizzeriaBar(name="Mike's", pizza_bar_specific_field="Doodle")
     336>>> p.name == "Mike's"
     337True
     338>>> p.pizza_bar_specific_field == "Doodle"
     339True
     340
     341#Note that patch 10808.diff fixes only one symptom, not the real problem.
     342#The real problem is that in case of diamond inheritance there are duplicate field definitions:
     343
     344  >>> print ' '.join([f.name for f in p._meta.fields])
     345  id name owner foodplace_ptr id name owner foodplace_ptr pizzeria_ptr bar_ptr pizza_bar_specific_field
     346 
     347#The first 4 fields occur twice.
     348#My patch won't fix the real problem, but another symptom.
     349#When the top-level model of your diamond structure contains a ForeignKey, then you get problems when trying to create inline formsets:
     350
     351  >>> from django.forms.models import inlineformset_factory
     352  >>> f = inlineformset_factory(Owner,PizzeriaBar)
     353  Traceback (most recent call last):
     354    ...
     355  Exception: <class '...PizzeriaBar'> has more than 1 ForeignKey to <class '....Owner'>
     356 
     357#The workaround I suggest for this problem is to specify the fk_name explicitly:
     358
     359  >>> from django.forms.models import inlineformset_factory
     360  >>> f = inlineformset_factory(Owner,PizzeriaBar,fk_name='owner')
     361 
     362#Unfortunately this workaround needs another patch 10808b.diff because inlineformset_factory() just can't imagine that a model can have two fields with the same name.
     363
     364
     365
    313366"""}
Back to Top