I'm moving the field some_field from Model_A to a new Model_B, with a OneToOne relationship. Before deleting this field in Model_A, I want to copy the value from (the historical) Model_A to the newly created Model_B. The problem is that I cannot retrieve the field at migration time, since Model_A doesn't any longer contain some_field.
This is the error message I get when I try running my custom migration:
AttributeError: 'Model_A' object has no attribute 'some_field'
Models before changes:
class Model_A:
some_field = models.BooleanField(default=False)
some_other_field = models.BooleanField(default=False)
Models after changes:
class Model_A:
some_other_field = models.BooleanField(default=False)
class Model_B:
model_a = models.OneToOneField(Model_A, related_name='extension')
some_field = models.BooleanField(default=False)
Migration:
class Migration(migrations.Migration):
dependencies = [
('my_app', '0001_initial'),
]
def forwards_func(apps, schema_editor):
# This is where I try to get the "historical" Model_A
Model_A = apps2.get_model("my_app", "Model_A")
# And this is where I intend to copy the some_field values
for model_A_instance in Model_A.objects.all():
b = Model_B(model_a=model_A_instance)
# b gets created correctly, but the following step fails
b.some_field = modelA_instance.some_field
b.save()
operations = [
migrations.CreateModel(
name='Model_B',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('some_field', models.BooleanField(default=False)),
('model_a', models.OneToOneField(related_name='extension', to='my_app.Model_A')),
],
options={
},
bases=(models.Model,),
),
migrations.RunPython(forwards_func),
migrations.RemoveField(
model_name='model_a',
name='some_field',
),
]
I'm pretty much aware that I somehow have to get hold of a "historical" representation of Model_A (= the one currently in the database), but I thought that was what the apps2.get_model("my_app", "Model_A") part was for.
Any input on how to accomplish this? Or should I maybe split the migration into two, where the first one creates Model_B + copies the some_field values and the second one deletes the some_field field from Model_A?
Yes, you need to have historical representation of Model_A and it's pretty easy to retrieve it. Thats what apps passed into your function called by RunPython migration is for, so why are you using some apps2 insdead of apps here? Also, you should get your Model_B from same apps instance as Model_A is from. Your migration should look like that:
class Migration(migrations.Migration):
dependencies = [
('my_app', '0001_initial'),
]
def forwards_func(apps, schema_editor):
# This is where I try to get the "historical" Model_A
# here is change - using apps passed into forwards_func by RunPython instead of apps2
Model_A = apps.get_model("my_app", "Model_A")
Model_B = apps.get_model("my_app", "Model_B")
# And this is where I intend to copy the some_field values
for model_A_instance in Model_A.objects.all():
b = Model_B(model_a=model_A_instance)
b.some_field = modelA_instance.some_field
b.save()
operations = [
migrations.CreateModel(
name='Model_B',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('some_field', models.BooleanField(default=False)),
('model_a', models.OneToOneField(related_name='extension', to='my_app.Model_A')),
],
options={
},
bases=(models.Model,),
),
migrations.RunPython(forwards_func),
migrations.RemoveField(
model_name='model_a',
name='some_field',
),
]
Why are you using apps2 anyway? And what it is?
Related
models.py
class Country(models.Model):
name = models.CharField(max_length=50, validators=[validate_name, ])
class Meta:
managed = False
db_table = 'countries'
def __str__(self):
return self.name
0001_initial.py
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Country',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50, validators=[countries.validators.validate_name])),
],
options={
'db_table': 'countries',
'managed': False,
},
),
]
sql
(venv) michael#michael:~/Documents/PyCharmProjects/db/db$ python manage.py sqlmigrate countries 0001_initial
BEGIN;
--
-- Create model Country
--
-- (no-op)
COMMIT;
Could you tell me whether this sql reflects the model or not? If not, how can it happen? And will it produce in the database?
There is no sql to apply to the db because of managed = False.
That is, running migrate does not change the db.
I created a dummy project just to test the new field JSONField of Django but the column doesn't not appear to be created (I am using Postgresql).
class Author(models.Model):
name = models.CharField(max_length=50)
description = models.TextField()
slug = models.SlugField()
details = models.JSONField()
class Meta:
verbose_name = "Author"
def __str__(self):
return self.name
If i go to the database, the column is not created -- screenshot
When i go to the admin view page of the table Author i get the following error -- screenshot
The error in the admin panel is understandable: you cannot view a column that does not exist. Do you guys have the same error? I can't find this error with JSONField anywhere.
Thanks
Note: This is my first post.
EDIT I create all the fields in the same time. Migration file:
# Generated by Django 3.1.3 on 2020-11-20 10:35
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Author',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=50)),
('description', models.TextField()),
('slug', models.SlugField()),
('details', models.JSONField()),
],
options={
'verbose_name': 'Author',
},
),
]
Migrated with sucess:
Migrations for 'blog':
blog/migrations/0001_initial.py
- Create model Author
check that you migration was applied to the right database
I have the following models:
class Contact(models.Model):
email = models.EmailField()
class EventList(models.Model):
event_contacts = models.ManyToManyField(Contact, through=EventMembership)
class EventMembership(models.Model):
event_list = models.ForeignKey(EventList, null=True, on_delete=models.PROTECT)
event_contact = models.ForeignKey(Contact, null=True, blank=False, on_delete=models.PROTECT)
However, when applying migrations for EventMembership on a completely clean database I get the following error:
psycopg2.errors.InvalidForeignKey: there is no unique constraint
matching given keys for referenced table "contacts_contact"
class Migration(migrations.Migration):
initial = True
dependencies = [
('lists', '0001_initial'),
('contacts', '0002_auto_20200308_2253'),
]
operations = [
migrations.CreateModel(
name='EventMembership',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('event_contact', apps.utils.django_multitenant.fields.TenantForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='contacts.Contact')),
('event_list', apps.utils.django_multitenant.fields.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='lists.EventList'))
]
]
Table contacts_contact clearly has a unique constraint in id as the primary key.
What could be causing this error? / How do I debug this?
You just need to do it step by step. Now you are trying to create a foreign key relationship with a table that is not in the database yet. So comment everything out except for Contact model, apply migrations, then add EventList etc. If you are relying on the fact that Contact model goes first, well, it doesn't help in this case.
I have the following issue:
I work on a Django app that has a primary DB and now we are adding a secondary DB for syncing only some of the data in it. When I tried to run the migrations, I got an error: null value in column 'field_2' violates not-null constraint. And here is the issue:
The model:
class A(models.Model):
field_1 = models.BooleanField(default=False)
field_2 = models.BooleanField(default=True) # added later
field_3 = models.BooleanField(default=True) # added later
and 4 migrations (well, more, but those don't count):
0001 - a related model is created
0002 - model A is created and the relation is created (ForeignKey)
0003 - field_2 is added to model A
0004 - field_3 is added to model A
However when I run migrate on the new DB, the migration stops at migration 0002 with the error mentioned above:
django.db.utils.IntegrityError: null value in column "field_2" violates not-null constraint
even though the field is created in a later migration.
I don't know hot to even begin a workaround this issue.
Edit:
Migration 0001:
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name='B',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('year', models.IntegerField()),
('active', models.BooleanField(default=True)),
],
)
]
Migration 0002:
class Migration(migrations.Migration):
dependencies = ['app', '0001']
operations = [
migrations.CreateModel(
name='A',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('field_1', models.BooleanField(default=False))
],
),
migrations.AddField(
model_name='b',
name='a',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to='app.B'),
),
]
Migration 0003:
class Migration(migrations.Migration):
dependencies = ['app', '0002']
operations = [
migrations.AddField(
model_name='a',
name='field_2',
field=models.BooleanField(default=True),
),
]
Migration 0004:
class Migration(migrations.Migration):
dependencies = ['app', '0003']
operations = [
migrations.AddField(
model_name='a',
name='field_3',
field=models.BooleanField(default=True),
),
]
Migrated: 0001
I have a contact information model/form which I want to add a contacttype foreign key to that should be displayed in a drop down menu:
models.py
class ContactType(models.Model):
contact_type = models.CharField(max_length=50)
class Contact(models.Model):
contact_name = models.CharField(max_length=200)
contact_email = models.CharField(max_length=200, validators=[validators.validate_email])
contact_type = models.ForeignKey(ContactType, default='General Enquiry')
forms.py
class ContactForm(forms.ModelForm):
# contact_type = forms.ModelChoiceField(queryset=ContactType.objects.all())
class Meta:
model = Contact
fields = ['contact_name', 'contact_email']
If I leave the contact_type variable where it is, I get the following error:
no such table: contact_contacttype
Here is my last migration:
dependencies = [
('contact', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='ContactType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('contact_type', models.CharField(max_length=50)),
],
),
migrations.AddField(
model_name='contact',
name='contact_type',
field=models.ForeignKey(default='General Enquiry', on_delete=django.db.models.deletion.CASCADE, to='contact.ContactType'),
),
]
whereas if I move it into meta class (should I?) nothing renders into the template.
So my question is, is a foreign key the appropriate relationship? And if so, how should I add the ModelChoiceField to the ContactForm