FOREIGN KEY constraint failed because of models.DO_NOTHING - django

I have this snippet of code for models.py
class Provider(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
provider = models.CharField(max_length=100, unique=True)
active = models.BooleanField(default=True)
when ever I tried to delete an object I faced an error says :
django.db.utils.IntegrityError: FOREIGN KEY constraint failed
I faced this issue on django 2.x , since every thing was perfect on 1.11.
I made a little search I found may this issue happen by this part on_delete=models.DO_NOTHING, So how could I fix it with kepping every thing as it is ?

Basically you're telling Django to do nothing when you delete a user. So you will try to delete the row which has a related foreign key, this is expected behavior.
If you want to keep the provider model even when the user got deleted you have to make user nullable and use models.SET_NULL.
If provider has non sense in your logic you can then cascade.
If you need to reassign to a default user you can use custom method.

Related

How to optimize call to database with OneToOneField?

According to the documentation, to optimize the db access :
If you only need a foreign key value, use the foreign key value that
is already on the object you’ve got, rather than getting the whole
related object and taking its primary key. i.e. do:
entry.blog_id
No problem to use with a ForeignKey and it works as intended.
But if I want to do the same with OneToOneField, it is not working :
Class CustomUser(Model):
...
class Profile(Model):
user = models.OneToOneField(
CustomUser,
on_delete=models.CASCADE,
)
Then, if I try to use the same tip as described in the documentation, in a view for example :
request.user.profile_id
I got the followig error :
AttributeError: 'CustomUser' object has no attribute 'profile_id'
Of course, it works with request.user.profile.uid but it is not the point here because there is an additional query to the DB.
Is it intended ? Is it a con to using OneToOneField ?
Since you're doing it in reverse (it's the model Profile that has the field user_id, and not CustomUser that has the field profile_id) I think you can't use this optimization. You'd have to move the OneToOneField to CustomerUser model. You can still access the object of course, but you're going to hit the database once more.
Edit
If possible for your project, this should work:
class Profile(Model):
...
class CustomUser(Model):
profile = models.OneToOneField(
Profile,
on_delete=models.CASCADE,
)
request.user.profile_id

What is the expected behaviour when a model has two foreign keys with different on_delete constraints?

Let's say I have this model:
class UserBook(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
book = models.ForeignKey(Book, on_delete=models.PROTECT)
Where the user is only allowed to borrow 1 book at a time. I want instances of this model to get deleted if the user gets deleted, but I don't want them to get deleted if a book gets deleted (by mistake, just a precaution).
What is the expected behaviour when a user gets deleted using the above constraint? I'm getting:
Cannot delete some instances of model 'UserBook' because they are
referenced through a protected foreign key
Is there a way to achieve what I want? I tried to delete UserBook on pre_save/post_save signals with the User as a sender but neither worked.
What you are trying to achieve is done through RESTRICT option.
Here is an example from the official docs
They also use models.ForeignKey and not models.OneToOne
This post may also be helpful https://stackoverflow.com/a/38389488/13482680

Getting a "The following content types are stale and need to be deleted" when trying to do a migrate. What does this mean, and how can I solve it?

This is my models.py:
class Notification(models.Model):
user = models.ForeignKey(User)
createdAt = models.DateTimeField(auto_now_add=True, blank=True)
read = models.BooleanField(default=False, blank=True)
class Meta:
abstract = True
class RegularNotification(Notification):
message = models.CharField(max_length=150)
link = models.CharField(max_length=100)
class FNotification(Notification):
# same as Notification
pass
When I do python manage.py makemigrations, this is what it says:
Migrations for 'CApp':
0019_auto_20151202_2228.py:
- Create model RegularNotification
- Create model FNotification
- Remove field user from notification
- Add field f_request to userextended
- Delete model Notification
First, it's weird that it says Remove field user from notification because user is still in my Notiication model (so if anyone can figure out why it says that it say 'removing the field user from notification', that would be great!) but nonetheless, when I move on and try to do python manage.py migrate I get this message:
Applying CMApp.0019_auto_20151202_2228... OK
The following content types are stale and need to be deleted:
CApp | notification
Any objects related to these content types by a foreign key will also
be deleted. Are you sure you want to delete these content types?
If you're unsure, answer 'no'.
Type 'yes' to continue, or 'no' to cancel: no
I typed no. But what exactly does this mean, why am I getting this message and how do I make it so that I don't require this message?
The message you get is triggered when you remove/delete a model and do a migration.
In most cases, you can delete them safely. However, in some cases this might result to data loss. If other models have a foreign key to the removed model, these objects will also be deleted.
Here's the django ticket that requests to make deleting stale content types safer.
EDIT
As #x-yuri pointed, this ticket has been fixed and has been released in Django 1.11.

Django 1.4 Multiple Databases Foreignkey relationship (1146, "Table 'other.orders_iorder' doesn't exist")

I have a Foreign Key from one model into another model in a differente database (I know I shouldn't do it but if I take care properly of Referential Integrity it shouldn't be a problem).
The thing is that everything works fine...all the system does (relationships on any direction, the router takes care of it) but when I try to delete the referenced model (which doesn't have the foreign key attribute)...Django still wants to go throught the relationship to check if the relationship is empty, but the related object is on another database so it doesn't find the object in this database.
I tried to set up on_delete=models.DO_NOTHING with no success. Also tried to clear the relationship (but it happens clear doesn't have "using" argument so I it doesn't work either). Also tried to empty the relationship with delete(objects...), no success.
Now I am pretty sure the problem is in super(Object,self).delete(), I can not do super(Object,self).delete(using=other_database) because the self object is not in another database just the RelatedManager is. So I don't know how to make Django to understand I don't want even to check that relationship, which by the way was already emptied before the super(Object,self).delete() request.
I was thinking if there is some method I can override to make Django avoid this check.
More graphical:
DB1: "default" database (orders app)
from django.db import models from shop.models import Order
class IOrder(models.Model):
name = models.CharField(max_length=20, unique=True, blank=False, null=False)
order = models.ForeignKey(Order, related_name='iorders', blank=True, null=True)
DB2: "other" database
class Order(models.Model):
description = models.CharField(max_length=20, blank=False, null=False)
def delete(self):
# Delete iOrder if any
for iorder in self.iorders.using('default'):
iorder.delete()
# Remove myself
super(Order, self).delete()
The problem happens when supper(Order.self).delete() is called, then it can not find the table (iorder) in this database (because it is in 'default')
Some idea? Thanks in advance,
I already resolved my issue changing super(Order,self).delete() with a raw SQL delete command. Anyway I would love to know if there is a more proper way of doing this

Creating Custom User Backends in Django

I have a Shops model and would like each shop to be able to login to my application. Following as best I can the guide at http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/ and various other googlings, I've got part of the way there, but I've run into a problem. When I try to login as a shop, I get the following error:
OperationalError at /login/
(1054, "Unknown column 'shops.user_ptr_id' in 'field list'")
Shops model:
class Shops(User):
shop_id = models.AutoField(primary_key=True)
shop_code = models.CharField(unique=True, max_length=5)
shop_type_fk = models.ForeignKey(ShopTypes,
null=True,
db_column='shop_type_id',
blank=True)
address_fk = models.ForeignKey(Addresses, db_column='address_id')
phone_number = models.CharField(max_length=30)
#email = models.EmailField(max_length=255, blank=True)
description = models.TextField(blank=True)
does_gift_aid = models.NullBooleanField(null=True, blank=True)
objects = UserManager()
class Meta:
db_table = u'shops'
I've sync'd the database, so surely it should have made the column user_ptr_id. Does anyone know where I'm going wrong?
"I've sync'd the database, so surely it should have made the column user_ptr_id."
What makes you think that? Especially in light of this clear statement in the docs for syncdb:
Syncdb will not alter existing tables
syncdb will only create tables for
models which have not yet been
installed. It will never issue ALTER
TABLE statements to match changes made
to a model class after installation.
Changes to model classes and database
schemas often involve some form of
ambiguity and, in those cases, Django
would have to guess at the correct
changes to make. There is a risk that
critical data would be lost in the
process.
If you have made changes to a model
and wish to alter the database tables
to match, use the sql command to
display the new SQL structure and
compare that to your existing table
schema to work out the changes.
It does sound like you had an existing shops table before changing it to inherit from User (as Daniel notes), and syncdb does not update the schema for existing tables.
You need to drop the table and then run syncdb, if possible. Otherwise you need to go into your database and add the user_ptr_id field manually, if you know how to do that. The definition should look something like this:
"user_ptr_id" integer NOT NULL UNIQUE REFERENCES "auth_user" ("id")