PostgreSQL psycopg2 error: there is no unique constraint / InvalidForeignKey - django

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.

Related

Two OneToOneFields for the same table are not allowed in Django models

so I am trying to store the user's location coordinates in another table by linking it to the original using a OneToOneField.
class Coordinates(models.Model):
lat = models.FloatField()
lng = models.FloatField()
class Trip (models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True) #Drivers Profile
departure = models.CharField(max_length=200)
departureCoord = models.OneToOneField(Coordinates, on_delete=models.PROTECT)
arrival = models.CharField(max_length=200)
arrivalCoord = models.OneToOneField(Coordinates, on_delete=models.PROTECT)
date = models.DateField(validators=[inthe_future])
time = models.TimeField(default=datetime.now().time())
vacant_seats = models.PositiveIntegerField()
vehicle_used = models.ForeignKey(Vehicle, on_delete=models.PROTECT)
price_per_person = models.IntegerField()
status = models.CharField(choices=STATUS, default='UPCOMING', max_length=10)
def __str__ (self):
return f"{self.user.first_name} - ({self.departure} => {self.arrival})"
I have used an OneToOneField for both arrivalCoord and departureCoord since each row on the Coordinates table is going to uniquely link the departure and arrival of every Trip.
So by still having a one-to-one relationship, is it possible for me to get the same thing done as when I try to run makemigrations I'm thrown with this error.
←[31;1mapp.Trip.departureCoord: (fields.E304) Reverse accessor for 'app.Trip.departureCoord' clashes with reverse accessor for 'app.Trip.arrivalCoord'.
HINT: Add or change a related_name argument to the definition for 'app.Trip.departureCoord' or 'app.Trip.arrivalCoord'.←[0m
←[31;1mapp.Trip.departureCoord: (fields.E305) Reverse query name for 'app.Trip.departureCoord' clashes with reverse query name for 'app.Trip.arrivalCoord'.
HINT: Add or change a related_name argument to the definition for 'app.Trip.departureCoord' or 'app.Trip.arrivalCoord'.←[0m
I think there is a better way to improve the design of the database, are you'll aware of anything?
Thanks!
EDIT
This is my latest migration file...
# Generated by Django 3.2.3 on 2021-06-18 04:22
import datetime
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('app', '0020_alter_trip_time'),
]
operations = [
migrations.CreateModel(
name='Coordinates',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('lat', models.FloatField()),
('lng', models.FloatField()),
],
),
migrations.AlterField(
model_name='trip',
name='time',
field=models.TimeField(default=datetime.time(9, 52, 22, 413173)),
),
migrations.AddField(
model_name='trip',
name='arrivalCoord',
field=models.OneToOneField(default=0.0, on_delete=django.db.models.deletion.PROTECT, related_name='arrivalCoord', to='app.coordinates'),
),
migrations.AddField(
model_name='trip',
name='departureCoord',
field=models.OneToOneField(default=0.0, on_delete=django.db.models.deletion.PROTECT, related_name='departureCoord', to='app.coordinates'),
),
]
I have created the Coordinates table and the OneToOneField for the first time.
You need to add a unique related_name to each OneToOneField so that unique reverse relationships can be added to the Coordinates model
class Trip (models.Model):
...
departureCoord = models.OneToOneField(Coordinates, on_delete=models.PROTECT, related_name='departure')
arrivalCoord = models.OneToOneField(Coordinates, on_delete=models.PROTECT, related_name='arrival')
...

Django new field "JSONField" error - Column does not exist

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

Creating models.UniqueConstraint in django, but the constraint is not applying

My table is defined as below.
class Role(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
to_field="email",
db_column="user",
on_delete=models.CASCADE
)
vertical = models.ForeignKey(
Verticals,
to_field="name",
db_column="vertical",
on_delete=models.CASCADE
)
product_domain = models.ForeignKey(
ProductDomains,
to_field="name",
db_column="product_domain",
null=True,
on_delete=models.CASCADE
)
class Meta:
constraints = [
models.UniqueConstraint(fields=[
'user',
'vertical',
'product_domain'
],
name='unique-permissions-per-user')
]
Here are the migrations that are generated
migrations.CreateModel(
name='Role',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('product_domain', models.ForeignKey(db_column='product_domain', null=True, on_delete=django.db.models.deletion.CASCADE, to='verticals.ProductDomains', to_field='name')),
('user', models.ForeignKey(db_column='user', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='email')),
('vertical', models.ForeignKey(db_column='vertical', on_delete=django.db.models.deletion.CASCADE, to='verticals.Verticals', to_field='name')),
],
),
migrations.AddConstraint(
model_name='role',
constraint=models.UniqueConstraint(fields=('user', 'vertical', 'product_domain'), name='unique-permissions-per-user'),
),
Serializers for the Role model is
class RoleSerializer(serializers.ModelSerializer):
class Meta:
model = Role
fields = '__all__'
Here is the interactive console for the same
Link to console image (Unable to add due to less reputation )
Here the UniqueConstraint is not working, why?
I use models.UniqueConstraint in the same project many times but it doesn't work in this case.
My configuration is
Django - 3.0.4
Django Rest Framework - 3.11.0
Database - MySql
Please help and ask if any information is missing out.
Your logs don't show any attempt to insert the same record into the DB twice, just validation.
It appears Django Rest Framework does not import constraints from UniqueConstraint, and only has code to validate with the old Meta.unique_together constraint definition.
There is a tracking issue, ModelSerializer not generating validators for constraints, on the DRF GitHub where you can follow the latest updates on adding support for UniqueConstraint validation.

Adding a foreign key drop down menu to django form

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

Problems accessing Django model fields from model before migration

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?