I'm a bit confused about how the OneToOneField works when deletion comes into play. The only quasi-authoritative info I can find is from this thread on django-developers:
I don't know if you discovered this yet, but the delete is working in one direction, but not in the direction you're expecting it to. For instance, using the models you posted in another message:
class Place(models.Model): name = models.CharField(max_length = 100) class Restaurant(models.Model): place = models.OneToOneField(Place, primary_key=True)
If you create a Place and a Restaurant that is linked to it, deleting the Restaurant will not delete the Place (this is the problem you're reporting here), but deleting the Place will delete the Restaurant.
I have the following model:
class Person(models.Model):
name = models.CharField(max_length=50)
# ... etc ...
user = models.OneToOneField(User, related_name="person", null=True, blank=True)
It's set up this way so I can easily access person
from a User
instance using user.person
.
However, when I try to delete a User
record in admin, naturally it's cascading backwards to my Person
model, just as the thread discussed, showing something along the lines of:
Are you sure you want to delete the user "JordanReiter2"? All of the following related items will be deleted:
Needless to say I do not want to delete the Person
record or any of its descendants!
I guess I understand the logic: because there is a value in the OneToOne field in the Person record, deleting the User record would create a bad reference in the user_id
column in the database.
Normally, the solution would be to switch where the OneToOne
field is located. Of course, that's not realistically possible since the User
object is pretty much set by django.contrib.auth
.
Is there any way to prevent a deletion cascade while still having a straightforward way to access person from user
? Is the only way to do it creating a User
model that extends the django.contrib
version?
I changed the model names so hopefully now it's a little clearer. Basically, there a thousands of Person records. Not every person has a login, but if they do, they have one and only one login.
Turns out that both ForeignKey
and OneToOneField
have an attribute on_delete
that you can set to models.SET_NULL
, like so:
class Person(models.Model):
name = models.CharField(max_length=50)
# ... etc ...
user = models.OneToOneField(User, on_delete=models.SET_NULL, related_name="person", null=True, blank=True)
This results in the behavior I was wanting: the User
model is deleted without touching the Person
record. I overlooked it because it's not explicitly listed under OneToOneField
, it simply says
Additionally, OneToOneField accepts all of the extra arguments accepted by ForeignKey...
Easy to miss.