mirror of https://github.com/django/django.git
Fixed #27629 -- Added router.allow_relation() calls for assignments between unsaved model instances.
This commit is contained in:
parent
9c4ea63e87
commit
a5a2ceeb45
1
AUTHORS
1
AUTHORS
|
@ -763,6 +763,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Stanislaus Madueke
|
||||
Stanislav Karpov <work@stkrp.ru>
|
||||
starrynight <cmorgh@gmail.com>
|
||||
Stefan R. Filipek
|
||||
Stefane Fermgier <sf@fermigier.com>
|
||||
Stefano Rivera <stefano@rivera.za.net>
|
||||
Stéphane Raimbault <stephane.raimbault@gmail.com>
|
||||
|
|
|
@ -205,11 +205,10 @@ class ForwardManyToOneDescriptor:
|
|||
elif value is not None:
|
||||
if instance._state.db is None:
|
||||
instance._state.db = router.db_for_write(instance.__class__, instance=value)
|
||||
elif value._state.db is None:
|
||||
if value._state.db is None:
|
||||
value._state.db = router.db_for_write(value.__class__, instance=instance)
|
||||
elif value._state.db is not None and instance._state.db is not None:
|
||||
if not router.allow_relation(value, instance):
|
||||
raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value)
|
||||
if not router.allow_relation(value, instance):
|
||||
raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value)
|
||||
|
||||
remote_field = self.field.remote_field
|
||||
# If we're setting the value of a OneToOneField to None, we need to clear
|
||||
|
@ -451,11 +450,10 @@ class ReverseOneToOneDescriptor:
|
|||
else:
|
||||
if instance._state.db is None:
|
||||
instance._state.db = router.db_for_write(instance.__class__, instance=value)
|
||||
elif value._state.db is None:
|
||||
if value._state.db is None:
|
||||
value._state.db = router.db_for_write(value.__class__, instance=instance)
|
||||
elif value._state.db is not None and instance._state.db is not None:
|
||||
if not router.allow_relation(value, instance):
|
||||
raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value)
|
||||
if not router.allow_relation(value, instance):
|
||||
raise ValueError('Cannot assign "%r": the current database router prevents this relation.' % value)
|
||||
|
||||
related_pk = tuple(getattr(instance, field.attname) for field in self.related.field.foreign_related_fields)
|
||||
# Set the value of the related field to the value of the related object's related field
|
||||
|
|
|
@ -415,6 +415,9 @@ Miscellaneous
|
|||
* ``QuerySet.raw()`` now caches its results like regular querysets. Use
|
||||
``iterator()`` if you don't want caching.
|
||||
|
||||
* The database router :meth:`allow_relation` method is called in more cases.
|
||||
Improperly written routers may need to be updated accordingly.
|
||||
|
||||
.. _deprecated-features-2.1:
|
||||
|
||||
Features deprecated in 2.1
|
||||
|
|
|
@ -319,7 +319,10 @@ class OtherRouter:
|
|||
return self.db_for_read(model, **hints)
|
||||
|
||||
def allow_relation(self, obj1, obj2, **hints):
|
||||
return None
|
||||
# ContentType objects are created during a post-migrate signal while
|
||||
# performing fixture teardown using the default database alias and
|
||||
# don't abide by the database specified by this router.
|
||||
return True
|
||||
|
||||
def allow_migrate(self, db, app_label, **hints):
|
||||
return True
|
||||
|
|
|
@ -2082,3 +2082,28 @@ class RouteForWriteTestCase(TestCase):
|
|||
self.assertEqual(e.mode, RouterUsed.WRITE)
|
||||
self.assertEqual(e.model, Book)
|
||||
self.assertEqual(e.hints, {'instance': auth})
|
||||
|
||||
|
||||
class NoRelationRouter:
|
||||
"""Disallow all relations."""
|
||||
def allow_relation(self, obj1, obj2, **hints):
|
||||
return False
|
||||
|
||||
|
||||
@override_settings(DATABASE_ROUTERS=[NoRelationRouter()])
|
||||
class RelationAssignmentTests(TestCase):
|
||||
"""allow_relation() is called with unsaved model instances."""
|
||||
multi_db = True
|
||||
router_prevents_msg = 'the current database router prevents this relation'
|
||||
|
||||
def test_foreign_key_relation(self):
|
||||
person = Person(name='Someone')
|
||||
pet = Pet()
|
||||
with self.assertRaisesMessage(ValueError, self.router_prevents_msg):
|
||||
pet.owner = person
|
||||
|
||||
def test_reverse_one_to_one_relation(self):
|
||||
user = User(username='Someone', password='fake_hash')
|
||||
profile = UserProfile()
|
||||
with self.assertRaisesMessage(ValueError, self.router_prevents_msg):
|
||||
user.userprofile = profile
|
||||
|
|
Loading…
Reference in New Issue