I have the following sample models:
class Note(models.Model):
text = models.TextField()
author = models.OneToOneField(User)
date_created = models.DateField(auto_now_add=True)
similar_note = models.ForeignKey("self", related_name='similar_note', null=True, blank=True)
Say there are two notes Winner and Loser.
Loser has a field similar_note that points to Winner Note.
When I delete any of the two, both gets deleted, how do I prevent this from happening?
I have tried doing the same from the django admin interface as well, it happens from there as well.
PS: I am using django1.2, please don't advice to upgrade, there are way too many constraints.
As mentioned here:
When Django deletes an object, by default it emulates the behavior of
the SQL constraint ON DELETE CASCADE -- in other words, any objects
which had foreign keys pointing at the object to be deleted will be
deleted along with it.
This cascade behavior is customizable via the on_delete argument to
the ForeignKey
Please check the on_delete parameter for model field:
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
Related
I was developing a chat system with channels and have this models for a thread (some attributes removed for simplicity's sake):
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
users = models.ManyToManyField('auth.User')
I realized it is also possible to implement it like this:
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
class ThreadUsers(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
What are the advantages/disadvantages of using one over the other?
All what you do - is the same. For your last example with custom M2M through model you can add M2M declaration users in Thread:
class Thread(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
# M2M declaration, which use your ThreadUsers
users = models.ManyToManyField('auth.User', through='ThreadUsers' )
class ThreadUsers(models.Model):
thread = models.ForeignKey(Thread, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
Pros:
You create model self
you can change behavior your M2M connection manually.
You can add additional fields in M2M through connection.
You have full control of this model.
Cons:
problem with m2m connections in django admin.
too much additional code, you can get a hard-to-find errors.
some fields/widgets don't want to work with M2M.through.
all was happened is your problem, this is not tested. Auto-through relation is tested in box.
we have in our projects 50/50 M2M-autothrough vs M2M-manualthrough. if I want to have more control on the models and realations - i use custom through.
p.s. in M2M-autothrough case Django created SomethingLikeYourThreadUsers Model and Table automatically.
We have two Django models:
class Project(models.Model):
project_title = models.CharField(max_length=30)
owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
class User(models.Model):
usernmae = models.CharField(max_length=50)
active_project = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING, related_name='current_project')
I have a user with object (with id say 692). And this user created a project with id=12345, therefore these owner field will get have this particular referenced.
I want to delete that user. But it shows error that
delete on table "app_user" violates foreign key constraint
This is expected as on_delete=models.DO_NOTHING, was set. One way I found out was using on_delete=models.CASCADE.
Question: How should I go about deleting the user (692) without changing the model definition(having to re-run migration)?
Doing it manually by deleting the project first, leads to the same foreign-key error, as owner field is User object.
How to handle this mutual foreign key relationship while deleting, as deleting any one of those two throws the foreign-key exception?
Update
Some correction in the model definition username is the field name instead of usernmae (typo). And the foreignkey for project is Project not the User model.
class Project(models.Model):
project_title = models.CharField(max_length=30)
owner = models.ForeignKey(User, null=True, on_delete=models.DO_NOTHING)
class User(models.Model):
username = models.CharField(max_length=50)
active_project = models.ForeignKey(Project, null=True, on_delete=models.DO_NOTHING, related_name='current_project')
IF you really don't want to make a migration (any specific reason?) and if you are ok with doing this manually this time. Then you have two options:
Go into the admin panel and manually change the User field in the project instance to a different user or to NULL. Now you should be able to delete the User instance since it's not referred anymore into the project.
If that worked, you can then delete the project instane as well.
Curios if this will work, let me know!
I have a Tournament model with related Standings:
class Tournament(models.Model):
standings = models.ForeignKey('Standings', blank=True, null=True)
I just did the following:
standings = Standings.objects.get(pk=pk)
standings.delete()
And it deleted the related tournament as well. This should not have happened, since standings is a nullable field.
Why did it happen and what can I do to prevent this from happening again?
Django 1.8 defaults to CASCADE:
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.
To change that behavior, you should add the on_delete argument (mandatory as of Django 2.x) as follows:
standings = models.ForeignKey(
'Standings', blank=True, null=True, on_delete=models.SET_NULL)
I went to add a text field to a model in my django app, made, applied my migrations then all of a sudden, I have tests failing left and right. Turns out, the migration decided to drop a field that still exists in my model.
Before going on, some of the relevant code. First, the effected model:
class CandidateProfile(models.Model):
user_profile = models.ForeignKey(UserProfile, related_name="candidate_profile", null=True, blank=True)
facebook_url = models.CharField(max_length=200, default="")
website = models.CharField(max_length=200, default="")
primary_email = models.CharField(max_length=100, default="")
party = models.ForeignKey(PoliticalParty, related_name="candidate_party", null=True, blank=True)
uploaded_picture = models.CharField(max_length=200, default="")
ref_id = models.CharField(db_index=True, max_length=50, default="")
create_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
The field I added was facebook_url. Prior to this migration, app working, tests passing, etc. The migration that was generated is:
class Migration(migrations.Migration):
dependencies = [
('users', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='candidateprofile',
name='party',
),
migrations.AddField(
model_name='candidateprofile',
name='facebook_url',
field=models.CharField(default=b'', max_length=200),
),
]
The fix itself is simple enough, I can roll back my migration, and manually remove the migrations.RemoveField. Unit tests did what they were supposed to do in letting me know I screwed something up, but I still get worried about something like this getting through to production, losing data and having to restore from a backup.
My question is: why does Django think that the field should be removed even though, it's clearly present as a foreign key in the model?
Edit: After the field was deleted, I tried changing properties on the party attribute to see if whatever diff-ing mechanism Django uses will pick it up. No dice, changing related_name, null, or blank didn't do anything. Running makemigrations detected no changes.
Also, foreign key model for reference:
class PoliticalParty(models.Model):
name = models.CharField(max_length="100")
full_name = models.CharField(max_length="255")
abbreviation = models.CharField(max_length="20")
ref_id = models.CharField(max_length="50", default="", db_index=True)
create_date = models.DateTimeField(auto_now_add=True)
update_date = models.DateTimeField(auto_now=True)
My guess is that you 'shadowed' the field, by creating an attribute or method with the same name.
class CandidateProfile(models.Model):
party = models.ForeignKey(PoliticalParty, related_name="candidate_party", null=True, blank=True)
def party(self):
"""This method will replace the model field"""
return ''
If you didn't do this, then please try to provide instructions that can recreate the issue (preferably with the latest point release 1.8.7). Dropping an existing field is a very serious data loss issue, and the Django developers would take it very seriously.
I used inheritance model in my project after changing the model; but I give non-nullable field error. What should I do?
I am using Django 1.7
class Questions(models.Model):
question_category = models.ForeignKey(Course, blank=False)
question_author = models.ForeignKey(Author, blank=False)
question_details = models.CharField(max_length=100, blank=False, default='')
timestamp = models.DateTimeField(auto_now_add=True)
class TypeFive(Questions):
question_title = models.CharField(max_length=100, blank=False, default=generator(5), unique=True, editable=False)
def __str__(self):
return "{}".format(self.question_title)
class TypeFiveChoice(models.Model):
question_choice = models.ForeignKey(TypeFive)
is_it_question = models.BooleanField(default=False)
word = models.CharField(default='', blank=False, max_length=20)
translate = models.CharField(default='', blank=False, max_length=20)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return "{} : {}, {}".format(self.question_choice, self.word, self.translate)
After migrations:
You are trying to add a non-nullable field 'questions_ptr' to typefive without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
In order to inherit from Questions in TypeFive, Django needs to add a relation from TypeFive to Questions. For all records in TypeFive that might already be in the database.
Django now doesn't know which question it should relate TopFive to. This is what the migrate command asks you for. You have a few options, but they greatly depend on your use case and whether you are in early development or if there is a production database where this migration has to run later.
I'm in early development and running it on localhost, so iI don't care
about my records. Now, what should I do?
In this case you haven't much to worry about, when migrate asks you type 1 and then press enter. Now add a primary key of a Questions instance that is in your database and then hit enter again.
Django now relates all TypeFive instances that are currently in the database to this question, so you might have to clean that up afterwards (e.g. by editing the TypeFive in Django admin).
#Nick Brady pointed this out in the question above so I don't mean to take credit but I wanted to highlight.
If your new inheritance class is only used for the purpose of being inherited from, you can easily get around this by setting your parent class to abstract.
class Parent(models.model):
Name = models.CharField(max_length=50)
class Meta:
abstract = True
class Child(Parent):
foobar = models.CharField(max_length=50)
class Meta:
db_table = "typenex_taxonomy_nodes"