Django datetime default value in migrations - python

I've read some questions about this issue in so, also this question is not giving the correct answer for my case:
I'm adding a created_time field in my already existing models, so no date in the mysql table belonging to the model.
class Configs(models.Model):
...
creation_date = models.DateTimeField(auto_now_add=True, blank=True)
...
I apply the migration using python manage.py makemigrations
And I get this error:
You are trying to add a non-nullable field 'creation_date' to
collections without a default; we can't do that (the database needs
something to populate existing rows). Please select a fix:
I tryed many options:
creation_date = models.DateTimeField(auto_now_add=True)
This one is giving the same error.
How can this migration be achieved, if USE_TZ in settings is set to False?
Btw, is this a bug in Django 1.9.4 makemigrations script?

auto_now_add set the current datetime when the instance is created, which never happens during your migration so you are trying to leave NULL a non-nullable field.
The solution would be to add a default date to your model, makemigrations, then remove the default parameter from the model, makemigrations again and finally migrate. You can achieve the same adding null=True to the field, adding a RunPython or RunSQL to your migration that populates the field, and then remove the null=true from the field.
At the end you can merge both migration files (or simple write it yourself) to end with something like:
operations = [
migrations.AddField(
model_name='configs',
name='creation_date',
field=models.DateTimeField(auto_now_add=True, null=True, blank=True),
),
migrations.RunPython(populate_dates),
migrations.AlterField(
model_name='configs',
name='creation_date',
field=models.DateTimeField(auto_now_add=True, blank=True),
),
]
I just wrote it so I hope it does not have many typos

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),
),

How to remove a non-nullable field in Django?

I've got a category field in my model which I want to remove. However it did not have null=True so (from previous experience) deleting it will cause a django.db.utils.IntegrityError: null value in column “category” violates not-null constraint error.
Any way around this?
Here's is the field:
class Post(models.Model):
...
category = models.CharField(max_length=20, choices=CATEGORY_CHOICES, default='1')
You must be run the manage.py makemigrations
And manage.py migrate to apply the changes
By default when you create a new django field and if you don't provide the argument null explicitly, it will be treated as False, so if you don't specified null argument at the creation it will be by default null = False. Just delete the field,save the file and make the python.manage.py makemigrations and then python.manage.py migrate to reflect the changes you have made to your model in the DB

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.

Is there a way to take an id inside a model and use that to create a relation in django?

I realized that I should have created a relation in my model, but instead I stored the id.
Is there a way that I can take the id and create a relation with my model object?
For example I have a model defined like this currently:
class Service(models.Model):
user_id= models.IntegerField()
service_name = models.CharField(max_length=100)
...
I have 1000+ objects stored in the database with objects like this. Now I want to define the model such that the prexisting model objects get updated with the proper relation according to the id. My model object would look like this and the prexisting objects would have the proper relation :
from django.contrib.auth.models import User
class Service(models.Model):
user= models.ForeignKey(User)
service_name = models.CharField(max_length=100)
There is. In Django 1.7+, first create a migration with manage.py makemigration <app_name>, but don't run it just yet. In your migration you'll find these operations:
operations = [
migrations.RemoveField(
model_name='service',
name='user_id',
),
migrations.AddField(
model_name='service',
name='user',
field=models.ForeignKey(to='auth.User'),
),
]
This will remove the column and wipe all data, but you don't want that. Instead, you should rename the field and use AlterField to add a foreign key constraint:
operations = [
migrations.RenameField(
model_name='service',
old_name='user_id',
new_name='user',
),
migrations.AlterField(
model_name='service',
name='user',
field=models.ForeignKey(to='auth.User'),
),
]
Now I can only test this on SQLite at the moment, which doesn't force foreign key constraints. On Mysql/PostGRESQL this will probably fail if you have a user_id that's not the id of a User. If that's the case, you'll need to apply a data migration first that handles any inconsistencies.
which version of django? 1.7+ has migrations built in. You just change your model and do the migration https://docs.djangoproject.com/en/1.7/topics/migrations/
If you are using an earlier version checkout south. It does roughly the same thing.
You can also just change your model, then change the table definition directly with sql

'NOT NULL constraint failed' after adding to models.py

I'm using userena and after adding the following line to my models.py
zipcode = models.IntegerField(_('zipcode'),
max_length=5)
I get the following error after I hit the submit button on th signup form:
IntegrityError at /accounts/signup/
NOT NULL constraint failed: accounts_myprofile.zipcode
My question is what does this error mean, and is this related to Userena?
You must create a migration, where you will specify default value for a new field, since you don't want it to be null. If null is not required, simply add null=True and create and run migration.
coldmind's answer is correct but lacks details.
The NOT NULL constraint failed occurs when something tries to set None to the zipcode property, while it has not been explicitly allowed.
It usually happens when:
Your field has Null=False by default, so that the value in the database cannot be None (i.e. undefined) when the object is created and saved in the database (this happens after a objects_set.create() call or setting the .zipcode property and doing a .save() call).
For instance, if somewhere in your code an assignment results in:
model.zipcode = None
This error is raised.
When creating or updating the database, Django is constrained to find a default value to fill the field, because Null=False by default. It does not find any because you haven't defined any. So this error can not only happen during code execution but also when creating the database?
Note that the same error would be returned if you define default=None, or if your default value with an incorrect type, for instance default='00000' instead of 00000 for your field (maybe can there be an automatic conversion between char and integers, but I would advise against relying on it. Besides, explicit is better than implicit). Most likely an error would also be raised if the default value violates the max_length property, e.g. 123456
So you'll have to define the field by one of the following:
models.IntegerField(_('zipcode'), max_length=5, Null=True,
blank=True)
models.IntegerField(_('zipcode'), max_length=5, Null=False,
blank=True, default=00000)
models.IntegerField(_('zipcode'), max_length=5, blank=True,
default=00000)
and then make a migration (python3 manage.py makemigration <app_name>) and then migrate (python3 manage.py migrate).
For safety you can also delete the last failed migration files in <app_name>/migrations/, there are usually named after this pattern:
<NUMBER>_auto_<DATE>_<HOUR>.py
Finally, if you don't set Null=True, make sure that mode.zipcode = None is never done anywhere.
If the zipcode field is not a required field then add null=True and blank=True, then run makemigrations and migrate command to successfully reflect the changes in the database.
In Django Null=True means Null Values are accepted. But Some Filed having Django that Blank=True are not satisfied for put Blank fields.
DateTimeField
ForeignKey
These two fields are used and if you want to put Blank I recommend adding NULL=TRUE
Since you added a new property to the model, you must first delete the database. Then manage.py migrations then manage.py migrate.

Categories