django model AutoField error - django

I had a model like this
class Alrule(models.Model):
priority = models.PositiveIntegerField(unique=True)
rule = models.CharField(max_length=256)
I migrated this model. After I decided to make priority field as primary key with AutoField
Then my model looked like this:
class Alrule(models.Model):
priority = models.AutoField(primary_key=True)
rule = models.CharField(max_length=256)
I made a migration and migrated this change.
Now again i needed to revert back. Now I made following changes:
class Alrule(models.Model):
priority = models.PositiveIntegerField(unique=True)
rule = models.CharField(max_length=256)
I made a migration.
Now when I migrate I get the following ERROR:
AssertionError: A model can't have more than one AutoField.
My understanding is priority field was set to AutoField before and it is not removed. Please help
My first migration look like this:
operations = [
migrations.RemoveField(
model_name='alrule',
name='id',
),
migrations.AlterField(
model_name='alrule',
name='priority',
field=models.AutoField(serialize=False, primary_key=True),
),
]
Second migration looks like this:
operations = [
migrations.AddField(
model_name='alrule',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, default=2, serialize=False, verbose_name='ID'),
preserve_default=False,
),
migrations.AlterField(
model_name='alrule',
name='priority',
field=models.PositiveIntegerField(unique=True),
),
]

If you use mssql-django, there is some limitations:
The following features are currently not supported:
mssql-django does not support SQL-based regex commands
Altering a model field from or to AutoField at migration
https://github.com/microsoft/mssql-django#limitations

operations = [
migrations.AddField(
model_name='alrule',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, default=2, serialize=False, verbose_name='ID'),
preserve_default=False,
),
migrations.AlterField(
model_name='alrule',
name='priority',
field=models.PositiveIntegerField(unique=True),
),
]
Your table already has AutoField and with first operation you are adding another one.
To avoid that just swap your operations in your migration like this:
operations = [
migrations.AlterField(
model_name='alrule',
name='priority',
field=models.PositiveIntegerField(unique=True),
),
migrations.AddField(
model_name='alrule',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, default=2, serialize=False, verbose_name='ID'),
preserve_default=False,
),
]
I.e. first alter field (remove AutoField from table) and after that is done - add your id AutoField back.

That is because AutoField has not been removed just as you correctly indicated. Delete these two migration files and run makemigrations again. If it doesn't work, you might have to also delete the migration file before that and run migrations again.
As a last resort, you could just delete the migrations files apart from the init.py file. But then, I am sure it should work once you delete these two migrations and make a new migration from there.

Yes I got the simple hack of the problem. It is like this
Make the final change like this:
class Alrule(models.Model):
prioriti = models.PositiveIntegerField(unique=True)
rule = models.CharField(max_length=256)
Here priority field should be given new name ('prioriti') so that previos one gets deleted completely and a new field is created.
Now make and apply migrations.
Now again rename "prioriti" field back to "priority". And make and apply migration.
And we are done.

Related

Cannot correct a failed migration django

I inadvertently did this:
ordering = models.IntegerField(default="Order/position")
I ran makemigrations and got no error. When I ran migrate it blew up with the error:
ValueError: invalid literal for int() with base 10: 'Order/position'
what I had meant to do was this:
ordering = models.IntegerField(default=0, verbose_name="Order/Position")
I updated to the correct field definition and while makemigrations is happy and noted the change migrate still keeps throwing that same error.
How do I fix this?
In case this matters - I am running Django with Postgres and both are in Docker containers
Is it possible to "cancel" a previously failed migration?
Is it common for makemigration to not catch big errors like this?
The best is probably to fix the migration file itself. In the migration file, it will probably looked like:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
# …
]
operations = [
migrations.CreateModel(
name='MyModel',
fields=[
(
'id',
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID',
),
),
(
'ordering',
models.IntegerField(
default='Order/position',
),
),
],
)
]
You fix this by altering the definition for ordering to the IntegerField you described.

Django incomplete migration of table with multiple foreign keys

Django version: 4.1.2
After heaving the following table defined in the model:
class Tasks(models.Model):
name_text = models.CharField(max_length=200)
duration_int = models.IntegerField(default=1)
...
the next two tables have been defined:
class Metiers(models.Model):
name_text = models.CharField(max_length=50)
...
class TasksProperties(models.Model):
task = models.ForeignKey(Tasks, on_delete=models.CASCADE, related_name='task_relation')
metier = models.ForeignKey(Metiers, on_delete=models.CASCADE, related_name='metier_relation')
...
doing the migration, the metier is not created inside the SQL table, but the rest:
class Migration(migrations.Migration):
dependencies = [
('simo', '0009_alter_specialdays_day_date'),
]
operations = [
migrations.CreateModel(
name='Metiers',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name_text', models.CharField(max_length=50)),
],
),
migrations.CreateModel(
name='TasksProperties',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('workload_mh', models.IntegerField(default=8)),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='simo.tasks')),
],
),
]
Is there any reason, why metier is not taken into account?
Update on 27/11/2022:
So, it still not clear why it was not working because I believe the structure is correct; therefore I did some try-on-errors and finally the migration is complete, although the reason is not obvious. See below the resolution:
Step 1) Remove the migrated TaskProperties class from models and do a migration
Step 2) models.py was updated as
class TasksProperties(models.Model):
workload_mh = models.IntegerField(default=8)
metier = models.ForeignKey('Metiers', on_delete=models.CASCADE, related_name='metier_relation')
It resulted that the "metier" as foreignkey was visible in SQL after the migration.
Step 3) Adding the "task" as well, the migration dropped the following question:
class TasksProperties(models.Model):
workload_mh = models.IntegerField(default=8)
metier = models.ForeignKey('Metiers', on_delete=models.CASCADE, related_name='metier_relation')
task = models.ForeignKey(Tasks, on_delete=models.CASCADE, related_name='task_relation')
*It is impossible to add a non-nullable field 'task' to tasksproperties without specifying a default. This is because the database needs something to populate existing rows.
Please select a fix:
Provide a one-off default now (will be set on all existing rows with a null value for this column)
Quit and manually define a default value in models.py.
Select an option: 2*
Then after updating the model.py adding default value default=0 to "task" the migration was successful:
class TasksProperties(models.Model):
workload_mh = models.IntegerField(default=8)
metier = models.ForeignKey('Metiers', on_delete=models.CASCADE, related_name='metier_relation')
task = models.ForeignKey(Tasks, on_delete=models.CASCADE, related_name='task_relation', default=0)
Why is this "defualt" value is needed?
Try these three commands:
python manage.py makemigrations appname
python manage.py sqlmigrate appname 0001
python manage.py migrate
And see if it solves

How can I undo this (accidental) table inheritance in Django? [duplicate]

My current project uses multi-table inheritance models:
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
class Cinema(Place):
sells_tickets = models.BooleanField(default=False)
sells_popcorn = models.BooleanField(default=False)
I want to switch to abstract base classes instead. Since my model is already deployed I need to write some custom migrations to convert the above schema to this one:
from django.db import models
class AbstractPlace(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Meta:
abstract = True
class Restaurant(AbstractPlace):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
class Cinema(AbstractPlace):
sells_tickets = models.BooleanField(default=False)
sells_popcorn = models.BooleanField(default=False)
Does anyone have any advice on the steps to take to achieve this?
I recently tackled this exact problem, which I solved by writing and running the migration in the code block below - loosely translated to fit the models in your case.
I'm pretty sure that it's not possible to alter the tables of the old Restaurant and Cinema models directly, as if you try to add fields to them, they will collide with the existing fields of the base model, and if you try to "decouple" the derived models from the base model by e.g. by manually setting abstract=True in the base model's options, Django reports that it's unable to find the base models of Restaurant and Cinema. (These issues might be caused by a bug, for all I know.) To circumvent this problem, I created new tables for the derived models, copied the data from the old tables to the new ones, deleted the old tables, and renamed the new tables to match the names of the old ones.
I got large parts of the code below from code generated by Django, which can be reproduced by creating a temporary migration (before creating one with the code below) which only deletes Restaurant, Cinema and Place, running makemigrations, and copying the CreateModel()s and AlterField()s (for related fields pointing to Restaurant or Cinema) from the generated migration.
For the record, I'm using Django 3.1.4.
from django.db import migrations, models
def copy_objects_from_restaurant_and_cinema_to_restaurant_tmp_and_cinema_tmp(apps, schema_editor):
Restaurant_Tmp = apps.get_model('<app name>', 'Restaurant_Tmp')
Cinema_Tmp = apps.get_model('<app name>', 'Cinema_Tmp')
Restaurant = apps.get_model('<app name>', 'Restaurant')
Cinema = apps.get_model('<app name>', 'Cinema')
# The `_meta.fields` list includes the PK
copy_objects_from_old_model_to_new_model(Restaurant, Restaurant_Tmp, Restaurant_Tmp._meta.fields)
copy_objects_from_old_model_to_new_model(Cinema, Cinema_Tmp, Cinema_Tmp._meta.fields)
def copy_objects_from_old_model_to_new_model(old_model, new_model, fields_to_copy):
field_names = [field.name for field in fields_to_copy]
for old_obj in old_model.objects.all():
old_obj_field_dict = {
field_name: getattr(old_obj, field_name)
for field_name in field_names
}
new_model.objects.create(**old_obj_field_dict)
def copy_objects_from_restaurant_tmp_and_cinema_tmp_to_restaurant_and_cinema(apps, schema_editor):
Restaurant_Tmp = apps.get_model('<app name>', 'Restaurant_Tmp')
Cinema_Tmp = apps.get_model('<app name>', 'Cinema_Tmp')
Restaurant = apps.get_model('<app name>', 'Restaurant')
Cinema = apps.get_model('<app name>', 'Cinema')
copy_objects_from_old_model_to_new_model(Restaurant_Tmp, Restaurant, Restaurant_Tmp._meta.fields)
copy_objects_from_old_model_to_new_model(Cinema_Tmp, Cinema, Cinema_Tmp._meta.fields)
class Migration(migrations.Migration):
dependencies = [
('<app name>', '<last migration>'),
]
operations = [
migrations.CreateModel(
name='Restaurant_Tmp',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('address', models.CharField(max_length=80)),
('serves_hot_dogs', models.BooleanField(default=False)),
('serves_pizza', models.BooleanField(default=False)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Cinema_Tmp',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('address', models.CharField(max_length=80)),
('sells_tickets', models.BooleanField(default=False)),
('sells_popcorn', models.BooleanField(default=False)),
],
options={
'abstract': False,
},
),
migrations.RunPython(copy_objects_from_restaurant_and_cinema_to_restaurant_tmp_and_cinema_tmp, migrations.RunPython.noop),
# Update foreign keys to reference the non-abstract models directly,
# instead of through the (automatically generated) `place_ptr` field of the old models
<
Run `migrations.AlterField()` here for each related field (like ForeignKey) of other models that point to Restaurant or Cinema,
but change their `to` argument from e.g. `<app name>.restaurant` to `<app name>.restaurant_tmp`
>
migrations.RunPython(migrations.RunPython.noop, copy_objects_from_restaurant_tmp_and_cinema_tmp_to_restaurant_and_cinema),
migrations.DeleteModel(
name='Restaurant',
),
migrations.DeleteModel(
name='Cinema',
),
migrations.DeleteModel(
name='Place',
),
migrations.RenameModel(
old_name='Restaurant_Tmp',
new_name='Restaurant',
),
migrations.RenameModel(
old_name='Cinema_Tmp',
new_name='Cinema',
),
]
Note that the migration I originally wrote was only tested to work using SQLite; other database management systems might not accept such a large variety of migration operations, and you might have to split it into multiple migrations. (I'm somewhat unsure what exactly could cause this problem, but I can recall that I've experienced it with PostgreSQL.)
Please let me know if this solves your problem! 😊

could not create unique index "credits_credit_pkey"

Initially, I had a model of Credit. But after adding models of Hypothec and AutoCredit with similar functionality, I realized that I needed to make a base model and inherit from it.
When I tried makemigrations, I received a question:
You are trying to add a non-nullable field 'abstractbaseproduct_ptr' to `credit` 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
*I entered 1; 1. Then I got this question:
It is not clear what the 'abstractbaseproduct_ptr' field is?
Then i got
You are trying to add a non-nullable field abstractbaseaction_ptr to creditaction without a default; we can't do that (the database needs something to populate existing rows).
Again introduced 1; 1.
When I try to migrate, I get
django.db.utils.IntegrityError: could not create unique index "credits_credit_pkey"
DETAIL: Key (abstractbaseproduct_ptr_id) = (1) is duplicated.
Such questions arose only with the Credit model. Apparently, because there is already data in this table...
How should I fix this?
class AbstractBaseProduct(models.Model):
bank = models.ForeignKey('banks.Bank', verbose_name=_('bank'))
#other fields
class AbstractBaseAction(models.Model):
name = models.CharField(_('name'), max_length=255)
short_description = models.CharField(_('short description'), max_length=255)
full_description = models.TextField(_('full description'), blank=True, null=True)
class Credit(AbstractBaseProduct):
class Meta:
verbose_name = _('credit')
verbose_name_plural = _('Credits')
class CreditAction(AbstractBaseAction):
credit = models.ForeignKey(Credit, verbose_name=_('credit'))
migration
migrations.AddField(
model_name='credit',
name='abstractbaseproduct_ptr',
field=models.OneToOneField(auto_created=True, default=1, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='credits.AbstractBaseProduct'),
preserve_default=False,
),
migrations.AddField(
model_name='creditaction',
name='abstractbaseaction_ptr',
field=models.OneToOneField(auto_created=True, default=1, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='credits.AbstractBaseAction'),
preserve_default=False,
),
You have named your model AbstractBaseProduct, but you haven't added abstract = True to the model's Meta class, so Django thinks you want multi-table inheritance instead of abstract base classes.
See the docs on model inheritance for more info.

Django: Default value in Database, not just ORM

I would like to have a default value for a column in the database, not just the django orm.
Related ticket is in state "wontfix": https://code.djangoproject.com/ticket/470
What is the preferred way to create a default value in the relational database?
In my case it is a BooleanField which should default to FALSE.
I use PostgreSQL, but AFAIK this should not matter in this context.
I solved it like this. The file was created with manage.py makemigrations. I added only the line at the bottom.
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='State',
fields=[
('id', models.CharField(max_length=256, serialize=False, primary_key=True)),
('state', models.BooleanField(default=True)),
],
bases=(models.Model,),
),
### The following line was added
migrations.RunSQL('alter table box_state alter column state set default false')
]
You can do like this:
class MYModel(models.Model):
is_teacher = models.BooleanField(default=False)