Possible source of cascade deletion on Django 1.9 project? - python

I'm working on a Django 1.9 project with a huge source and lots of is inherited from previous developers.
When I delete a User object from the shell there are many other related objects that get deleted as well. I get a print output with a Tuple containing:
(# of deleted objects, {u'CLASS_NAME': #number of deleted objects...})
I've searched all the code throughly and there are no Signals attached to User pre_ post_ or on_ delete, neither I can find where this print output is constructed... so, I have no idea where this is coming from.
Any ideas for other possible ways this can be happening?

A ForeignKey [Django-doc] has an on_delete=… parameter. Prior to django-2.0, you did not have to state this explicitly, in that case it used uses CASCADE, as is specified in the documentation:
on_delete will become a required argument in Django 2.0. In older versions it defaults to CASCADE.
This thus means that if you did not specify an on_delete=… parameter, or you specified this as CASCADE, then if the target record is removed, that model record is removed as well.
You can specify another handler for this. In fact you can even write your own handler, but unless you have to do something very sophisticated, the builtin handlers will likely be sufficient, you can choose between:
CASCADE
PROTECT
SET_NULL
SET_DEFAULT
SET(…)
DO_NOTHING
There are basically four categories: a cascade (CASCADE), prevent the removal (PROTECT), set it to another value (SET_NULL, SET_DEFAULT, SET(…), and DO_NOTHING.
DO_NOTHING is often not a good idea, since most databases enforce referential integrity and will thus refuse to update/delete, since then the foreign key column no longer points to a valid record. By SET_NULL, the field needs to be NULLable (so null=True), you can also set t to a different view.
You should thus pick the strategy that you think is the best, and then the ForeignKey looks like:
class MyModel(models.Model):
my_field = models.ForeignKey(OtherModel, null=True, on_delete=models.SET_NULL)

Related

is it possible to override a foreign key relation with a custom method/property

Context
I'm working on refactoring a Django 2.X app, particularly the core model, CoreModel. There's a single database (Postgres) containing all related tables.
Instances of CoreModel will no longer live in Postgres after this refactor, they will live somewhere else but outside the scope of the Django project, let's say some AWS No-SQL database service.
There also several satellites models SateliteModel to CoreModel which will continue to live on Postgres, but CoreModelis currently modelled as a foreign key field.
class CordeModel(models.Model):
pass
class SatelliteModel(models.Model):
core = models.ForeignKey(CoreModel)
def some_instance_method(self):
return self.core.calculate_stuff() # <- override self.core!
Problem
The code is filled with mentions to the CoreModel relation, and I haven't been able to successfully solved this issue.
My first naive approach was to implement a #property getter method, that way I had enough flexibility to do something like:
#property
def core(self):
try:
# ORM
return self.core
except CoreNotFound:
# External datastore
return aws_client.fetch_core()
With this snippet I have a circular dependency on the core name, so the idea is out.
I could rename the foreign key: but I would much rather not touch the database schema. After all I'm already refactoring the central part of the app, and that's an very error-prone process. I'd do this if there's no other choice.
I could rename the #property field, to something like current_core: This way I avoid the infinite recursion part, but this in turn would imply a very big task of searching the whole code base for mentions of the relation, and this being the central model, it would take a lot of time.
After some hours of research I'm beginning to doubt if the concept of overriding a getter for a foreign key field is possible, as I need it. Maybe this is isn't exactly what I'm looking for, it's a very unusual use case, but the requirement is also very unusual.
Any insights you can give are greatly appreciated.
UPDATE
I've forgotten to add the most crucial piece of information.
Most CoreModel will be removed for Postgres (the historic ones), but there's a tiny part of CoreModels that will remain and will be moved after a while. In essence, only the "active" CoreModels will stay in Postgres, but all will eventually be moved out, while new CoreModel will be created.
So that rules out the possibility of change the ForeignKey field for an integer.
You could retain but rename the foreign key and then add a property with the old name
class SatelliteModel(models.Model):
old_core = models.ForeignKey(CoreModel, null=True, blank=True, on_delete=models.SET_NULL)
#property
def core(self):
try:
return self.old_core
except CoreModel.DoesNotExist:
return aws_client.fetch_core()
This would change the column name in your schema, although you could override the column name to prevent this
old_core = models.ForeignKey(CoreModel, db_column='core_id', null=True, blank=True, on_delete=models.SET_NULL)
It may be possible to create a subclass of ForeignKey that would perform as you wished, if this answer is not sufficient I can share some thoughts

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.

Get rid of get_profile() in a migration to Django 1.6

With Django 1.5 and the introduction of custom user models the AUTH_PROFILE_MODULE became deprecated. In my existing Django application I use the User model and I also have a Profile model with a foreign key to the User and store other stuff about the user in the profile. Currently using AUTH_PROFILE_MODULE and this is set to 'app.profile'.
So obviously, my code tends to do lots of user.get_profile() and this now needs to go away.
Now, I could create a new custom user model (by just having my profile model extend User) but then in all other places where I currently have a foreign key to a user will need to be changed also... so this would be a large migration in my live service.
Is there any way - and with no model migration - and only by creating/overriding the get_profile() function with something like my_user.userprofile_set.all()[0]) somewhere?
Anyone out there that has gone down this path and can share ideas or experiences?
If I where to do this service again now - would obviously not go this way but with a semi-large live production system I am open for short-cuts :-)
Using a profile model with a relation to the built-in User is still a totally legitimate construct for storing additional user information (and recommended in many cases). The AUTH_PROFILE_MODULE and get_profile() stuff that is now deprecated just ended up being unnecessary, given that built-in Django 1-to-1 syntax works cleanly and elegantly here.
The transition from the old usage is actually easy if you're already using a OneToOneField to User on your profile model, which is how the profile module was recommended to be set up before get_profile was deprecated.
class UserProfile(models.Model):
user = OneToOneField(User, related_name="profile")
# add profile fields here, e.g.,
nickname = CharField(...)
# usage: no get_profile() needed. Just standard 1-to-1 reverse syntax!
nickname = request.user.profile.nickname
See here if you're not familiar with the syntactic magic for OneToOneField's that makes this possible. It ends up being a simple search and replace of get_profile() for profile or whatever your related_name is (auto related name in the above case would be user_profile). Standard django reverse 1-1 syntax is actually nicer than get_profile()!
Change a ForeignKey to a OneToOneField
However, I realize this doesn't answer your question entirely. You indicate that you used a ForeignKey to User in your profile module rather than a OneToOne, which is fine, but the syntax isn't as simple if you leave it as a ForeignKey, as you note in your follow up comment.
Assuming you were using your ForeignKey in practice as an unique foreign key (essentially a 1-to-1), given that in the DB a OneToOneField is just a ForeignKey field with a unique=True constraint, you should be able to change the ForeignKey field to a OneToOneField in your code without actually having to make a significant database migration or incurring any data loss.
Dealing with South migration
If you're using South for migrations, the code change from the previous section may confuse South into deleting the old field and creating a new one if you do a schemamigration --auto, so you may need to manually edit the migration to do things right. One approach would be to create the schemamigration and then blank out the forwards and backwards methods so it doesn't actually try to do anything, but so it still freezes the model properly as a OneToOneField going forward. Then, if you want to do things perfectly, you should add the unique constraint to the corresponding database foreign key column as well. You can either do this manually with SQL, or via South (by either editing the migration methods manually, or by setting unique=True on the ForeignKey and creating a first South migration before you switch it to a OneToOneField and do a second migration and blank out the forwards/backwards methods).

Nullable ForeignKeys and deleting a referenced model instance

I have a ForeignKey which can be null in my model to model a loose coupling between the models. It looks somewhat like that:
class Message(models.Model):
sender = models.ForeignKey(User, null=True, blank=True)
sender_name = models.CharField(max_length=255)
On save the senders name is written to the sender_name attribute. Now, I want to be able to delete the User instance referenced by the sender and leave the message in place.
Out of the box, this code always results in deleted messages as soon as I delete the User instance. So I thought a signal handler would be a good idea.
def my_signal_handler(sender, instance, **kwargs):
instance.message_set.clear()
pre_delete.connect(my_signal_handler, sender=User)
Sadly, it is by no means a solution. Somehow Django first collects what it wants to delete and then fires the pre_delete handler.
Any ideas? Where is the knot in my brain?
Django does indeed emulate SQL's ON DELETE CASCADE behaviour, and there's no out-of-the box documented way to change this. The docs where they mention this are near the end of this section: Deleting objects.
You are right that Django's collects all related model instances, then calls the pre-delete handler for each. The sender of the signal will be the model class about to be deleted, in this case Message, rather than User, which makes it hard to detect the difference between a cascade delete triggered by User and a normal delete... especially since the signal for deleting the User class comes last, since that's the last deletion :-)
You can, however, get the list of objects that Django is proposing to delete in advance of calling the User.delete() function. Each model instance has a semi-private method called _collect_sub_objects() that compiles the list of instances with foreign keys pointing to it (it compiles this list without deleting the instances). You can see how this method is called by looking at delete() in django.db.base.
If this was one of your own objects, I'd recommend overriding the delete() method on your instance to run _collect_sub_objects(), and then break the ForeignKeys before calling the super class delete. Since you're using a built-in Django object that you may find too difficult to subclass (though it is possible to substitute your own User object for django's), you may have to rely on view logic to run _collect_sub_objects and break the FKs before deletion.
Here's a quick-and-dirty example:
from django.db.models.query import CollectedObjects
u = User.objects.get(id=1)
instances_to_be_deleted = CollectedObjects()
u._collect_sub_objects(instances_to_be_deleted)
for k in instances_to_be_deleted.ordered_keys():
inst_dict = instances_to_be_deleted.data[k]
for i in inst_dict.values():
i.sender = None # You will need a more generic way for this
i.save()
u.delete()
Having just discovered the ON DELETE CASCADE behaviour myself, I see that in Django 1.3 they have made the foreign key behaviour configurable.

Categories

Resources