mirror of
https://github.com/django/django.git
synced 2025-10-31 09:41:08 +00:00
Fixed #7539, #13067 -- Added on_delete argument to ForeignKey to control cascade behavior. Also refactored deletion for efficiency and code clarity. Many thanks to Johannes Dollinger and Michael Glassford for extensive work on the patch, and to Alex Gaynor, Russell Keith-Magee, and Jacob Kaplan-Moss for review.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14507 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -1,42 +1,106 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Tests for some corner cases with deleting.
|
||||
"""
|
||||
from django.db import models, IntegrityError
|
||||
|
||||
from django.db import models
|
||||
|
||||
class DefaultRepr(object):
|
||||
def __repr__(self):
|
||||
return u"<%s: %s>" % (self.__class__.__name__, self.__dict__)
|
||||
class R(models.Model):
|
||||
is_default = models.BooleanField(default=False)
|
||||
|
||||
class A(DefaultRepr, models.Model):
|
||||
def __str__(self):
|
||||
return "%s" % self.pk
|
||||
|
||||
|
||||
get_default_r = lambda: R.objects.get_or_create(is_default=True)[0]
|
||||
|
||||
|
||||
class S(models.Model):
|
||||
r = models.ForeignKey(R)
|
||||
|
||||
|
||||
class T(models.Model):
|
||||
s = models.ForeignKey(S)
|
||||
|
||||
|
||||
class U(models.Model):
|
||||
t = models.ForeignKey(T)
|
||||
|
||||
|
||||
class RChild(R):
|
||||
pass
|
||||
|
||||
class B(DefaultRepr, models.Model):
|
||||
a = models.ForeignKey(A)
|
||||
|
||||
class C(DefaultRepr, models.Model):
|
||||
b = models.ForeignKey(B)
|
||||
class A(models.Model):
|
||||
name = models.CharField(max_length=30)
|
||||
|
||||
class D(DefaultRepr, models.Model):
|
||||
c = models.ForeignKey(C)
|
||||
a = models.ForeignKey(A)
|
||||
auto = models.ForeignKey(R, related_name="auto_set")
|
||||
auto_nullable = models.ForeignKey(R, null=True,
|
||||
related_name='auto_nullable_set')
|
||||
setvalue = models.ForeignKey(R, on_delete=models.SET(get_default_r),
|
||||
related_name='setvalue')
|
||||
setnull = models.ForeignKey(R, on_delete=models.SET_NULL, null=True,
|
||||
related_name='setnull_set')
|
||||
setdefault = models.ForeignKey(R, on_delete=models.SET_DEFAULT,
|
||||
default=get_default_r, related_name='setdefault_set')
|
||||
setdefault_none = models.ForeignKey(R, on_delete=models.SET_DEFAULT,
|
||||
default=None, null=True, related_name='setnull_nullable_set')
|
||||
cascade = models.ForeignKey(R, on_delete=models.CASCADE,
|
||||
related_name='cascade_set')
|
||||
cascade_nullable = models.ForeignKey(R, on_delete=models.CASCADE, null=True,
|
||||
related_name='cascade_nullable_set')
|
||||
protect = models.ForeignKey(R, on_delete=models.PROTECT, null=True)
|
||||
donothing = models.ForeignKey(R, on_delete=models.DO_NOTHING, null=True,
|
||||
related_name='donothing_set')
|
||||
child = models.ForeignKey(RChild, related_name="child")
|
||||
child_setnull = models.ForeignKey(RChild, on_delete=models.SET_NULL, null=True,
|
||||
related_name="child_setnull")
|
||||
|
||||
# Simplified, we have:
|
||||
# A
|
||||
# B -> A
|
||||
# C -> B
|
||||
# D -> C
|
||||
# D -> A
|
||||
# A OneToOneField is just a ForeignKey unique=True, so we don't duplicate
|
||||
# all the tests; just one smoke test to ensure on_delete works for it as
|
||||
# well.
|
||||
o2o_setnull = models.ForeignKey(R, null=True,
|
||||
on_delete=models.SET_NULL, related_name="o2o_nullable_set")
|
||||
|
||||
# So, we must delete Ds first of all, then Cs then Bs then As.
|
||||
# However, if we start at As, we might find Bs first (in which
|
||||
# case things will be nice), or find Ds first.
|
||||
|
||||
# Some mutually dependent models, but nullable
|
||||
class E(DefaultRepr, models.Model):
|
||||
f = models.ForeignKey('F', null=True, related_name='e_rel')
|
||||
def create_a(name):
|
||||
a = A(name=name)
|
||||
for name in ('auto', 'auto_nullable', 'setvalue', 'setnull', 'setdefault',
|
||||
'setdefault_none', 'cascade', 'cascade_nullable', 'protect',
|
||||
'donothing', 'o2o_setnull'):
|
||||
r = R.objects.create()
|
||||
setattr(a, name, r)
|
||||
a.child = RChild.objects.create()
|
||||
a.child_setnull = RChild.objects.create()
|
||||
a.save()
|
||||
return a
|
||||
|
||||
class F(DefaultRepr, models.Model):
|
||||
e = models.ForeignKey(E, related_name='f_rel')
|
||||
|
||||
class M(models.Model):
|
||||
m2m = models.ManyToManyField(R, related_name="m_set")
|
||||
m2m_through = models.ManyToManyField(R, through="MR",
|
||||
related_name="m_through_set")
|
||||
m2m_through_null = models.ManyToManyField(R, through="MRNull",
|
||||
related_name="m_through_null_set")
|
||||
|
||||
|
||||
class MR(models.Model):
|
||||
m = models.ForeignKey(M)
|
||||
r = models.ForeignKey(R)
|
||||
|
||||
|
||||
class MRNull(models.Model):
|
||||
m = models.ForeignKey(M)
|
||||
r = models.ForeignKey(R, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
|
||||
class Avatar(models.Model):
|
||||
pass
|
||||
|
||||
|
||||
class User(models.Model):
|
||||
avatar = models.ForeignKey(Avatar, null=True)
|
||||
|
||||
|
||||
class HiddenUser(models.Model):
|
||||
r = models.ForeignKey(R, related_name="+")
|
||||
|
||||
|
||||
class HiddenUserProfile(models.Model):
|
||||
user = models.ForeignKey(HiddenUser)
|
||||
|
||||
Reference in New Issue
Block a user