Django set model fields after create - python

I have a model Movie and a Model DefaultMoviePriceFor which saves the default prices for a specific genre:
class Movie(models.Model):
name = models.TextField('name', null=True)
genre = models.TextField('genre', null=True)
price = models.IntegerField('price', default=0)
class DefaultMoviePriceForGenre(models.Model):
genre = models.TextField('genre', null=True)
price = models.IntegerField('price', default=0)
Now I would like to fill up Movie.price with the default price of the Movie.genre everytime an object of Movie is instantiated.
Is there any good way to do so? I have SQL triggers in my head, they could do that, don't they? How is it done in django?

One way to do this is with the pre-save signal. This will be fired immediately before any instance of the model is saved, and will received a created boolean arg that will let you set the price only if the object is new.
There's also a post-save signal if for some reason you want to do it after saving, EG because your behavior depends on the new instance's PK. But pre-save should work here.
Alternatively, you can override the model class's save method. https://docs.djangoproject.com/en/2.1/topics/db/models/#overriding-model-methods
See this answer for some discussion of the pros and cons of the two approaches: Django signals vs. overriding save method
This is all assuming the desired behavior is exactly as described - look up the current default price of the genre when the instance is created and preserve it independently of future changes to the genre. If you want to do something more flexible - EG say "horror movies cost X now unless overridden, but if I change the default genre price later they should all update" you might be best served with a method on the Movie class that computes the price based on current state rather than setting it at creation and breaking that connection. But it depends what you want.

Related

During a django model's object creation, how to pop out and save a value from a JSON array stored in JSONField of another model's object?

I am building a website in django where users can purchase lets say activation keys for a software. Now there are a limited number of these keys, all of whome are right now stored in a json array in a JSONField of a django model named Software.
Whenever a user purchases a key, an object of PurchasedKey model is created. During this, an activation key from the list of available keys must be saved in the attribute named activation_key of this object. After saving the key it must also be deleted from the list of available keys. This is the part I am not sure how to do.
I could just manipulate the JSON to retrieve one key from the list, remove it from the list and the update and save the Software object. But is there a better way to do this with probably also a better way to store the available keys instead of using JSONField.
# models.py
from django.db import models
from django.contrib.auth.models import User
class Software(models.Model):
name=models.CharField(max_length=20)
available_keys=models.JSONField()
class PurchasedKey(models.Model):
purchased_by=models.ForeignKey(User, on_delete=models.CASCADE)
software=models.ForeignKey(Software, on_delete=models.CASCADE)
activation_key= What to do here
Create a new model to hold the software keys
SoftwareKey(models.Model):
software_key = models.CharFiled(length=256)
// or put the json field here
active = models.BooleanField(default=True)
Purchase(models.Model):
purchased_by=models.ForeignKey(User, on_delete=models.CASCADE)
software=models.ForeignKey(Software, on_delete=models.CASCADE)
activation_key= models.ForeignKey(SoftwareKeys, on_delete=models.SET_NULL)
active = models.BooleanField(default=True)
Then in the Purchase model add some unique partial indexes(https://pypi.org/project/django-partial-index/) to restrict adding the same key again.
Also you can make the activation_key 1-1, So activation key can be added only once.
activation_key= models.OneToOneField(SoftwareKeys, on_delete=models.SET_NULL)
Additionally you can add a new field in SoftwareKey
purchased = models.BooleanField(default=False)
and make it true if it purchased.
Deleting the purchased keys from the model is not a good practice, keep it there for future reference, use some flags to know whether its purchased or not.
Just override the Create Method of Purchase-Model when you are creating, in which you can modified the other model instance using save method or if you are using DRF then it would lot easier because in it you can use perform_create.

Django model design pattern - get youngest related object

Django lets you follow relations link, but none of the filter methods let you get youngest/oldest, or max/min, afaik.
Laravel has "has one of many", and I wish that Django had something similar without requiring window functions, that have their own limitations.
Annotations despite being promising, are also a dead end.
Therefore I wonder what the best design is for the following situation:
I have "model A", whose instances will pass through several statuses during their lifecycle ("created", "processing", "complete"). I want to know what status an instance of model A currently has, but also have a record of when each status was in effect. I don't want to parse logs.
I thought a good approach was to create a status model (model B) whose foreign key is a model A instance. it becomes easy to see when each status was started and stopped.
However if I want to get the current status (an instance of model B) for all my model A instances, I need to do n+1 database queries. This seems sub-optimal.
What are some alternatives?
However if I want to get the current status (an instance of model B) for all my model A instances, I need to do n+1 database queries. This seems sub-optimal.
No, you can make use of Subquery expressions [Django-doc]. Indeed, if you have two models:
class Item(models.Model):
name = models.CharField(max_length=128)
class ItemStatus(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
status = models.CharField(max_length=128)
started = models.DateTimeField(auto_now_add=True)
You can annotate each Item with the last status with:
from django.db.models import OuterRef, Subquery
Item.objects.annotate(
last_status=Subquery(
ItemStatus.objects.filter(
item_id=OuterRef('pk')
).order_by('-started').values('status')[:1]
)
)
For each Item, there will be an extra attribute named .last_status that will contain the .status of the related ItemStatus that started last. If there is no such StatusItem, last_status will be None (NULL).
This will be determined by subqueries at the database side, hence it is done in the same query where you retrieve the Items, and thus does not suffer from the N+1 problem.

UpdateView that creates or increments existing instance

I have a model that keeps track how much items have been "bought" at a certain value.
For example, one could think of items as stocks, value as the price they have been bought at.
class Inventory(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField()
value = models.PositiveIntegerField()
class Meta:
db_table = 'app_version'
constraints = [
models.UniqueConstraint(fields=['item', 'value'], name='unique valued items')
]
Now, I'm trying to write a class-based Create/UpdateView in which one simply declares which item has been bought at what price. Then, the view should either create a new instance or increment an existing instance's quantity.
I already figured out that an UpdateView might be the best start as I can overwrite the get_object method to have this "create or update" behaviour.
However, I'm not sure how to do the incrementing.
Perhaps I'm also completely on the wrong track and there is a much simpler solution I am missing here.
It would be great to get some ideas and input on how to approach this problem.
Thank you so much!
Just use get_or_update method. It allows you to know it's new instance or from db. But if you want to create an api, it's the better way to use django rest framework. It ships with a lot of build-in tools for quick api creation.

What's the best approach for this database models structure?

I'm developing a Property Management System with Django, right now I'm working on an app named by "Property Check", basically the purpose of it is to provide a form with a list of tasks like "Diswasher: clean & empty?", those tasks need to be checked at a property by a staff member.
The main idea is to allow admin to create Tasks and their Categories on the admin side.
Example: Task - Dishwater: clean & empty belongs to Category - Kitchen.
Each Property Check belongs to a property, it has the list of tasks and those tasks have different status, like "Checked" or "Needs attention".
So far this is what I've created:
models.py
class Task(models.Model):
name = models.CharField(db_column='SafetyTaskName', max_length=100, blank=False, null=False)
category = models.ForeignKey(Categories, db_column='category')
task_check = models.ForeignKey(TaskCheck)
class Categories(models.Model):
name = models.CharField(db_column='Categories', max_length=40, null=False, blank=False)
class TaskCheck(models.Model):
status = models.CharField(db_column='Status', choices=STATUS_CHOICES, default='nd')
image = models.ImageField(upload_to='property_check',null=True)
notes = models.CharField(db_column='Notes', max_length=500, blank=True, null=True) # Field name made lowercase.
class Propertycheck(models.Model):
property = models.ForeignKey(Property, models.DO_NOTHING, db_column='ID_Property') # Field name made lowercase.
task = models.CharField(TaskCheck)
name = models.CharField(db_column='Name', max_length=150)
date = models.DateField(db_column='Date', default=timezone.now) # Field name made lowercase.
next_visit = models.DateField(db_column='Next Visit')
staff = models.ForeignKey(User, db_column='Staff', max_length=25)
notes = models.CharField(db_column='Notes', max_length=500, blank=True, null=True) # Field name made lowercase.
Functional example of what I pretend:
A staff member goes to a property that needs to be checked, he fills
the form that contains all the tasks. In case of needing more tasks,
the admin goes to the admin panel and adds a new one. The same status
applies to every task.
Requirements:
A property has many property checks;
A property check has a list of tasks;
Admin must be capable to add tasks and categories;
Tasks belong to one category;
Property checks are made by a staff member;
The task list is the same to every property;
Every task must have a status (Ex.: completed state);
Problem:
I'm a bit confused about where to use the foreignkeys. I need property check to show the list of tasks, and for each one, their status.
Due to my experience I'm stuck at this right now, so I need some help with this.
Could you please take a look at what've done and let me know a better solution?
* **Update ***
Thanks to Bruno Desthuilliers answer, I could restructure my models by following his advices. I think this solution is closer to what I need, but my question is, are my changes 100% correct according to the requirements on Bruno's answer?
class Task(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(Categories)
property = models.ManyToManyField(Property)
class Categories(models.Model):
name = models.CharField(max_length=40)
class TaskCheck(models.Model):
status = models.CharField(choices=STATUS_CHOICES, default='nd')
image = models.ImageField(upload_to='task_check', null=True)
notes = models.TextField(max_length=500)
task = models.ForeignKey(Task)
property_check = models.ForeignKey(Propertycheck)
class Propertycheck(models.Model):
property = models.ForeignKey(Property, models.DO_NOTHING)
name = models.CharField(max_length=150)
date = models.DateField(default=timezone.now)
next_visit = models.DateField()
staff = models.ForeignKey(User, max_length=25)
notes = models.TextField(max_length=500, default='')
My english ain't the best and I wasn't sure about the best title for my question.
A property has many property checks;
This describes only half of the relationship's cardinality - you also need to specify how many properties a property check can belong to. In this case the answer seems rather obvious (I can't see a case where a same property check would belong to more than one property), BUT unless you have a real and deep working knowledge of the domain, you should STILL ask your customer - sometimes "obvious" things are actually wrong ;-)
But if we consider that "a property has many property checks" AND "a property check belongs to one single property", we have a one to many relationship. At the db schema level, this is materialized by a foreign key on the "one" side in the "many" side, ie PropertyCheck must have a fk on Property.
This is logical when you remember that in the relational model, fields are atomic values (one single value in each field). You couldn't store a list of related PropertyCheck ids in Property, but you can store a single Property id in each PropertyCheck.
This is also logical when you think of the constraints - a Property can actually have "zero to many" related property checks (you can have a property that has never been "checked" so far), but a PropertyCheck MUST have a related property (it wouldn't make sense to have a property check without property, would it ?). If property checks ids where stored as a list in Property, you could still create property checks without property (and you would have consistency issues too if a property check was deleted and the property's list of property checks not updated).
So, to make a long story short: for a one to many relationship, the fk resides on the "many" side and points to the "one" side.
A property check has a list of tasks;
Are you sure this one is right ? It seems to me that you're confusing the user's view of the application with the database schema.
Sure, what the user views when he's on the "property check" page is a list of tasks to perform (and a checkbox etc for each task) - but this doesn't mean the tasks belong to the property check. If that was the case, the admin would have to create a new list of tasks for each property check... As fad as I understand the domain, the point is that there's a list of tasks for each property, and that the system builds a list of (not yet checked) task checks for each property check. Which FWIW is already what you started to design.
So (assuming I got the problem right), your rule is actually "each property has a list of tasks". Now we have the other cardinality to sort out: does a task belong to one single property, or can the same task be shared by many properties ?
We already covered the first case (cf above). In the second case - which is actually more likely since there are certainly quite a few tasks that will be the same for most properties -, you have a many to many relationship. Those are materialized by a relationship table which has a fk on each side of the relationship, with a unicity constraint on the pair of fks (you don't want to have the same task listed twice for a same property). Note that with Django's ORM, you don't need to explicitely declare a model for this (unless of course you need to add some other field to the relationship, but so far I don't see a need for this here) - just declare a many2many field on any side of the relationship (doesn't really matter) and the ORM will create the intermediary table for you.
Then you have a relationship between property check and task check. Here it's a simple one to many relationship - a property check has many task checks, a task check belongs to one single property check. The only constraint here is that those task checks's task must belong to the same property as the property check's property (yes, it's a bit confused when written that way xD). To say it more simply: the task list of a property is used as a blueprint to create the tasks check list for a property check.
IOW you have:
a task belongs to one or many property
a property has many tasks
a property has many property checks
a property check belongs to one single property
a task check references one single task
a task has many task checks
a task check's task must be one of the tasks of the task check's property check's property (duh!)
Admin must be capable to add tasks and categories;
This is a requirement indeed, but it's not related to what interest us here since this is handled at code level (permissions), not at the db schema level.
Tasks belong to one category;
and a category can have many tasks - one to many relationship, cf above.
Property checks are made by a staff member;
and a staff member can do many property checks - one to many relationship, cf above.
The task list is the same to every property;
Ah, this one is interesting. If this is true, it means that you actually don't need any relationship between Task and Property.
But that's still something I'd double-check with the customer - from experience, customers tend to only think of the general case when they explain the domain, then when they start testing the software a whole lot of corner cases appear out of the blue, and you suddenly realize you will have to rewrite half or more of your schema and code. I actually had the case on one of the very first application I was involved in - not as a developper actually, I was just one of the app's users, and the first thing I had to do with the app revealed such shortcomings, leading to a full month of additional development (which the company that employed me had to pay for since they had signed on the - wrong - requirements). Needless to say the persons responsible for this costly mistake were either blamed or, for one, just plain fired.
Every task must have a status (Ex.: completed state);
This one is wrong too. The status belongs to the task check, not to the task.
Ok, so the models you posted are not too far off. As I already mentionned in a comment, you have some one to many relationships wrong (fk on the wrong side of the relationship) but with the explanations above you should be able to sort this out. You may also want to double check some of the rules with the customer and adjust your models accordingly.
A couple other things now:
First, unless you're working with a legacy database (which is obviously not the case here), you'd be better leaving the model fields db_column attribute alone - the ORM will use the model field's name as db column name, and that's most often the best default - at least you don't have to check your models.py file for column names when you want to do raw SQL queries. Note that for foreign keys, the model's attribute will yield the related model instance, but will create a "fieldname_id" column.
Second point: if a textfield or charfield is not required, do NOT use "null=True" - else you'd have two possible values indicating "no data", either SQL "NULL" or an empty string. Better to only have one of them, in this case the empty string, so remove the "null=True" and use "default=''" instead. Also, for free text (the "notes" field for example), you may want to use a textfield instead of a charfield. This avoids placing useless contraints on the maximum length (that you can bet the users WILL ask you to extend), and will also be translated by Django's ModelForms to a proper html "text" widget instead of a (single line) html "input".
Third point: "blank=False" and "null=False" are already the defaults - a field is required unless specified otherwise - so explicitely passing them for required fields only adds "code noise". The most readable code is no code at all ;-)
Hope that clears up things for you, if not feel free to ask for details / explanations in a comment.

What does adding on_delete to models.py do, and what should I put in it? [duplicate]

I'm quite familiar with Django, but I recently noticed there exists an on_delete=models.CASCADE option with the models. I have searched for the documentation for the same, but I couldn't find anything more than:
Changed in Django 1.9:
on_delete can now be used as the second positional argument (previously it was typically only passed as a keyword argument). It will be a required argument in Django 2.0.
An example case of usage is:
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass
What does on_delete do? (I guess the actions to be done if the model is deleted.)
What does models.CASCADE do? (any hints in documentation)
What other options are available (if my guess is correct)?
Where does the documentation for this reside?
This is the behaviour to adopt when the referenced object is deleted. It is not specific to Django; this is an SQL standard. Although Django has its own implementation on top of SQL. (1)
There are seven possible actions to take when such event occurs:
CASCADE: When the referenced object is deleted, also delete the objects that have references to it (when you remove a blog post for instance, you might want to delete comments as well). SQL equivalent: CASCADE.
PROTECT: Forbid the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
RESTRICT: (introduced in Django 3.1) Similar behavior as PROTECT that matches SQL's RESTRICT more accurately. (See django documentation example)
SET_NULL: Set the reference to NULL (requires the field to be nullable). For instance, when you delete a User, you might want to keep the comments he posted on blog posts, but say it was posted by an anonymous (or deleted) user. SQL equivalent: SET NULL.
SET_DEFAULT: Set the default value. SQL equivalent: SET DEFAULT.
SET(...): Set a given value. This one is not part of the SQL standard and is entirely handled by Django.
DO_NOTHING: Probably a very bad idea since this would create integrity issues in your database (referencing an object that actually doesn't exist). SQL equivalent: NO ACTION. (2)
Source: Django documentation
See also the documentation of PostgreSQL for instance.
In most cases, CASCADE is the expected behaviour, but for every ForeignKey, you should always ask yourself what is the expected behaviour in this situation. PROTECT and SET_NULL are often useful. Setting CASCADE where it should not, can potentially delete all of your database in cascade, by simply deleting a single user.
Additional note to clarify cascade direction
It's funny to notice that the direction of the CASCADE action is not clear to many people. Actually, it's funny to notice that only the CASCADE action is not clear. I understand the cascade behavior might be confusing, however you must think that it is the same direction as any other action. Thus, if you feel that CASCADE direction is not clear to you, it actually means that on_delete behavior is not clear to you.
In your database, a foreign key is basically represented by an integer field which value is the primary key of the foreign object. Let's say you have an entry comment_A, which has a foreign key to an entry article_B. If you delete the entry comment_A, everything is fine. article_B used to live without comment_A and don't bother if it's deleted. However, if you delete article_B, then comment_A panics! It never lived without article_B and needs it, it's part of its attributes (article=article_B, but what is article_B???). This is where on_delete steps in, to determine how to resolve this integrity error, either by saying:
"No! Please! Don't! I can't live without you!" (which is said PROTECT or RESTRICT in Django/SQL)
"All right, if I'm not yours, then I'm nobody's" (which is said SET_NULL)
"Good bye world, I can't live without article_B" and commit suicide (this is the CASCADE behavior).
"It's OK, I've got spare lover, I'll reference article_C from now" (SET_DEFAULT, or even SET(...)).
"I can't face reality, I'll keep calling your name even if that's the only thing left to me!" (DO_NOTHING)
I hope it makes cascade direction clearer. :)
Footnotes
(1) Django has its own implementation on top of SQL. And, as mentioned by #JoeMjr2 in the comments below, Django will not create the SQL constraints. If you want the constraints to be ensured by your database (for instance, if your database is used by another application, or if you hang in the database console from time to time), you might want to set the related constraints manually yourself. There is an open ticket to add support for database-level on delete constraints in Django.
(2) Actually, there is one case where DO_NOTHING can be useful: If you want to skip Django's implementation and implement the constraint yourself at the database-level.
The on_delete method is used to tell Django what to do with model instances that depend on the model instance you delete. (e.g. a ForeignKey relationship). The on_delete=models.CASCADE tells Django to cascade the deleting effect i.e. continue deleting the dependent models as well.
Here's a more concrete example. Assume you have an Author model that is a ForeignKey in a Book model. Now, if you delete an instance of the Author model, Django would not know what to do with instances of the Book model that depend on that instance of Author model. The on_delete method tells Django what to do in that case. Setting on_delete=models.CASCADE will instruct Django to cascade the deleting effect i.e. delete all the Book model instances that depend on the Author model instance you deleted.
Note: on_delete will become a required argument in Django 2.0. In older versions it defaults to CASCADE.
Here's the entire official documentation.
FYI, the on_delete parameter in models is backwards from what it sounds like. You put on_delete on a foreign key (FK) on a model to tell Django what to do if the FK entry that you are pointing to on your record is deleted. The options our shop have used the most are PROTECT, CASCADE, and SET_NULL. Here are the basic rules I have figured out:
Use PROTECT when your FK is pointing to a look-up table that really shouldn't be changing and that certainly should not cause your table to change. If anyone tries to delete an entry on that look-up table, PROTECT prevents them from deleting it if it is tied to any records. It also prevents Django from deleting your record just because it deleted an entry on a look-up table. This last part is critical. If someone were to delete the gender "Female" from my Gender table, I CERTAINLY would NOT want that to instantly delete any and all people I had in my Person table who had that gender.
Use CASCADE when your FK is pointing to a "parent" record. So, if a Person can have many PersonEthnicity entries (he/she can be American Indian, Black, and White), and that Person is deleted, I really would want any "child" PersonEthnicity entries to be deleted. They are irrelevant without the Person.
Use SET_NULL when you do want people to be allowed to delete an entry on a look-up table, but you still want to preserve your record. For example, if a Person can have a HighSchool, but it doesn't really matter to me if that high-school goes away on my look-up table, I would say on_delete=SET_NULL. This would leave my Person record out there; it just would just set the high-school FK on my Person to null. Obviously, you will have to allow null=True on that FK.
Here is an example of a model that does all three things:
class PurchPurchaseAccount(models.Model):
id = models.AutoField(primary_key=True)
purchase = models.ForeignKey(PurchPurchase, null=True, db_column='purchase', blank=True, on_delete=models.CASCADE) # If "parent" rec gone, delete "child" rec!!!
paid_from_acct = models.ForeignKey(PurchPaidFromAcct, null=True, db_column='paid_from_acct', blank=True, on_delete=models.PROTECT) # Disallow lookup deletion & do not delete this rec.
_updated = models.DateTimeField()
_updatedby = models.ForeignKey(Person, null=True, db_column='_updatedby', blank=True, related_name='acctupdated_by', on_delete=models.SET_NULL) # Person records shouldn't be deleted, but if they are, preserve this PurchPurchaseAccount entry, and just set this person to null.
def __unicode__(self):
return str(self.paid_from_acct.display)
class Meta:
db_table = u'purch_purchase_account'
As a last tidbit, did you know that if you don't specify on_delete (or didn't), the default behavior is CASCADE? This means that if someone deleted a gender entry on your Gender table, any Person records with that gender were also deleted!
I would say, "If in doubt, set on_delete=models.PROTECT." Then go test your application. You will quickly figure out which FKs should be labeled the other values without endangering any of your data.
Also, it is worth noting that on_delete=CASCADE is actually not added to any of your migrations, if that is the behavior you are selecting. I guess this is because it is the default, so putting on_delete=CASCADE is the same thing as putting nothing.
As mentioned earlier, CASCADE will delete the record that has a foreign key and references another object that was deleted. So for example if you have a real estate website and have a Property that references a City
class City(models.Model):
# define model fields for a city
class Property(models.Model):
city = models.ForeignKey(City, on_delete = models.CASCADE)
# define model fields for a property
and now when the City is deleted from the database, all associated Properties (eg. real estate located in that city) will also be deleted from the database
Now I also want to mention the merit of other options, such as SET_NULL or SET_DEFAULT or even DO_NOTHING. Basically, from the administration perspective, you want to "delete" those records. But you don't really want them to disappear. For many reasons. Someone might have deleted it accidentally, or for auditing and monitoring. And plain reporting. So it can be a way to "disconnect" the property from a City. Again, it will depend on how your application is written.
For example, some applications have a field "deleted" which is 0 or 1. And all their searches and list views etc, anything that can appear in reports or anywhere the user can access it from the front end, exclude anything that is deleted == 1. However, if you create a custom report or a custom query to pull down a list of records that were deleted and even more so to see when it was last modified (another field) and by whom (i.e. who deleted it and when)..that is very advantageous from the executive standpoint.
And don't forget that you can revert accidental deletions as simple as deleted = 0 for those records.
My point is, if there is a functionality, there is always a reason behind it. Not always a good reason. But a reason. And often a good one too.
Using CASCADE means actually telling Django to delete the referenced record.
In the poll app example below: When a 'Question' gets deleted it will also delete the Choices this Question has.
e.g Question: How did you hear about us?
(Choices: 1. Friends 2. TV Ad 3. Search Engine 4. Email Promotion)
When you delete this question, it will also delete all these four choices from the table.
Note that which direction it flows.
You don't have to put on_delete=models.CASCADE in Question Model put it in the Choice.
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.dateTimeField('date_published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_legth=200)
votes = models.IntegerField(default=0)
simply put, on_delete is an instruction to specify what modifications will be made to the object in case the foreign object is deleted:
CASCADE: will remove the child object when the foreign object is deleted
SET_NULL: will set the child object foreign key to null
SET_DEFAULT: will set the child object to the default data given while creating the model
RESTRICT: raises a RestrictedError under certain conditions.
PROTECT: prevents the foreign object from being deleted so long there are child objects inheriting from it
additional links:
https://docs.djangoproject.com/en/4.0/ref/models/fields/#foreignkey
Here is answer for your question that says: why we use on_delete?
When an object referenced by a ForeignKey is deleted, Django by default emulates the behavior of the SQL constraint ON DELETE CASCADE and also deletes the object containing the ForeignKey. This behavior can be overridden by specifying the on_delete argument. For example, if you have a nullable ForeignKey and you want it to be set null when the referenced object is deleted:
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
The possible values for on_delete are found in django.db.models:
CASCADE: Cascade deletes; the default.
PROTECT: Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
SET_NULL: Set the ForeignKey null; this is only possible if null is True.
SET_DEFAULT: Set the ForeignKey to its default value; a default for the ForeignKey must be set.
Let's say you have two models, one named Person and another one named Companies, and that, by definition, one person can create more than one company.
Considering a company can have one and only one person, we want that when a person is deleted that all the companies associated with that person also be deleted.
So, we start by creating a Person model, like this
class Person(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=20)
def __str__(self):
return self.id+self.name
Then, the Companies model can look like this
class Companies(models.Model):
title = models.CharField(max_length=20)
description=models.CharField(max_length=10)
person= models.ForeignKey(Person,related_name='persons',on_delete=models.CASCADE)
Notice the usage of on_delete=models.CASCADE in the model Companies. That is to delete all companies when the person that owns it (instance of class Person) is deleted.
Reorient your mental model of the functionality of "CASCADE" by thinking of adding a FK to an already existing cascade (i.e. a waterfall). The source of this waterfall is a primary key (PK). Deletes flow down.
So if you define a FK's on_delete as "CASCADE," you're adding this FK's record to a cascade of deletes originating from the PK. The FK's record may participate in this cascade or not ("SET_NULL"). In fact, a record with a FK may even prevent the flow of the deletes! Build a dam with "PROTECT."
Deletes all child fields in the database when parent object is deleted then we use on_delete as so:
class user(models.Model):
commodities = models.ForeignKey(commodity, on_delete=models.CASCADE)
CASCADE will also delete the corresponding field connected with it.

Categories

Resources