Django 1.7: Makemigration: non-nullable field - python

I am trying to use django-orderedmodel (https://github.com/kirelagin/django-orderedmodel) in my project.
Running makemigrations doesn't work:
You are trying to add a non-nullable field 'order' to slide 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
Select an option:
I would like to know where I'm doing this wrong. Thanks

As the order field is unique, you'll need to add the field in several migration steps, replacing the original operations in your migration:
Add a nullable field, set the default to NULL.
Set the field to a unique value in each row.
Add a NOT NULL constraint.
I.e. something like this:
operations = [
migrations.AddField('myapp.MyModel', 'order', models.PositiveIntegerField(null=True, unique=True)),
migrations.RunPython(set_order),
migrations.AlterField('myapp.MyModel', 'order', models.PositiveIntegerField(blank=True, unique=True)),
]
where set_order is a function that sets the order to a valid value, e.g.:
def set_order(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
for i, model in enumerate(MyModel.objects.all()):
model.order = i
model.save()
It's easiest to provide a default value (i.e. 0), and then replace the operations in the generated migration.

Related

Providing a static default value for a new field in existing objects in model? [duplicate]

I added a new, non-nullable field to my Django model and am trying to use migrations to deploy that change. How would I set default value to use for existing models to be some function of those models rather than a constant?
As an example let's say I previously had a created_on field and I just added an updated_on field whose value I want to set initially to the model's created_on. How would I do this in a migration?
This is what I am trying to start with:
migrations.AddField(
model_name='series',
name='updated_as',
field=models.DateTimeField(default=????, auto_now=True),
preserve_default=False,
),
I just learned how to do this with a single migration!
When running makemigrations django should ask you to set a one-off default. Define whatever you can here to keep it happy, and you'll end up with the migration AddField you mentioned.
migrations.AddField(
model_name='series',
name='updated_as',
field=models.DateTimeField(default=????, auto_now=True),
preserve_default=False,
),
Change this one operation into 3 operations:
Initially make the field nullable, so the column will be added.
Call a function to populate the field as needed.
Alter the field (with AlterField) to make it not nullable (like the above, with no default).
So you end up with something like:
migrations.AddField(
model_name='series',
name='updated_as',
field=models.DateTimeField(null=True, auto_now=True),
),
migrations.RunPython(set_my_defaults, reverse_func),
migrations.AlterField(
model_name='series',
name='updated_as',
field=models.DateTimeField(auto_now=True),
),
with your functions defined as something like:
def set_my_defaults(apps, schema_editor):
Series = apps.get_model('myapp', 'Series')
for series in Series.objects.all().iterator():
series.updated_as = datetime.now() + timedelta(days=series.some_other_field)
series.save()
def reverse_func(apps, schema_editor):
pass # code for reverting migration, if any
Except, you know, not terrible.
Note: Consider using F expressions and/or database functions to increase migration performance for large databases.
You need to do it in two migrations. First of all, add your field, but make nullable. Create a migration file as usual. After that set your field to not-nullable and run makemigrations again, but don't lauch migrate yet. Open the second migration and define a function at the top:
def set_field_values(apps, schema_editor):
# use apps.get_model("app_name", "model_name") and set the defualt values
then, in your migration file there is a list of operations. Before the alter field operation add
RunPython(set_field_values)
and it should do it
You should also define a reverse for your function set_my_defaults(), in case you what to revert the migration in the future.
def reverse_set_default(apps, schema_editor):
pass
The reverse function in this case need to do nothing, since you are removing the field.
And add it to RunPython:
migrations.RunPython(set_my_defaults, reverse_set_default),
run makemigrations and set a default value based on field type.
migrations.AddField(
model_name='series',
name='updated_as',
field=models.DateTimeField(null=True, auto_now=True),
),
migrations.RunSQL(
(
"UPDATE myapp_series x"
"SET x.updated_as = x.created_on"
),
migrations.RunSQL.noop
),
migrations.AlterField(
model_name='series',
name='updated_as',
field=models.DateTimeField(auto_now=True),
),

Django. Create composite primary key

I want create composite promary key like {id, project_id}. I remove old tables(all). when i do:
python manage.py makemigrations
I have a mistake:
AssertionError: Model mdm.Group can't have more than one AutoField.
change my model:
id = models.AutoField(db_index=True, primary_key=False)
and add composite primary key as
constraints = [
models.UniqueConstraint(
fields=['id', 'project_id'], name='unique_group_project'
)
]
From docs:
By default, Django gives each model the following field:
id = models.AutoField(primary_key=True)
This is an auto-incrementing primary key.
If you’d like to specify a custom primary key, just specify primary_key=True on one of your fields. If Django sees you’ve explicitly set Field.primary_key, it won’t add the automatic id column.
Each model requires exactly one field to have primary_key=True (either explicitly declared or automatically added).
I just don't understand the problem. If I add AutoField, It's necessarily must PK. How I can resolve problem with Autofield id and composite PK (id, project_id)?

Django: non-nullable field without a default

In Django I use manage.py makemigrations and manage.py migrate on the following models.py file:
class Family(models.Model):
comment1 = models.CharField(max_length=80)
#comment2 = models.CharField(max_length=80)
After this successful initialization, I changed models.py to (I just uncomment the new model field which is basically a copy of the other model field):
class Family(models.Model):
comment1 = models.CharField(max_length=80)
comment2 = models.CharField(max_length=80)
Now when I try to makemigrations again I get the following error:
You are trying to add a non-nullable field 'comment' to family 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:
Why didn't I get this error upon intialization in the first place?
Others are right. you should set a default value for that field.
but there is a a trick that you can solve this. but it is not a good way... only if you have no choice.
1. comment all of your table
2. run makemigrations and migrate
3. uncomment your table
4.2. run makemigrations and migrate again
image1=models.ImageField(upload_to='app/image12',help_text="hi", null=True)
That is, set null= True'in the field.
It happens when you change your model after stored database.
If you want to make it non nullable without default.
you have to provide value for the same field when you create migration file. thats why it asks for the option. type "1" in terminal while python manage.py makemigrations and then provide a value for the field for previously saved row(if any).
Let me know if this helps.
I have faced a same issue, in my case:
Focus on the alert:
...non-nullable field 'comment' to family...
model: Family
field: comment
Just add one more attr in the field comment of the model family:
default=''
You can add this: default=None or default="something"
or add null=True, blank=True.

django postgres conditional constrain

I'm trying to set a conditional unique constrain to one of my model field (column on db perspective) with respect to another boolean field but can't find a way to do it.
I'm creating a web management tool, to aid/control custumers atendance, based on numbered cards while they are still on the store.
My main objective is to prevent my user to save an card number while this number is still active (aka boolean field) with another custumer.
Something like this on models.py:
class Cards(models.Model):
card_number = models.PositiveIntegerField("""{{RESTRICTION NEEDED}}""")
card_date = models.DateTimeField(auto_now=False, auto_now_add=True)
custumer = models.ForeignKey(Customer)
status = models.BooleanField(default=False)
maybe something like this would work directly on postgres, didn't tried yet
CREATE UNIQUE INDEX card_number_ind ON Cards (card_number) WHERE (status is False);
Any chance to do it directly with Django? I mean set a conditional unique constrain with respect to a boolean field (status) on Django?
TIA
Obs.This is my first question ever on stackoverflow, feel free to criticize on style.
Django doesn't support defining conditional constraints in models.py, however you can create a migration to add such a constraint.
Start by creating an empty migration
./manage.py makemigrations appname --empty
This will create an empty migration file. Next, you'll want to add your custom constraint to the migration
class Migration(migrations.Migration):
...
operations = [
migrations.RunSQL('create unique index card_number_ind on cards (card_number) where (status is false)'),
]
Starting in Django 2.2 there is now a UniqueConstraint declaration available that has a condition option.
condition
UniqueConstraint.condition
A Q object that specifies the condition you want the constraint to enforce.
For example:
UniqueConstraint(fields=['user'], condition=Q(status='DRAFT'), name='unique_draft_user')
ensures that each user only has one draft.

how to update model in django by adding new primary key field?

How to replace default primary key in Django model with custom primary key field?
I have a model with no primary key defined at first since django automatically adds an id field by default as primary field.
#models.py
from django.db import models
class Event(models.Model):
title = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=150)
I added some objects into it from django shell.
>>e = Event('meeting', 'Contents about meeting')
>>e.save()
>>e = Event('party', 'Contents about party')
>>e.save()
Then I require to add custom character field as primary into this model.
class Event(models.Model):
event-id = models.CharField(max_length=50, primary_key=True)
...
Running makemigrations:
$ python manage.py makemigrations
You are trying to add a non-nullable field 'event-id' to event 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
Select an option: 1
Please enter the default value now, as valid Python
The datetime and `django.utils.timezone modules` are available, so you can do e.g. timezone.now()
>>> 'meetings'
Migrations for 'blog':
0002_auto_20141201_0301.py:
- Remove field id from event
- Add field event-id to event
But while running migrate it threw an error:
.virtualenvs/env/local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 485, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: blog_event__new.event-id
In my experience (using Django 1.8.* here), I've seen similar situations when trying to update the PK field for models that already exist, have a Foreign Key relationship to another model, and have associated data in the back-end table.
You didn't specify if this model is being used in a FK relation, but it seems this is the case.
In this case, the error message you're getting is because the data that already exists needs to be made consistent with the changes you're requesting --i.e. a new field will be the PK. This implies that the current PK must be dropped for django to 'replace' them. (Django only supports a single PK field per model, as per docs[1].)
Providing a default value that matches currently existing data in the related table should work.
For example:
class Organization(models.Model):
# assume former PK field no longer here; name is the new PK
name = models.CharField(primary_key=True)
class Product(models.Model):
name = models.CharField(primary_key=True)
organization = models.ForeignKey(Organization)
If you're updating the Organization model and products already exist, then existing product rows must be updated to refer to a valid Organization PK value. During the migration, you'd want to choose one of the existing Organization PKs (e.g. 'R&D') to update the existing products.
[1] https://docs.djangoproject.com/en/1.8/topics/db/models/#automatic-primary-key-fields
Django has already established an auto incrementing integer id as primary key in your backend as and when u made the previous model.
When u were trying to run the new model , An attempt was made to recreate a new primary key column that failed.
Another reason is,When u made the field,Django was expecting a unique value be explicitly defined for each new row which it couldn't found,hence the reason.
As told in previous answer you can re-create the migration and then try doing it again.It should work.. cheers :-)
The problem is that you made the field unique, then attempted to use the same value for all the rows in the table. I'm not sure if there's a way to programmatically provide the key, but you could do the following:
Delete the migration
Remove the primary_key attribute from the field
Make a new migration
Apply it
Fill in the value for all your rows
Add the primary_key attribute to the field
Make a new migration
Apply it
It's bruteforce-ish, but should work well enough.
Best of luck!

Categories