Migration rename model field ManyToMany Django 1.8 - python

I have a very similar situation like this: Django migration strategy for renaming a model and relationship fields
I need to rename Foo to Bar.
We have an identical myapp:
class Foo(models.Model):
name = models.CharField(unique=True, max_length=32)
description = models.TextField(null=True, blank=True)
But I have in my myotherapp a ManyToMany field:
class AnotherModel(models.Model):
foo = models.ForeignKey(Foo)
is_awesome = models.BooleanField()
class YetAnotherModel(models.Model):
foo = models.ManyToManyField(Foo, blank=True, null=True) # Here!
is_ridonkulous = models.BooleanField()
I tried rename:
foo = models.ManyToManyField(Foo, blank=True, null=True)
to IntegerField() but doesn't work. How can I do that?

This is how I did it. Note: I was NOT in production, so I did not have to worry about information already in the tables. If you currently have data that you need to keep in the linking table, back up your data first. Also, I was using Django 1.9, but I think that everything referenced here is in 1.8 too.
The issue with the many-to-many relationship is the intermediate tables. Using RemoveField and AddField handled that.
The myapp migration for the model rename probably looks something like this:
class Migration(migrations.Migration):
dependencies = [
('Foo', '0001_initial.py'),
]
operations = [
migrations.RenameModel(
old_name='Foo',
new_name='Bar',
),
]
Next you would run:
python manage.py makemigrations --empty myotherapp
Then you would put this code in the new migration:
dependencies = [
('myotherapp', '0001_initial.py'),
('myapp', '0002_whateverthismigrationwasnamed')
]
operations = [
migrations.RemoveField(
model_name='YetAnotherModel',
name='Foo'
),
migrations.AddField(
model_name='YetAnotherModel',
name='Bar',
field=models.ManyToManyField(blank=True, null=True, to='myapp.Bar'),
),
]
It's important to make sure that you add the rename model migration from myapp as a dependency so it runs first.
Now, if you are in production you should NOT use this method without taking precautions. It straight up deletes the linking table. This from the django docs on RemoveField:
Bear in mind that when reversed, this is actually adding a field to a model. The operation is reversible (apart from any data loss, which of course is irreversible) if the field is nullable or if it has a default value that can be used to populate the recreated column.
If you are in production, you will want to take steps to backup the data so you can restore it into the new table.

Related

Django 1.11 - make column foreign key without deleting

Previously we made integer field like:
cart_id = models.IntegerField(_('cart_id'), null=True)
But now I want to make this field foreign key:
cart = models.ForeignKey(Cart, null=True, db_column='cart_id')
The problem is that in the migration it generates two operations for deleting field and creating new one:
operations = [
migrations.RemoveField(
model_name='order',
name='cart_id',
),
migrations.AddField(
model_name='order',
name='cart',
field=models.ForeignKey(db_column=b'cart_id', to='cart.Cart', null=True),
preserve_default=True,
),
]
Is there any way to make it as alter field?
First add the ForeignKey. Set the default blank=True and run migrations.
Then run this code to fill the previous instances (python manage.py shell):
m = Order.objects.all()
for i in m:
c = Cart.object.get(id=i.cart_id)
i.cart = c
i.save()
Once done check if the ForeignKey is filled in the admin.
You can remove blank=True in ForeignKey it is a required field.

How to override the migrations of a third-party django package

All the previous answers I came across are not clear or old.
I have a third-party package installed and migrations ran.
Problem is the third-party package uses an Interger field to reference users(based on the assumption that the app is using the default django user), but in my case I am using a uuid for user IDs
package models.py
class UserDashboardModule(models.Model):
title = models.CharField(verbose_name=_('Title'), max_length=255)
user = models.PositiveIntegerField(verbose_name=_('user'))
column = models.PositiveIntegerField(verbose_name=_('column'))
order = models.IntegerField(verbose_name=_('order'))
collapsed = models.BooleanField(verbose_name=_('collapsed'), default=False)
...
# Migrations of third-party package
operations = [
migrations.CreateModel(
name='UserDashboardModule',
fields=[
('id', models.AutoField(verbose_name='ID', primary_key=True, serialize=False, auto_created=True)),
('title', models.CharField(verbose_name='Title', max_length=255)),
('module', models.CharField(verbose_name='module', max_length=255)),
('app_label', models.CharField(verbose_name='application name', max_length=255, blank=True, null=True)),
('user', models.PositiveIntegerField(verbose_name='user')),
]
),
]
My user model is like this
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
...
this setup makes it impossible to use the third-party package and I will like to maintain my uuids on users table.
What I want to do is to somehow override this migration and turn the user field to a uuid instead of an interger field.
PS: I have tried creating an empty migration in my users app and wrote the migration but it didn't work.
don't know this is gonna work or not , you can set id of user model as whatever this app is comfortable to work with . then make a migration file that depends on the last migration of other app then convert your model and foreign key to whatever you want it to be .
This can be done with django's MIGRATION_MODULES setting.
For example, I needed to override the migrations in the puput application, for a very similar reason to yours (they hard coded the name of what should have been a swappable dependency model).
I copied all of puput's migration files to a directory in one of my own apps, in my case app/custom_puput_migrations and edited them as I saw fit.
Then in settings I put:
MIGRATION_MODULES = {'puput': 'app.custom_puput_migrations')
Then I recreated my database from scratch and away it went.
The downside of this is that you have to maintain those migrations; each time you upgrade the third party app version, you need to check for and copy across any migrations they may have made.

Django: Add new foreign key to existing model with default value as another foreign key from the same model

I recently started using Django, so please be patient. I have a model with 2 foreign keys
class Application(models.Model):
assessment_owner = models.ForeignKey(User, related_name='assessment_owner')
creator = models.ForeignKey(User, related_name='creator')
I am trying to add new foreign key called tech_lead to the same model, and default value for tech_lead should be assessment_owner. Later on, I can update the value for tech_lead using data load, but initially it should be assessment owner.
With following code snippet, Django asks for a default value while making migrations and assigns the same tech_lead everywhere. I would like to define default value for tech_lead through code, and simple default attribute doesn't work. I have tried using signals pre_save and post_save with no luck.
class Application(models.Model):
assessment_owner = models.ForeignKey(User, related_name='assessment_owner')
creator = models.ForeignKey(User, related_name='creator')
tech_lead = models.ForeignKey(User, related_name='tech_lead')
I am using Django 1.11.3 and postgreSQL.
Migration was successful with one-off default value.
Error Stack -
Env details
error
error
Thanks in advance.
tech_lead = models.ForeignKey(User, related_name='tech_lead')
breaks integrity because your database is already populated with Application instances. If you want to add a not nullable FK to your scheme, you should specify default value. Otherwise, if you can't provide default value, you should consider allowing tech_lead to be NULL, i.e:
tech_lead = models.ForeignKey(User, related_name='tech_lead', null=True)
then using data migration to populate field with values you want:
from django.db import migrations
def populate_tech_lead(apps, schema_editor):
Application = apps.get_model('yourappname', 'Application')
for application in Application.objects.all():
application.tech_lead = application.assessment_owner
application.save()
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(populate_tech_lead),
]
and then removing null=True from the field:
tech_lead = models.ForeignKey(User, related_name='tech_lead')
Step 1. add null=True to the tech_lead field as
class Application(models.Model):
assessment_owner = models.ForeignKey(User, related_name='assessment_owner')
creator = models.ForeignKey(User, related_name='creator')
tech_lead = models.ForeignKey(User, related_name='tech_lead', null=True)
Step 2. create migration file by python manage.py makemigrations
Step 3. migrate the db python manage.py migrate
Step 4. open django shell, python manage.py shell
Step 5. run the following script
from your_app.models import Application
from django.db.models.expressions import F
Application.objects.filter(tech_lead__isnull=True).update(tech_lead=F('assessment_owner'))

Django migrations with foreign key

I' trying to migrate this model:
class Questionpart_image(models.Model):
questionpart = models.ForeignKey(Questionpart, null=True, blank=True)
image = models.ImageField()
to this:
class Questionpart_image(Questionpart): # notice this base class
image = models.ImageField()
to make advantage of inheritance. Django produces the following migration:
class Migration(migrations.Migration):
dependencies = [
('ochsite', '0016_auto_20150809_1903'),
]
operations = [
migrations.RemoveField(
model_name='questionpart_image',
name='id',
),
migrations.RemoveField(
model_name='questionpart_image',
name='questionpart',
),
migrations.AddField(
model_name='questionpart_image',
name='questionpart_ptr',
field=models.OneToOneField(default='', primary_key=True, to='ochsite.Questionpart', serialize=False, parent_link=True, auto_created=True),
preserve_default=False,
),
]
but this does not set the right foreign key to questionpart_ptr from questionpart field. How can I achieve that?
I've been searching for a long time, but nothing...thanks
Simply don't rely on automatic migrations, make your own or modify that one.
Simplest solution will be by moving AddField in migration to the top of the list and inject between it and RemoveFields RunPython block that will rewrite id's from old field to new (and in other direction in reverse, if needed).

Renaming models(tables) in Django

so I've already created models in Django for my db, but now want to rename the model. I've change the names in the Meta class and then make migrations/migrate but that just creates brand new tables.
I've also tried schemamigration but also not working, I'm using Django 1.7
Here's my model
class ResultType(models.Model):
name = models.CharField(max_length=150)
ut = models.DateTimeField(default=datetime.now)
class Meta:
db_table = u'result_type'
def __unicode__(self):
return self.name
Cheers
Django does not know, what you are trying to do. By default it will delete old table and create new.
You need to create an empty migration, then use this operation (you need to write it by yourself):
https://docs.djangoproject.com/en/stable/ref/migration-operations/#renamemodel
Something like this:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RenameModel("OldName", "NewName")
]

Categories