فهرست منبع

Fixed #26362 -- Fixed update of the inherited id field of an object when its parent changes.

Paulo Alvarado 7 سال پیش
والد
کامیت
c432927160
2فایلهای تغییر یافته به همراه53 افزوده شده و 0 حذف شده
  1. 16 0
      django/db/models/fields/related_descriptors.py
  2. 37 0
      tests/model_inheritance_regress/tests.py

+ 16 - 0
django/db/models/fields/related_descriptors.py

@@ -277,6 +277,22 @@ class ForwardOneToOneDescriptor(ForwardManyToOneDescriptor):
                 return obj
         return super().get_object(instance)
 
+    def __set__(self, instance, value):
+        super().__set__(instance, value)
+        # If the primary key is a link to a parent model and a parent instance
+        # is being set, update the value of the inherited pk(s).
+        if self.field.primary_key and self.field.remote_field.parent_link:
+            opts = instance._meta
+            # Inherited primary key fields from this object's base classes.
+            inherited_pk_fields = [
+                field for field in opts.concrete_fields
+                if field.primary_key and field.remote_field
+            ]
+            for field in inherited_pk_fields:
+                rel_model_pk_name = field.remote_field.model._meta.pk.attname
+                raw_value = getattr(value, rel_model_pk_name) if value is not None else None
+                setattr(instance, rel_model_pk_name, raw_value)
+
 
 class ReverseOneToOneDescriptor:
     """

+ 37 - 0
tests/model_inheritance_regress/tests.py

@@ -527,3 +527,40 @@ class ModelInheritanceTest(TestCase):
             restaurant = italian_restaurant.restaurant_ptr
             self.assertEqual(restaurant.place_ptr.restaurant, restaurant)
             self.assertEqual(restaurant.italianrestaurant, italian_restaurant)
+
+    def test_id_field_update_on_ancestor_change(self):
+        place1 = Place.objects.create(name='House of Pasta', address='944 Fullerton')
+        place2 = Place.objects.create(name='House of Pizza', address='954 Fullerton')
+        place3 = Place.objects.create(name='Burger house', address='964 Fullerton')
+        restaurant1 = Restaurant.objects.create(
+            place_ptr=place1,
+            serves_hot_dogs=True,
+            serves_pizza=False,
+        )
+        restaurant2 = Restaurant.objects.create(
+            place_ptr=place2,
+            serves_hot_dogs=True,
+            serves_pizza=False,
+        )
+
+        italian_restaurant = ItalianRestaurant.objects.create(
+            restaurant_ptr=restaurant1,
+            serves_gnocchi=True,
+        )
+        # Changing the parent of a restaurant changes the restaurant's ID & PK.
+        restaurant1.place_ptr = place3
+        self.assertEqual(restaurant1.pk, place3.pk)
+        self.assertEqual(restaurant1.id, place3.id)
+        self.assertEqual(restaurant1.pk, restaurant1.id)
+        restaurant1.place_ptr = None
+        self.assertIsNone(restaurant1.pk)
+        self.assertIsNone(restaurant1.id)
+        # Changing the parent of an italian restaurant changes the restaurant's
+        # ID & PK.
+        italian_restaurant.restaurant_ptr = restaurant2
+        self.assertEqual(italian_restaurant.pk, restaurant2.pk)
+        self.assertEqual(italian_restaurant.id, restaurant2.id)
+        self.assertEqual(italian_restaurant.pk, italian_restaurant.id)
+        italian_restaurant.restaurant_ptr = None
+        self.assertIsNone(italian_restaurant.pk)
+        self.assertIsNone(italian_restaurant.id)