Django on_delete=models.CASCADE has no effect at SQL level - python

My models.py file contains:
class User(models.Model):
email = models.CharField(max_length=100, unique=True)
password = models.CharField(max_length=100)
create_time = models.DateTimeField(auto_now_add=True)
class Session(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
token = models.CharField(max_length=100, unique=True)
When i command python manage.py makemigrations and then command python manage.py sqlmigrate <app_name> <migration_name> i don't see anything that says "ON DELETE=CASCADE"
However, the migrations work without failure when i type python manage.py migrate.
Now, if i go to the mysql table (using SequelPro) and try to delete a row for a user who currently has a session entry, i get the following error: "One row was not removed. Reload the table to be sure that the contents have not changed in the meantime. Check the Console for possible errors inside the primary key(s) of this table!".
Now, when i go to the session table and delete the sessions of this user and then try to delete the user's row from the user table, it deletes properly. This indicates ON DELETE = CASCADE is not actually working at the MySQL level.
How can i correct it?

From the docs (emphasis mine):
ForeignKey.on_delete
When an object referenced by a ForeignKey is deleted, Django will
emulate the behavior of the SQL constraint specified by the on_delete
argument.
Django does not actually set an ON DELETE clause in the database. If you need one, you can add one manually using a RunSQL operation. Be sure to use the same index name, or keep the original index, or you might run into errors later on.

Related

Django asking for default value during migration

I've got the following models:
title = models.CharField(max_length=100)
description = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
class Ingredient(models.Model):
ingredients_recipe = models.ForeignKey(Post, on_delete=models.CASCADE)
type = models.CharField(max_length=50)
quantity = models.IntegerField()
measurement = models.CharField(max_length=50)
class MethodStep(models.Model):
methods_recipe = models.ForeignKey(Post, on_delete=models.CASCADE, null=True)
step = models.TextField()
When i try to run makemigrations, i get no errors for the Ingredient class, but on the MethodStep class i get an error saying:
You are trying to add a non-nullable field 'methods_recipe' to methodstep without a default; we can't do that (the database needs something to populate existing rows).
Can someone help me? I'm not sure whats different about each of these classes which would mean the MethodStep has an error but Ingredient doesn't.
Edit: I've also tried deleting/clearing/removing the db but the error persists everytime makemigrations is run.
Sometimes in addition to resetting the database you will also have to remove all existing Migrations from your project (or at least the ones for the package these models belong to).
Delete all files in the migrations folder EXCEPT for the _init__.py file.
When simply dumping your db and restarting django, startup will apply the migration history in the order they were created.
That means if you added this ForeignKey Field after already migrating changes to the db, django won't let you migrate without a default unless you delete the migration history.
This happens after you create a model field.
This is because you have added rows/objects in the table/model since your last migration.
Django is aware of table rows in your db and and cannot figure out what to fill the new field with for the old rows. To fix this, depending on the type of field you created newly, update the field adding:
default=None :if you don't mind those fields having nothing to go with otherwise consider some default string or int or float consistent with the field going forward
AND also add
blank = True
To take everything back to square one, apart from deleting your db, you will also need to delete associated migration files. If you are not sure which it is, delete all of them but do not touch the init file in the folder. Then makemigrations and migrate.
PS: I'm sure you know that with the last approach you lose all records in the model.

Django Migrate Fails due to Type Conflict

Here is my Chapter model code:
class Chapter(models.Model):
name = models.CharField(max_length=180)
is_canon = models.BooleanField(default=True)
series_id = models.ForeignKey('Series', on_delete=models.CASCADE,
blank=True, null=True)
def __str__(self):
return self.name
When running python3 manage.py makemigrations, I provided a one off value of 'NULL' for the new field series_id in order to populate existing rows, when I should have backed out and added blank=True, null=True to the definition, as it is now in the code provided above. So, now, I want to run python3 manage.py migrate, but of course the migration fails because I've got a bunch of 'NULL' strings in places where django is expecting actual NULL values/integer values.
How to I get rid of those 'NULL' values and reset things so I can migrate?
Thank you for your time.
Adding to Pythonista comment. You could roll back to the previous migration. Delete the migration file and create it once again. That is the cleanest way in my opinion.

django: default for non-nullable field

I am learning django by doing some tutorials and am currently on ownership in the effective-django tutorial. Unfortunately, the tutorials is written for an older version of django so some things are slightly different.
I am running into problems when adding an 'owner' field to my model Contact in models.py:
from django.db import models
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class Contact(models.Model):
"""
Interface for contact class
"""
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField()
owner = models.ForeignKey(User)
def __str__(self):
return ' '.join([self.first_name, self.last_name])
def get_absolute_url(self):
return reverse('contacts-view', kwargs={'pk': self.id})
When trying to run 'makemigrations', i get warnings:
You are trying to add a non-nullable field 'owner' to contact without a default: can't do that
This makes sense for an existing database, so i tried flushing the database. However, the problem remains. What should be the default value for the field owner? I also tried making the field nullable but that gives me the error that field owner cannot be null.
I usually set to 1. In this case already existed records will be assigned to the first user.
If you want you can later reassign these records in the next data migration.
Well, it does not matter if the database is empty or not. It matters that you probably have already at least one migration that first creates the model, and then you try to run makemigrations that adds the field.
If these migrations are not too important for you (which I recon they are not since this is a tutorial), just delete all previous migrations and run makemigrations fresh.
Alternatively, you would have to manually change migrations, possibly provide a default value etc.

Provide a default for ForeignKey field on existing entries in Django

I have this model:
class Movie(models.Model):
# here are some fields for this model
to which I added the following field (the database was already populated with Movie models):
user = models.ForeignKey(User, default=1)
I ran the commands 'makemigrations' and then 'migrate':
python manage.py makemigrations myapp
python manage.py migrate
but it doesn't work. What I want to do is to add the 'user' field to Movie objects and provide a default for it for all the existing Movie objects in my database (in this case the default is the User object with id=1).
Another thing that I tried is to leave it without the default value and then, when i run the 'makemigrations' command, to give it the default value 1 (by selecting the "provide a one-off default now" option). In both cases, when I run the 'migrate' command I get an IntegrityError:
django.db.utils.IntegrityError: movies_movie__new.user_id may not be NULL
I also checked the ids for the Users that are already in the database to make sure that a User with id=1 exists and it does. So why doesn't it work? Thank you.
If you add "null=True" it works:
user = models.ForeignKey(User, default=1, null=True)
Then
python manage.py makemigrations movies
Migrations for 'movies':
0003_auto_20150316_0959.py:
...
- Add field user to Movie
...
Then
python manage.py migrate movies
The database should look like:
id|user_id|name
1|1|movie_name
2|1|movie_2
3|1|movie_3
...
All the empty user_ids get a value of 1. So you're done.
Now, if you need to make that field required, just change the model back to
user = models.ForeignKey(User, default=1)
make another migration
and migrate again
This time it will work since all fields have user_id.
;-)

Django Models (1054, "Unknown column in 'field list'")

No idea why this error is popping up. Here are the models I created -
from django.db import models
from django.contrib.auth.models import User
class Shows(models.Model):
showid= models.CharField(max_length=10, unique=True, db_index=True)
name = models.CharField(max_length=256, db_index=True)
aka = models.CharField(max_length=256, db_index=True)
score = models.FloatField()
class UserShow(models.Model):
user = models.ForeignKey(User)
show = models.ForeignKey(Shows)
Here is the view from which I access these models -
from django.http import HttpResponse
from django.template import Context
from django.template.loader import get_template
from django.http import HttpResponse, Http404
from django.contrib.auth.models import User
def user_page(request, username):
try:
user = User.objects.get(username=username)
except:
raise Http404('Requested user not found.')
shows = user.usershow_set.all()
template = get_template('user_page.html')
variables = Context({
'username': username,
'shows' : shows})
output = template.render(variables)
return HttpResponse(output)
At this point I get an error -
OperationalError: (1054, "Unknown column 'appname_usershow.show_id' in 'field list'")
As you see this column is not even present in my models? Why this error?
maybe your tables schema has been changed? Also, running syncdb does not update already created tables.
You might need to drop all the tables & then run syncdb again. Also remember to take backup of your data!!
As #inception said my tables schema had changed & running syncdb did not update already created tables.
Apparently any changes to the models when updated through syncdb does not change (as in update/modify) the actual tables. So I dropped the relevant DB & ran syncdb on empty DB. Now it works fine. :)
For others, SOUTH data migration tool for Django seems to be favorite option. It seems to provide options which django models & syncdb falls short on. Must check out...
Update 29th Sept 2019: From Django 1.7 upwards, migrations are built into the core of Django. If you are running a previous lower version of Django, you can find the repository on BitBucket.
Normally I get this when when I'm trying to access field which doesn't exist in Database.
Check if the field exist in the database. If you change model and perform syncdb it won't update the database, I'm not sure if that's the case.
On other note Django offers shortcut to replace try/except block in your code using get_object_or_404. (available in django.shortcuts )
try:
user = User.objects.get(username=username)
except:
raise Http404('Requested user not found.')
can be changed to:
user = get_object_or_404(User, username=username)
I have met the same problems:
First, run
manage.py sqlall [appname]
and you can find:
`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY,
and I add the column manual:
ALTER TABLE tb_realtime_data ADD id integer AUTO_INCREMENT NOT NULL PRIMARY KEY FIRST;
and then it worked.
I think django will add the column called id itself.
For convenience, each model is given an autoincrementing primary key field named id unless you explicitly specify primary_key=True on a field (see the section titled “AutoField” in Appendix A).
you can click here for details.
Good luck.
I faced the same error like you posted above with MySQL database back-end, after long time of resolving this error, I ended up with below solution.
Manually added column to database through workbench with name exactly the same as it is shown in your error message.
After that run below command
python manage.py makemigrations
Now migrations will be successfully created
After that run below command
python manage.py migrate --fake
Now every thing works fine without any data lose issue
In the appropriate field explicitly set
primary_key = True
This is quite an old question but you might find the below useful anyway:
Check the database, you're most likely missing the show_id in the appname_usershow table.
This could occur when you change or add a field but you forget running migration.
OR
When you're accessing a non managed table and you're trying to add a ForeignKey to your model.
Following the example above:
class UserShow(models.Model):
# some attributes ...
show = models.ForeignKey(Show, models.DO_NOTHING)
class Meta:
managed = False
db_table = 'appname_departments'
This is a late response but hopefully someone will find this useful.
I was working on Django REST APIs using DB models. When I added a new column to my existing model(said column already existed in the db table from the start), I received the error shown below:
"Unknown column ',column name>' in 'field list'"executed".
What I missed was migrating the model over to the database.
So I executed the following commands from python terminal:
py -3 manage.py makemigrations ---> It will not allow NULL values for the new column added to the model, even though the column is present in the database from the start. So you need to add a default value, mine was an Integerfield, so I updated my column definition in the model as IntegerField(default=0).
From here onwards it should be straightforward, if there are no other errors.
py -3 manage.py migrate
py -3 manage.py runserver
After executing these steps when I called my REST APIs they were working properly.
I created a model file for my app and then did several sqlall as I refined my tables. One of the changes I made was I set primary_key=True to one of my fields. At the end called for syncdb. Added a dummy value and and tried to access it with User.objects.all(), User being my model class. Although this worked fine, this error came up while printing the list returned by the all() call. It read DatabaseError: (1054, "Unknown column 'polls_user.id' in 'field list'")
Surprisingly, I tried and could get it resolved with another call to syncdb. I remember not having seen the id column in the table at any time throughout the exercise when I checked it through the mysql command line client.
I received this error while trying to use Django Rest Framework serializers. Make sure if you have a serializer that subclasses ModelSerializer, that you migrate any changes to the Models before writing your serializer classes (or just comment anything related to the serializer, migrate, then uncomment).
PS F:\WebApp> python manage.py makemigrations
You are trying to add a non-nullable field 'price' to destination 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 with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option: 2
PS F:\WebApp> python manage.py sqlmigrate travello 0001
BEGIN;
-- Create model Destination
CREATE TABLE travello_destination (id integer AUTO_INCREMENT NOT NULL PRIMARY KEY, name varchar(100) NOT NULL, img varchar(100) NOT NULL, desc longtext NOT NULL, offer bool NOT NULL);
COMMIT;
PS F:\WebApp> python manage.py makemigrations
Migrations for 'travello':
travello\migrations\0002_destination_price.py
- Add field price to destination
PS F:\WebApp> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, travello
Running migrations:
Applying travello.0002_destination_price... OK
I had this issue when using a composite Primary Key of several VarChar fields and trying to call a table_set.all() queryset.
Django wanted a table_name_id PK column for this queryset, there wasn't one so it threw out this error.
I fixed it by manually creating the table_name_id and setting it to an auto-incremented, integer PK column instead of the composite PK.
I then specified those VarChar composite columns as unique_together in the Model's meta section so they act like a PK.
The direct solution is delete files under folder ../Project/App/migrations, and the method to avoid this problem is create new databaes table column instead of chagne the existing one.

Categories

Resources