Ticket #10808: 10808b-14404.diff

File 10808b-14404.diff, 6.0 KB (added by lsaffre, 5 years ago)

10808b against rev 14404 with tests converted to unittest

  • django/db/models/base.py

     
    300300        # Now we're left with the unprocessed fields that *must* come from
    301301        # keywords, or default.
    302302
     303        # In the case of diamond inheritance, where B and C inherit from A, and
     304        # D inherits from B and C, D will have "redundant" copies of each of
     305        # A's fields. As we iterate through all the fields, the second time we
     306        # see a field we run the risk of reassigning it the default value, so
     307        # if a field has already been seen in assigned_fields, we ignore it.
     308        assigned_fields = set()
    303309        for field in fields_iter:
     310            if field.attname in assigned_fields:
     311                continue
    304312            is_related_object = False
    305313            # This slightly odd construct is so that we can access any
    306314            # data-descriptor object (DeferredAttribute) without triggering its
     
    345353                setattr(self, field.name, rel_obj)
    346354            else:
    347355                setattr(self, field.attname, val)
     356            assigned_fields.add(field.attname)
    348357
    349358        if kwargs:
    350359            for prop in kwargs.keys():
  • django/forms/models.py

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

     
    22
    33from django.core.exceptions import FieldError
    44from django.test import TestCase
     5from django.forms.models import inlineformset_factory
    56
    67from models import (Chef, CommonInfo, ItalianRestaurant, ParkingLot, Place,
    7     Post, Restaurant, Student, StudentWorker, Supplier, Worker)
     8    Post, Restaurant, Student, StudentWorker, Supplier, Worker,
     9    FoodPlace, Bar, Pizzeria, PizzeriaBar, Owner)
    810
    911
    1012class ModelInheritanceTests(TestCase):
     
    269271        self.assertNumQueries(1,
    270272            lambda: ItalianRestaurant.objects.select_related("chef")[0].chef
    271273        )
     274
     275        # Test of diamond inheritance __init__. If B and C inherit from A,
     276        # and D inherits from B and C, we should be able to use __init__
     277        # for D to properly set all the fields, regardless of the redundant
     278        # copies of A's fields that D inherits from B and C.
     279
     280        p = PizzeriaBar(name="Mike's", pizza_bar_specific_field="Doodle")
     281        self.assertEqual(p.name,"Mike's")
     282        self.assertEqual(p.pizza_bar_specific_field,"Doodle")
     283
     284        # Note that patch 10808.diff fixes only one symptom, not
     285        # the real problem. The real problem is that in case of diamond
     286        # inheritance there are duplicate field definitions (the first
     287        # 4 fields occur twice):
     288
     289        self.assertEqual(
     290          [f.name for f in p._meta.fields],
     291          'id name owner foodplace_ptr '
     292          'id name owner foodplace_ptr '
     293          'pizzeria_ptr bar_ptr pizza_bar_specific_field'.split())
     294 
     295        # Another symptom of this problem is:
     296        # When the top-level model of your diamond structure contains a ForeignKey,
     297        # then you get problems when trying to create inline formsets:
     298
     299        try:
     300            f = inlineformset_factory(Owner,PizzeriaBar)
     301            self.fail("expected Exception: <class '...PizzeriaBar'> "
     302                      "has more than 1 ForeignKey to <class '....Owner'>")
     303        except Exception:
     304            pass
     305 
     306        # Patch 10808b*.diff won't fix the real problem, but makes it possible to
     307        # work around to specify the fk_name explicitly.
     308
     309        f = inlineformset_factory(Owner,PizzeriaBar,fk_name='owner')
     310 
     311        # Before patch 10808b*.diff, the following failed because
     312        # inlineformset_factory() just couldn't imagine that a model can
     313        # have two fields with the same name.
     314
     315
     316         
     317 No newline at end of file
  • tests/modeltests/model_inheritance/models.py

     
    143143
    144144    def __unicode__(self):
    145145        return self.content
     146
     147#
     148# Diamond inheritance test
     149#
     150
     151class Owner(models.Model):
     152    name = models.CharField(max_length=255)
     153   
     154class FoodPlace(models.Model):
     155    name = models.CharField(max_length=255)
     156    owner = models.ForeignKey(Owner,blank=True,null=True)
     157
     158class Bar(FoodPlace):
     159    pass
     160
     161class Pizzeria(FoodPlace):
     162    pass
     163
     164class PizzeriaBar(Bar, Pizzeria):
     165    pizza_bar_specific_field = models.CharField(max_length=255)
Back to Top