Django - delete an object without deleting its related objects - django

I have two models:
class Client(models.Model):
some_field = models.CharField()
class Ticket(models.Model):
client = models.ForeignKey(Client)
Tickets are FOREVER in my system, but I want users to be able to delete clients they don't want anymore. Currently it'll delete all the tickets created by the Client.
Is this a bad idea (architecturally speaking), and should I just mark them as not_needed or something instead?
If it's not a bad idea, what's the best way to do it, while remaining DRY. I don't want to have to override delete() for each model that does this, but will if I have to (what's the best way to do that, if that's the only way).

So this question is very old but in case someone runs across it (like I did): starting from Django 1.3, you can use the on_delete parameter for ForeignKey models, as described here.

The django.contrib.auth module has to deal with this same problem in the User model. Their solution is to have:
class User(models.Model):
# ...
is_active = models.BooleanField(default=True)
# ...
So "deleting" a User is just setting is_active to False. Everything that works with Users needs to check is_active.
For the record, I think deleting Clients in this case is a Bad Idea.
But for the sake of argument, if you delete a Client, then its related Tickets need to first become clientless. Change the model for Ticket to:
class Ticket(models.Model):
client = models.ForeignKey(Client, null=True, blank=True,
related_name='tickets')
Then, to delete a Client, do:
for ticket in clientToDelete.tickets:
ticket.client = None
ticket.save()
clientToDelete.delete()
You can put this code into Client's delete method, but it will get skipped if you do a mass (i.e. QuerySet-based) delete of Clients.

Personally, I think it is a bad idea (you'd have orphan ticket records). I would just mark those clients as 'not_needed' or 'deleted'. You also get the added benefit of the ability to 'un-delete' those clients later.

Related

Django OneToOneField on_delete CASCADE convention

I have these models in my code
class Address(models.Model):
street = models.CharField(max_length=50)
city = models.CharField(max_length=10)
class User(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
address = models.OneToOneField(Address, on_delete=models.CASCADE)
I understand how models.CASCADE works in Django. If I delete an Address record, the corresponding User record would be deleted, but this is not what I want. I want the Address record to be deleted if the User record gets deleted.
I know I can accomplish that by putting the OneToOneField in Address rather than User, but that doesn't make sense to me from a database schema perspective because a User has an Address. An Address doesn't have a User.
I tried searching about how to force the deletion to go in the reverse way, but apparently it's not possible from all the options that Django has (https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.ForeignKey.on_delete).
Does anyone know an alternative? Or the right way to construct the database schema otherwise?
Thank you!
I do not know how to specify in your obfuscation layer (orm), but at the database level you specify something like
,constraint usr2adr_fk
foreign key (adr_id)
references address(id)
on delete set null --<<< instead of delete
Just a couple FYI comments:
1:1 relationships are always questionable. There are times for security reasons or if you do significant analysis one some of the columns. But otherwise just move the column from the child table into the parent. Also, at the database level they are very difficult to actually enforce. You need bi-directional FKs and unique constraint on the column.
Age is a very poor attribute. It changes annually so you need a Update to keep it current, but not all of them change at the same time. Much better to store birth date then calculate age when it is needed. No update necessary.
According to the docs, the on_delete=models.CASCADE won't delete the related object (OneToOneField for ex.) and the signals like pre_delete and post_delete should be used to simulate the behaviour

Django DB design for deleting crucial models?

I've run into an issue that I really haven't dealt with before. I have a task to upgrade from django 1 ==> 2. (django 1 doesn't require on_delete when dealing with relationships)
I have a couple of crucial models that have relationships inside, but I definitely don't want to CASCADE those records. For example, if a user deletes their account, I don't want their expenses to be deleted. Maybe we need to keep those expense instances for tax records later, etc.
I have read that DO_NOTHING can also be dangerous.
With a model like this, what would be the best course of action when dealing with the ForeignKeys?
I appreciate all the help in advance.
class Expenses(models.Model):
user = models.ForeignKey(Account, null=True, blank=True,
on_delete=models.?)
event = models.ForeignKey(Event, null=True, blank=True,
on_delete=models.?)
payee = models.CharField(max_length=128, null=True, blank=True)
category = models.ForeignKey(Category, blank=True, null=True,
related_name='expense_category', on_delete=models.?)
I have a task to upgrade from django 1 to 2. (django 1 doesn't require on_delete when dealing with relationships)
In django-1.x, if you did not specify on_delete, it used CASCADE [Django-doc], so in fact by specifying it, you can make it more safe.
I have read that DO_NOTHING can also be dangerous.
Well most databases will raise an integrity error for this, since then it would refer to a user that no longer exists. So DO_NOTHING is not in itself dangerous, it will simply for most databases not allow deleting the database, but that by rasing an IntegrityError.
With a model like this, what would be the best course of action when dealing with the ForeignKeys?
Perhaps here PROTECT [Django-doc] is here more appropriate, since it will simply prevent deleting the object if it is still referenced.
The best solution however depends on a large number of details. Therefore it might be better to look at the possible on_delete=… strategies [Django-doc].

allow unauthenticated users to suggest changes, but wait for administrator approval before changing object

Assume I have a very simple model:
class School(models.Model):
name = models.CharField(max_length = 100, unique=True)
I want to allow unauthenticated users to use a modelform to suggest changes to School objects, but I want to flag those changes as not yet being seen by an administrator. Once an administrator approves, I will then make the suggested change to the existing School object.
What is the best way to do this? Do I need to subclass the School class, perhaps calling it UpdateToSchool and allowing users make suggestions on this subclassed model rather than the target model itself?
Here is one way you can address this, to have a SuggestedSchoolEdits (or something like that) class that would hold attributes such as:
class SuggestedSchoolEdits(object):
school = models.ForeignKey(School) #You could use generic foreign key to extend this to any type - not just school
field = models.CharField(choices=<list of fields user can edit>)
value = models.TextField()
user = models.ForeignKey(User, null=True, blank=True) #if you want approval for logged in users too
moderator_approved = models.BooleanField()
approver = models.ForeignKey(User)
#Whatever else you wish to track
Now, when an edit is made, in the view, you can create an object of this type instead of updating the existing object. Once a moderator approves, a post_save signal could trigger the update of the School object.
This way, you have complete control over which one gets approved, rejected, etc. and you can keep track of suggestions, etc..

How do you decide on creating a new model vs a field in django?

I'm creating a user profile class for my new django website, and I am trying to decide how to represent a user's physical address in my models.
Is it better practice to create a new subclass of model and reference it with a OneToOne key like
class UserProfile(models.Model):
...
address = models.OneToOneField(AddressModel)
...
class AddressModel(models.Model)
street_address = models.CharField(max_length=30)
city = models.CharField(max_length=15)
....
or is it better to create a new address field like
class UserProfile(models.Model):
...
address = AddressField(location_dict)
...
class AddressField(models.Field)
# details go here
...
I generally find it useful to have separate models if the entries might be created independently. For example, if you might end up with a collection of addresses AND a collection of users, not all of which will be linked immediately, then I'd keep them separate.
However, if all addresses in your database will always and immediately be associated with a user, I'd simply add a new field to the model.
Note: some people will tell you that it's wrong and evil to have nullable database columns, and that you should therefore have a separate model if any of your addresses will ever be None. I disagree; while there are often many great reasons to avoid nullable columns, in cases like this I don't find the inconvenience of checking for a null address any more onerous than checking whether the one-to-one model entry exists.
Like Eli said, it's a question of independence. For this particular example, I would make the address a field of UserProfile, but only if you expect to have one address per user. If each user might have multiple addresses (a home address and a vacation address, for example), then I would recommend setting up a model using ForeignKey, which models a Many-To-One relationship.
class UserProfile(models.Model):
...
class AddressModel(models.Model)
user = models.ForeignKey(UserProfile)
street_address = models.CharField(max_length=30)
city = models.CharField(max_length=15)
location = models.CharField(max_length=15) #"Home," "work," "vacation," etc.
Then many AddressModel objects can be created and associated with each UserProfile.
To answer your question, I'd say in general it's probably better to separate out the address as mentioned by other users.
I think the more you learn about database normalization the easier this question is to answer.
This article, Using MySQL, Normalisation, should help you figure out the basics of the "forms" of normalization. BTW, even though it's titled MySQL, it's really very generic for relational databases.
While you don't always need to go through all the normal-forms for all projects, learning about it really helps.

OneToOne relation with the User model (django.contrib.auth) without cascading delete

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:
User: JordanReiter
Person: JordanReiter
Submission: Title1
Submission: Title2
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?
Update
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.
For this use case you should use a simple ForeignKey. OneToOne means there is one, and can only be one and it can only be associated with this one specific other object. The deletion occurs because it doesn’t make sense to have a null onetoone field, it CAN'T be associated with anything else.