Django 1.8 migrations - CircularDependencyError - python

I have 2 django apps i.e. main and authtools. When I run
python manage.py migrate
, I get a CircularDependencyError:
raise CircularDependencyError(", ".join("%s.%s" % n for n in cycle))
django.db.migrations.graph.CircularDependencyError: main.0001_initial, authtools.0001_initial
In my setting file I have the AUTH_USER_MODEL defined as such:AUTH_USER_MODEL = 'authtools.User' . The migration files created look like this:
For the authtools app, it shows dependancies as:
dependencies = [
('main', '__first__'),
('auth', '0001_initial'),
]
And for the main app, the depandancies are shown as:
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
What could be wrong?

If you use ManyToMany + 'through' option to different application, than according to this answer you should:
Comment a whole line where you use ManyToMany+through by putting #
at the beginning of the line
makemigrations of_all_apps_you_need (also the one where the line from p1 exist)
uncomment the line from p1
makemigrations the_app_where_the_line_from_p1_exist
migrate
If you dont use ManyToMany, than according to this answer, try similar actions.
Lets suppose that you want to create these models:
libros/models.py:
class Libro(models.Model):
name = models.CharField(max_length=20)
perfile = models.ForeignKey('perfiles.Perfile', null=True)
perfiles/models.py:
class Perfile(models.Model):
name = models.CharField(max_length=20)
libro = models.ForeignKey('libros.Libro', null=True)
Of course you can't do it because of the circular dependency. So comment out foreign key in the Libro model:
class Libro(models.Model):
name = models.CharField(max_length=20)
# perfile = models.ForeignKey('perfiles.Perfile', null=True)
And run two migrations:
python manage.py makemigrations libros
python manage.py makemigrations perfiles
After that uncomment the perfile foreign key in the Libro model and run another migration:
python manage.py makemigrations libros

I think you need to follow this ticket of django code base: https://code.djangoproject.com/ticket/22932
According to them your migration code should look like either this (https://code.djangoproject.com/attachment/ticket/22932/team.0001_initial.py.diff) or(https://code.djangoproject.com/attachment/ticket/22932/team.0002_auto_20140704_1453.py):
# -*- coding: utf-8 -*-
2 from __future__ import unicode_literals
3
4 from django.db import models, migrations
5 from django.conf import settings
6
7
8 class Migration(migrations.Migration):
9
10 dependencies = [
11 migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 ('team', '0001_initial'),
13 ]
14
15 operations = [
16 migrations.CreateModel(
17 name='TeamCaptain',
18 fields=[
19 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
20 ('rider', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
21 ('team', models.ForeignKey(to='team.Team')),
22 ],
23 options={
24 },
25 bases=(models.Model,),
26 ),
27 ]

The idea with migrations is that you have to plan the workflow that Django needs to follow when applying your models.
Let's say you have two models and each of one has a ForeignKey to the other one. You cannot create these two models and makemigrations. You have to apply them step by step. First apply one model without the relation to the other one, then the other model (now, the relation to the first one can be kept) and then the first model's relation to the second. Think logical. It's impossible to write a model which relies on another one without having the second one.
But this has to be done just for the first migrations creation process (e.g. a new application without any migration). Then you just maintain and update your models.

I've experienced the same issue and was able to resolve it by interchanging the order of makemigration command.
Using TitanFighter's example. There's no need to escape model fields, instead makemigration for perfiles first then migrations for libros.
Hopefully this helps someone.

Related

Django cannot delete custom field class; makemigrations throws AttributeError

Lead-up:
I subclassed django.db.models.fields.CharField. Then used that custom field called myapp.models.QueryStringField in a model and made my migrations and migrated successfully. Then I changed my mind and decided to replace it with a normal CharField in my model, which I did (again with successfull migration).
Problem:
When I then deleted the QueryStringField class entirely from myapp.models and did makemigrations, it threw the following error (last lines shown here):
File "C:\...\migrations\00....py", line 17, in Migration
field=myapp.models.QueryStringField(max_length=255),
AttributeError: module 'myapp.models' has no attribute 'QueryStringField'
What can I do to fix this? I understand that this is technically correct, since the migration references a class that is not present, but surely this can be solved somehow. I am a little nervous about just deleting migration files.
You can not just delete a field class, that class is referenced by the migration files.
You should change the migration files where the QueryStringField is involved. You can inspect the migration where you changed the field, and remove that part of the migration, so:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('someapp', '1234_some_name'),
]
operations = [
# migrations.AlterField(
# model_name='mymodel',
# name='myfield',
# field=models.QueryStringField(
# # ...
# ),
# ),
]
as well as in the migration where you change it back:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('someapp', '5678_some_other_name'),
]
operations = [
# migrations.AlterField(
# model_name='mymodel',
# name='myfield',
# field=models.CharField(
# # ...
# ),
# ),
]
After you removed these changes (and all changes that thus work with the QueryStringField), you can safely remove it.

Add multiple class objects from another class

I want to generate multiple country objects with the similar language object without going through the admin UI.
models.py
from django.db import models
class World(models.Model):
country = models.CharField(max_length=200)
Language = models.CharField(max_length=200)
class add_countries(models.Model):
sub_part1 = ['USA','ENGLAND','AUSTRALIA','CANADA']
for sub in sub_part1:
sub = World.country
World.Language = "English"
add_countries()
for country in ['USA','ENGLAND','AUSTRALIA','CANADA']:
World.objects.get_or_create(country=country, language='English')
You can put above code inside a method and call that.
If you're looking to have a few database entries that are always there, you should probably consider using data migrations. This would result in these database rows being created when you migrate your database. The basic idea would be something like this:
from django.db import migrations
def add_languages(apps, schema_editor):
# We can't import the Person model directly as it may be a newer
# version than this migration expects. We use the historical version.
World = apps.get_model('yourappname', 'World')
languages = {'English': ['USA','ENGLAND','AUSTRALIA','CANADA']}
for language, countries in languages.items():
for country in countries:
World.objects.get_or_create(country=country, Language=language)
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
migrations.RunPython(add_languages),
]
The migration file would be in your app's migrations directory (something like project_root/yourappname/migrations/0002_insert_languages.py in this case). Consult the documentation for more information.

View new column conditioned on other columns in Django

With Django 1.11, I am trying to add a new column that is conditioned on other columns (as shown in the below image) and view it at front-end. This link is the closest example but I would like to implement it in Django. How can we do this?
I suggest implement this action on the migration file:
After you changed model, execute ./manage.py makemigrations
Open new migration file in editor, content of this file maybe is similar as below:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.AddField('MyModel', 'A_islargerthan_B', models.BooleanField(default=False)),
]
Now you must inject your updater code by using migrations.RunPython :
from django.db import migrations, models
def update_A_islargerthan_B(apps, schema_editor):
MyModel = apps.get_model('my_app', 'MyModel')
for obj in MyModel.objects.all():
obj.A_islargerthan_B = obj.column_A > obj.column_B
obj.save()
class Migration(migrations.Migration):
dependencies = [('migrations', '0001_initial')]
operations = [
migrations.AddField('MyModel', 'A_islargerthan_B', models.BooleanField(default=False)),
migrations.RunPython(update_A_islargerthan_B),
]
Run ./manage.py migrate
Read more about Django migrations

How can I add a new field in model when I already have a datamigration in Django?

I have a migrations come from the work of another person, and I'm in on a code base controlled by the version too. I created a migration file after adding a new field but the problem is in the data migration (0003_datamigration_initial_service_configuration.py)that is created before through the function get_or_create () is executed in the head of the list while my migration file data in the end I should not change a migration already developed.
This is my list of migrations
0001_initial.py
0002_datamigration_initial_users_list.py
0003_datamigration_initial_mymodel.py
...
0015.addnewfield
0003_datamigration_initial_mymodel.py:
from __future__ import unicode_literals
from django.db import migrations
from ..models import MyModel
def create_initial_mymodel(apps, schema_editor):
current_product_type, created = MyModel.objects.get_or_create()
class Migration(migrations.Migration):
dependencies = [
('myapp', '0002_datamigration_initial_users_list'),
]
operations = [
migrations.RunPython(create_initial_mymodel),
]
Rhe error is no such column named new_field
How can I fix the problem without editing any data migrations?
You should use apps.get_model to get the MyModel model, instead of importing it directly.
def create_initial_mymodel_forward(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
current_product_type, created = MyModel.objects.get_or_create()
Using apps.get_model will load a historical version of the model with the correct fields.

Django 1.8: Delete/rename a model field in data migration

I need to change the relationship of a model field from ForeignKey to ManyToManyField. This comes with a data migration, to update the pre-existing data.
The following is the original model (models.py):
class DealBase(models.Model):
[...]
categoria = models.ForeignKey('Categoria')
[...]
)
I need the model field 'categoria' to establish a many2many relationship with the model 'Categoria' in the app 'deal'.
What I did:
Create a new field 'categoria_tmp' in DealBase
class DealBase(models.Model):
categoria = models.ForeignKey('Categoria')
categoria_tmp = models.ManyToManyField('Categoria',related_name='categoria-temp')
make a schema migration
python manage.py makemigrations
Edit the migrationfile.py to migrate data from categoria to categoria-tmp
def copy_deal_to_dealtmp(apps, schema_editor):
DealBase = apps.get_model('deal', 'DealBase')
for deal in DealBase.objects.all():
deal.categoria_tmp.add(deal.categoria)
deal.save()
class Migration(migrations.Migration):
dependencies = [
('deal', '0017_dealbase_indirizzo'),
]
operations = [
migrations.AddField(
model_name='dealbase',
name='categoria_tmp',
field=models.ManyToManyField(related_name='categoria-temp', to='deal.Categoria'),
preserve_default=True,
),
migrations.RunPython(
copy_deal_to_dealtmp
)
]
make data migration
python manage.py migrate
Finally I need to delete the column 'dealbase.categoria' and rename the column 'dealbase.categoria-tmp' to 'dealbase.categoria'
I'm stuck at step 5.
Could someone help me out? I cannot find online an answer, I'm using Django 1.8.
Thanks!
You just need to create two additional migrations: one to remove the old field and the other to alter the new field.
First remove dealbase.categoria and create a migration and then rename dealbase.categoria-tmp to dealbase.categoria and create another migration.
This will delete the first field and then alter the tmp field to the correct name.
Try this, may help you.
Step 1 as yours
python manage.py makemigrations && python manage.py migrate
Open shell
for i in DealBase.objects.all()
i.categoria_tmp.add(i.categoria)
Remove your field categoria
python manage.py makemigrations && python manage.py migrate
Add field
categoria = models.ManyToManyField('Categoria',related_name='categoria-temp')
Then
python manage.py makemigrations && python manage.py migrate
Open shell
for i in DealBase.objects.all():
for j in i.categoria_tmp.all():
i.categoria.add(j)
Remove field categoria_tmp
python manage.py makemigrations && python manage.py migrate
If you don't have any data in this model, just comment on that model, and then run manage.py makemigrations and migrate. Then delete the wrong field and delete the comment code, and make makemigrations and migrate. This also works in Django 2.

Categories

Resources