migration trouble with unique random default value in model django - python

I want to add a new field to my already existing model called color. I want it to be unique and also want it to be randomly selected by default. So what I do is:
color = models.CharField(max_length=15, default=random_color, unique=True)
My random_color looks like this
def random_color():
"""
Returns:
[string] : returns a rgb value in hex string
"""
while True:
generated_color = f'#{random_hex()}{random_hex()}{random_hex()}'
if not MyModel.objects.filter(color=generated_color):
return generated_color
I followed a similar logic to what has been provided here.
Now the problem with this approach is that there is no color to begin with to look for.
And I also want my migration to add in a bunch of default random color values to my already existing tables.
How do I fix this?

There might be a simpler way to accomplish this, but these steps should work:
Add the new color field with null=True and without the default and the unique=True.
Run makemigrations for your app.
Run makemigrations --empty to create a custom migration for your app. Add a RunPython operation using your random_color() to populate the new column.
Alter the color field to add the default, unique constraint, and remove the null=True.
Run makemigrations again.

Related

Unable to apply migration on altered model in django

I am new to django.
I have changed some fields in my already created Django model. But It says this message when I try to apply migrations on it:
It is impossible to add a non-nullable field 'name' to table_name without specifying a default. This is because 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 manually define a default value in models.py.
Although I have deleted the data of this table from database. I cannot set it's default value because the field has to store unique values. Do I need to delete my previous migration file related to that table?
I have applied data migrations, but still getting the same error when applying migrations again:
def add_name_and_teacher(apps, schema_editor):
Student = apps.get_model('app_name', 'Student')
Teacher = apps.get_model('app_name', 'Teacher')
for student in Student.objects.all():
student.name = 'name'
student.teacher = Teacher.objects.get(id=1)
student.save()
class Migration(migrations.Migration):
dependencies = [
('app', '0045_standup_standupupdate'),
]
operations = [
migrations.RunPython(add_name_and_teacher),
]
So, before you had a nullable field "name". This means that it's possible to have null set as that field's value.
If you add a not null constraint to that field (null=False), and you run the migrations, you will get an integrity error from the database because there are rows in that table that have null set as that field's value.
In case you just made two migrations where first you added a nullable field, but then remembered it mustn't be nullable and you added the not null constraint, you should simply revert your migrations and delete the previous migration. It's the cleanest solution.
You can revert by running python manage.py migrate <app name> <the migration that you want to keep>
Then you simply delete the new migrations and run python manage.py makemigrations again.
In case the migration with the nullable field was defined very early on and there is already data there and it's impossible to delete that migration, you will need to figure out how to populate that data. Since you say that there is also the unique constraint, you can't just provide a default because it will cause issues with that constraint.
My suggestion is to edit the migration file and add migrations.RunSQL where you write custom SQL code which will insert values to the field. Make sure you place the RunSQL operation before the operation that adds the not null constraint (it should be AlterField or AddConstraint) as they are run in order.
You could also use migrations.RunPython, but I prefer the RunSQL because future changes in the code might break your migrations which is a hassle to deal with.
Docs for RunSQL

Django - IntegrityError in terminal when migrating changes to a relationship field

I've got a Django model like the following..
class ExampleModel(models.Model):
name = models.CharField(...)
related_user = models.ForeignKey(UserTypeA, related_name='related_example', blank=True, null=True, on_delete=models.SET_NULL)
where I recently had to make a change to the related_user field by changing the ForeignKey from UserTypeA to UserTypeB.
Of course, this raises an error in the terminal when I attempt to python manage.py makemigration...
django.db.utils.IntegrityError: insert or update on table "models_examplemodel" violates foreign key constraint "models_examplemodel_related_user_id_ac0c6018_fk_accounts_"
DETAIL: Key (related_user_id)=(13) is not present in table "accounts_usertypea".
What's the safest way to go about making these changes? Currently I'm in development so I'm happy to delete my data/migrations/whatever, but I imagine in production this would be difficult.
The ideal behaviour I'd like to see here is the relations from ExampleModel and UserTypeA just being deleted, and so the current relationships would be set to NULL. Thoughts?
if you simply want to drop UserTypeA and use UserTypeB with None values simply do this:
remove related_user field
generate migrations
add related_user field
generate migrtions
If you want to do something more complecated (fill UserTypeB based on UserTypeA) these are the steps
add realted_user_b field with default as null
generate migration file
write a data migrations file which fills realted_user_b based on current data docs
remove realted_user field
generate migration file
rename realted_user_b to realted_user
generate migration file

How to create a migration after adding an hstore field? (django-hstore vs. South)

I have an existing model in the database. I want to enhance it with an hstore field. I installed hstore Postgres extension, django-hstore app, changed the appropriate settings in the django project:
SOUTH_DATABASE_ADAPTERS = {'default': 'south.db.postgresql_psycopg2'}
DATABASES = {
'default': {
'ENGINE': 'django_hstore.postgresql_psycopg2',
...
I checked that the django app works with new settings -- it does. So I added the new field to one of the models:
data = hstore.DictionaryField(db_index=True)
Next step: db migration. And here I'm lost. When trying to create a migration for the new field, I get this:
The field 'Project.data' does not have a default specified, yet is NOT NULL.
Since you are adding this field, you MUST specify a default
value to use for existing rows. Would you like to:
1. Quit now, and add a default to the field in models.py
2. Specify a one-off value to use for existing columns now
What do I do here? Did I miss something? I didn't find any references to a default value (or null=True) in any django-hstore related article.
this message usually appears when South is trying to update your models on the database and finds existing rows on the table yo are trying to modify. In order to continue and create the new field on database you must specify a value for the existing rows of tha table you are migrating. What I usually do, if it is a development stage, I go for option number 2 and set the value to 0, {} empty dict, or even NULL, depending on the field type.
As already mentionned, when you hit 2, you can either go for an empty string ("") or fill the field the way it's stored:
? The field 'MyModel.data' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> "foo=>bar,foo2=>bar2"

South migration default value from other column?

I have a model that has one column right now, description, that populates an area on a few different pages of the site. The client wants this split up into a couple different columns, so they can populate different values in certain parts of the site. So I have to change that description column to frontpage_description and resourcepage_description. I am trying to come up with a way to do this in South so that the value of the description column is the (initial) default value for both of the "new" columns. This is what I have so far:
# file: project/app/xxxx_mymigration.py
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
db.rename_column('myapp', 'description', 'frontpage_description')
db.add_column('myapp', 'resourcepage_description', self.gf('myfields.TextField')(default='CHANGEME'), keep_default=False)
def backwards(self, orm):
db.rename_column('myapp', 'frontpage_description', 'description')
db.delete_column('myapp', 'resourcepage_description')
models = {
# ...
The part I am wondering about is the self.gf(...)(default='CHANGEME'), I am wondering if there is a way to set the value of description or frontpage_description to be the default value for resourcepage_description?
I have looked into the orm parameter, which does allow you to access models during the migration, but all the examples I have come across involve using it to define relations during a migration, and not actually accessing individual records.
Am I just going to have to split this up into a schemamigration and a datamigration?
This is exactly a three-step: schema migration to add the columns, a data migration to set them, and another schema migration to delete the original description column. 90% of that is done for you from the ./manage command, so it's not as if this is tragically more work.

AppEngine: Query datastore for records with <missing> value

I created a new property for my db model in the Google App Engine Datastore.
Old:
class Logo(db.Model):
name = db.StringProperty()
image = db.BlobProperty()
New:
class Logo(db.Model):
name = db.StringProperty()
image = db.BlobProperty()
is_approved = db.BooleanProperty(default=False)
How to query for the Logo records, which to not have the 'is_approved' value set?
I tried
logos.filter("is_approved = ", None)
but it didn't work.
In the Data Viewer the new field values are displayed as .
According to the App Engine documentation on Queries and Indexes, there is a distinction between entities that have no value for a property, and those that have a null value for it; and "Entities Without a Filtered Property Are Never Returned by a Query." So it is not possible to write a query for these old records.
A useful article is Updating Your Model's Schema, which says that the only currently-supported way to find entities missing some property is to examine all of them. The article has example code showing how to cycle through a large set of entities and update them.
A practice which helps us is to assign a "version" field on every Kind. This version is set on every record initially to 1. If a need like this comes up (to populate a new or existing field in a large dataset), the version field allows iteration through all the records containing "version = 1". By iterating through, setting either a "null" or another initial value to the new field, bump the version to 2, store the record, allows populating the new or existing field with a default value.
The benefit to the "version" field is that the selection process can continue to select against that lower version number (initially set to 1) over as many sessions or as much time is needed until ALL records are updated with the new field default value.
Maybe this has changed, but I am able to filter records based on null fields.
When I try the GQL query SELECT * FROM Contact WHERE demo=NULL, it returns only records for which the demo field is missing.
According to the doc http://code.google.com/appengine/docs/python/datastore/gqlreference.html:
The right-hand side of a comparison can be one of the following (as
appropriate for the property's data type): [...] a Boolean literal, as TRUE or
FALSE; the NULL literal, which represents the null value (None in
Python).
I'm not sure that "null" is the same as "missing" though : in my case, these fields already existed in my model but were not populated on creation. Maybe Federico you could let us know if the NULL query works in your specific case?

Categories

Resources