Cannot alter tables after Django migration in posgres DB - python

I really need to know why in the world we cannot change the column types after we do the migrations to Postgres DB.
I already created my models by python manage.py makemigrations then do migrate. Everything looks fine and tables are created on the postgres DB.
class test_API(models.Model):
IDnumber = models.IntegerField(null=False, blank=False)
State = models.CharField(max_length = 256, null = True)
Exlcludmethod = models.CharField(max_length=256, null=True)
class test_API_2(models.Model):
Idnumber = models.Foreignkey(test_API, max_length = 256, blank=False, null = False)
value = models.CharField(max_length=128, default="")
last_updated = models.DateTimeField(auto_now=True)
Lets say we want to make change to IDnumber column from Integerfield to Charfield.
class test_API(models.Model):
IDnumber = models.CharField(null=False, blank=False)
State = models.CharField(max_length = 256, null = True)
Exlcludmethod = models.CharField(max_length=256, null=True)
and run the python manage.py makemigrations again
return self.cursor.execute(sql)
psycopg2.errors.DuplicateTable: relation "test_API" already exists
How can we modify/change column types and do migrations. What is the proper way of doing this ?
Using
Django:Django==4.0.3
Python = 3.9.1
Posgresql = 2.9.3
Django migrations using RunPython to commit changes
django 1.7 migrate gets error "table already exists"

It seems like you want to drop the link to test_API in test_API_2. If you want to be able store 'abcd' type strings, and not try to keep anything from the relation previously created by the ForeignKey field, then I believe you need to do this in a two step process.
First step would be to remove (or comment) the IDNnumber field, make migrations, then add it back with a new type of field.
The IDNumber field was more than an integer field, it has an indexed relation to test_API, so you need to sever that first by removing the field, then create a new field with identical name.

Related

Django creates a migration that seems already reflected in original postgresql schema

I've modified the foreign key calendar as nullable in my Django model CalendarAssign. \
# ---------------------------------------------------------------------------- #
class Calendars(models.Model):
id = models.CharField(primary_key=True, max_length=100)
cms_id = models.CharField(max_length=100)
default_program = models.ForeignKey(ControlPrograms, models.CASCADE, blank=True, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
managed = True
db_table = 'calendars'
# ---------------------------------------------------------------------------- #
class CalendarAssign(models.Model):
device_mac = models.ForeignKey(Device, models.CASCADE)
calendar = models.ForeignKey(Calendars, models.CASCADE, null=True)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
managed = True
db_table = 'calendar_assign'
When applying the migration generated by Django it gives me an error.
operations = [
migrations.AlterField(
model_name='calendarassign',
name='calendar',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='smartbridge.Calendars'),
)
Generated sql code uses unsupported feature 'WITH ORDINALITY'.
It's because Django doesn't support the Postrges version we are using.
WITH ORDINALITY appears in psql 9.4 but we use version 9.1.
Both Postgres and Django cannot be upgraded right now. So I need to write the migration manually (without 'WITH ORDINALITY' feature).
migrations.RunSQL("DO $$DECLARE r record;\
BEGIN\
FOR r IN SELECT table_name,constraint_name \
FROM information_schema.constraint_table_usage \
WHERE table_name IN ('calendars') AND constraint_name like '%calendar_assign_calendar_id%'\
LOOP\
EXECUTE 'ALTER TABLE calendar_assign DROP CONSTRAINT '|| quote_ident(r.constraint_name) || ';';\
END LOOP;\
ALTER TABLE calendar_assign ALTER COLUMN calendar_id DROP NOT NULL; \
ALTER TABLE calendar_assign \
ADD CONSTRAINT calendar_assign_calendar_id_fk_calendars_id FOREIGN KEY (calendar_id) REFERENCES calendars(id);\
END$$;")
Migration seems to work fine.
calendar is now nullable but Django still detect some difference.
If a ask Django to generate the migration corresponding to the difference it generates the same as before my manual migration.
I would like Django to see no difference after my migration.
Thanks
I think you will have to set managed = False for the time being, otherwise the makemigrations command will each time think it has not been made nullable yet.
The migration construction command looks to the previous migration files, and thus constructs a conceptual model how a table will look like in the database if all the previous migrations took place. Based on that model it will look for differences with your Django model that you constructed, and thus create a migration file for that.
As long as you thus do not migrate with the AlterField command, Django will think you did not make the field nullable. It can not parse SQL so even if you made it nullable over there, it will still assume that that the field is non-NULLable.
By setting it to managed=False [Django-doc], Django will no longer manage the migrations of that file. You can create an empty migration [Django-doc] with:
python3 manage.py makemigrations --empty
and use this to define SQL queries to perform on the table.

Django migration from dynamic fields

I've the following Django model:
class Apple(models.Model):
text = models.TextField()
I've already many records, and I'd like to add a subject field to the model, so it'll look like:
class Apple(models.Model):
text = models.TextField()
subject = models.CharField(max_length = 128)
. In this case I run a makemigrations, but since subject can be empty, I need to set a default value either in the model, or in the migration file.
What would be the correct procedure if I'd like to take the subject from the text for the already existing database lines (for instance: text[:64])?
My solution would be to create a migration with a default value, run a management command to update the values, and with a new migration remove the default value for the subject. Is there a better solution? What is it? Can I somehow combine / do this in the migration itself?
Python: 3.4.5
Django: 1.9.2
For some databases including postgresql, it can be quicker to add a nullable field, therefore I would change your approach to:
schema migration creates the field with null=True (no need to set a default)
data migration populates the field
schema migration removes null=True from field
You can combine the three operations in one migration file. However the Django docs for data migrations recommend that you keep them separate.
You can do it in migration itself, create a migration file with blank=True, null=True in subject field.
class Apple(models.Model):
text = models.TextField()
subject = models.CharField(max_length=128, blank=True, null=True)
Then create another empty migration file.
python manage.py makemigrations --empty yourappname
Paste below code in that file.
from django.db import migrations
def set_subject(apps, schema_editor):
Apple = apps.get_model('yourappname', 'Apple')
for a in Apple.objects.all():
a.subject = a.text
a.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', 'name of above migration file'),
]
operations = [
migrations.RunPython(set_subject),
]

Django AbstractEmailUser model column does not exist

I created a CustomUser model, inheriting from AbstractEmailUser.
I wanted to add an avatar field, after finishing it and making migrations but I'm getting the following error:
column account_customuser.avatar does not exist
LINE 1: ...user"."name", "account_customuser"."valid_email", "account_c...
models.py looks like this now
class CustomUser(AbstractEmailUser):
nickname = models.CharField('nickname', max_length=100, unique=True)
name = models.CharField(max_length=200, blank=True, null=True, default=None)
valid_email = models.BooleanField('valid email', default=False, blank=True)
avatar = models.ImageField(upload_to='profile/photo', blank=True, null=True, default=None)
What can I do to correctly add the avatar field?
As stated here: Django Programming error column does not exist even after running migrations
Something may have gone wrong in your migration process.
Go to your database and find a table named django_migrations where
all the migrations are listed.
Find the row with the migration in which you added the avatar column to your model and delete it from the database (only the row).
Migrate again: ./manage.py migrate
Another possibility is that you are using Django Toolbar like what happened here: Django Migration Error: Column does not exist, in which case you need to comment the toolbar in your installed apps and rerun migrations.
Did you apply a new migration with these changes?
You can check this using showmigrations or use makemigrations to create a migration and migrate to apply it.

Django migrations

I am trying to build a blog on django. I have gone as far as creating models. Here they are:
from django.db import models
import uuid
class Users(models.Model):
username = models.CharField(max_length = 32, primary_key = True)
password = models.CharField(max_length = 32)
email = models.EmailField()
registration_date = models.DateTimeField(auto_now_add = True)
class Posts(models.Model):
author = models.ForeignKey("Users")
header = models.CharField(max_length=100)
body = models.TextField()
pub_date = models.DateTimeField(auto_now_add = True)
mod_date = models.DateTimeField(auto_now = True)
upvotes = models.PositiveIntegerField()
views = models.PositiveIntegerField()
post_id = models.AutoField(primary_key = True)
class Answers(models.Model):
body = models.TextField()
author = models.ForeignKey("Users")
pub_date = models.DateTimeField(auto_now_add = True)
mod_date = models.DateTimeField(auto_now = True)
post = models.ForeignKey("Posts")
answer_id = models.UUIDField(primary_key = True, default=uuid.uuid4)
After running python manage.py migrate I get the following:
You are trying to add a non-nullable field 'post_id' to posts without
a default; we can't do that (the database need mething 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
Even if I press 1 and try to set a random one-off value, it migrates successfully but later on the website crashes with "no such table: blog_posts". But I think it should work without such workarounds as setting the default value manually anyway.
I tried playing with primary keys for Posts and Answers. I tried completely removing them so that django automatically sets them itself and tried changing it from AutoField to UUIDField and vice versa but it didn't help. What am I doing wrong?
You're seeing this error message because django is trying to build a consistent history of migrations, and it complains that if there was a database that held data with your old migrations, and you'd try to add a non-nullable field, it wouldn't know what to do.
Migrations are supposed to be put into version control and used across different development/production environments. If you add a field that must not be null, existing data of other environments (for example a production database that held models that did not have the field post_id) then django will warn you about this, with the error message that you got, and offer two solutions:
This field should always be prepoulated with a default value( you have to modify the models.py)
This is a one time migration and you supply a one-off value, for example "LEGACY" to mark pre-migration data.
If you're not in production and there is no valuable data on your development server, an easy way to fix this error message is just to delete the existing migration files and run python manage.py makemigrations && python manage.py migrate again.

Django is ignoring a field item for a Model with a fresh DB

I've completely wiped all my database tables in order to add a new field to a model since wiping SQL tables clean is the fastest way to do so without going the 'south' route when the SQL data only contains dummy data for testing purposes.
So here's my Model:
class Student(models.Model):
uid = models.ForeignKey(User)
title = models.CharField(max_length=250, help_text='The student name.')
resume = models.FileField(upload_to=get_upload_resume_name)
location = models.ForeignKey(Location)
country = models.ForeignKey(Country)
prim_interest = models.CharField(max_length=250, help_text='Maximum 250 characters.')
sec_interest = models.CharField(max_length=250, help_text='Maximum 250 characters.')
cellphone = models.IntegerField(default=0)
email_verified = models.BooleanField(default=False)
thumbnail = models.FileField(upload_to=get_upload_file_name)
def __unicode__(self):
return self.title
and the last field that I've added called 'thumbnail' is not being created by Django when I call syncdb right after deleting all my tables.
My method of completely wiping the tables has always worked no matter what drastic changes are applied to models.py and suddenly this is not the case for this instance. I could show you more code snippets but I have no clue where else in the Django project has a direct effect on generating models.
What could be causing syncdb to be refusing to write this new field 'thumbnail' to the Student table in the DB?
Could be That south is interfering. You habe no migrations waiting. If I remember well syncdb dos not create tables That south is scheduled to create.
Why not make a south migrate after your syncdb?

Categories

Resources