multiple Many to Many constraints - ProgrammingError column xy_id does not exists

Hi everyone,

I’m trying to add contraints to a model with multiple many to many fields.

Basically i would like to ensure that at least one value is entered for tenant_person OR for tenant_company. An d the same for the landord_tenant and landolord_company

I did write this constraints:
class Meta:
constraints = [
models.CheckConstraint(
name="%(app_label)s_%(class)s_tenant_not_null",
check=(models.Q(tenant_person__isnull=False) | models.Q(tenant_company__isnull=False))
),
models.CheckConstraint(
name="%(app_label)s_%(class)s_landlord_not_null",
check=(models.Q(landlord_person__isnull=False) | models.Q(landlord_company__isnull=False))
)
]

When I make the migrations everything is fine but when I migrate I get this error:

django.db.utils.ProgrammingError: column “person_id” does not exist

My DB backend is Postgres in a docker

I looked around in the doc, stackoverflow and the forum… I guess that it might be related to the type (ManyToMany) of the fields.

Any help, direction to the right documentation would be much appreciated

Axel

Here is my full model:

class RentContract(models.Model):
#
# BANK_TRANSFER = 'bank transfer'
# BLOCKED_ACCOUNT = 'blocked account'
# CASH = 'cash'
# DEPOSIT_TYPE = [
#     (BANK_TRANSFER, 'Bank transfer'),
#     (BLOCKED_ACCOUNT, 'Blocked account'),
#     (CASH, 'Cash'),
# ]

property = models.ForeignKey('real_estate.Property', on_delete=models.PROTECT)
tenant_person = models.ManyToManyField('address_book.Person', related_name='tenant_person', default=None,
                                       blank=True)
tenant_company = models.ManyToManyField('address_book.Company', related_name='tenant_company', default=None,
                                        blank=True)
tenant_representative = models.ForeignKey('address_book.Person', on_delete=models.PROTECT,
                                          related_name='tenant_representative', default=None, null=True, blank=True)

landlord_person = models.ManyToManyField('address_book.Person', related_name='landlord_person', default=None,
                                         blank=True)
landlord_company = models.ManyToManyField('address_book.Company', related_name='landlord_company', default=None,
                                          blank=True)
landlord_representative = models.ForeignKey('address_book.Person', on_delete=models.PROTECT,
                                            related_name='landlord_representative', default=None, null=True,
                                            blank=True)
date_signed = models.DateField()
date_start = models.DateField()
length = models.IntegerField(default=None, blank=True, null=True)
date_stop = models.DateField(default=None, blank=True, null=True)
renew_auto = models.BooleanField()
stop_normal_notification = models.IntegerField(default=2)
stop_normal_indemnity = models.FloatField(default=2)
stop_pro_notification = models.IntegerField(default=1)
stop_pro_indemnity = models.FloatField(default=1)
rent = models.FloatField()
expanses = models.FloatField()
payment_target_account = models.ForeignKey('accounting.BankAccount', on_delete=models.PROTECT)
# deposit = models.FloatField()
# deposit_type = models.CharField(max_length=25, choices=DEPOSIT_TYPE, default=BLOCKED_ACCOUNT)

timestamp_updated = models.DateTimeField(auto_now=True)
timestamp_created = models.DateField(auto_now_add=True)
comment = models.TextField(default=None, blank=True, null=True)
note = models.TextField(default=None, blank=True, null=True)

def __str__(self):
    return f"{self.property.name} - {self.date_start}"

class Meta:
    constraints = [
        models.CheckConstraint(
            name="%(app_label)s_%(class)s_tenant_not_null",
            check=(models.Q(tenant_person__isnull=False) | models.Q(tenant_company__isnull=False))
        ),
        models.CheckConstraint(
            name="%(app_label)s_%(class)s_landlord_not_null",
            check=(models.Q(landlord_person__isnull=False) | models.Q(landlord_company__isnull=False))
        )
    ]

It’s probably going to be helpful to see the migration file that is failing, and possibly helpful to see the Person and Company models.

Thank you for your reply and your time …

Here is the migration:

Generated by Django 3.1.5 on 2021-03-27 15:38

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('renting', '0004_auto_20210327_1415'),
    ]

    operations = [
        migrations.AddConstraint(
            model_name='rentcontract',
            constraint=models.CheckConstraint(check=models.Q(('tenant_person__isnull', False), ('tenant_company__isnull', False), _connector='OR'), name='renting_rentcontract_tenant_not_null'),
        ),
        migrations.AddConstraint(
            model_name='rentcontract',
            constraint=models.CheckConstraint(check=models.Q(('landlord_person__isnull', False), ('landlord_company__isnull', False), _connector='OR'), name='renting_rentcontract_landlord_not_null'),
        ),
    ]

Here is the Person model:

class Person(models.Model):
    firstname = models.CharField(max_length=100)
    lastname = models.CharField(max_length=100)
    address = models.ForeignKey(Address, on_delete=models.PROTECT, default=None, null=True, blank=True)
    company = models.ManyToManyField(Company, through='Position', default=None, blank=True)
    phone = models.CharField(max_length=50, default=None, blank=True, null=True)
    email = models.EmailField(default=None, blank=True, null=True)

    timestamp_updated = models.DateTimeField(auto_now=True)
    timestamp_created = models.DateField(auto_now_add=True)
    comment = models.TextField(default=None, blank=True, null=True)
    note = models.TextField(default=None, blank=True, null=True)

    def __str__(self):
        return f"{self.lastname} {self.firstname}"

    class Meta:
        ordering = ('lastname',)

and finaly the Company model:

class Company(models.Model):
    name = models.CharField(max_length=100)
    legal_form = models.CharField(max_length=35, default=None, blank=True, null=True)
    address = models.ForeignKey(Address, on_delete=models.PROTECT, default=None, null=True, blank=True)
    vat_number = models.CharField(max_length=100, default=None, null=True, blank=True)
    registration_number = models.CharField(max_length=100, default=None, null=True, blank=True)
    phone = models.CharField(max_length=50, default=None, blank=True, null=True)
    email = models.EmailField(default=None, blank=True, null=True)
    internal_uuid = models.CharField(max_length=35, default=None, null=True, blank=True)

    timestamp_updated = models.DateTimeField(auto_now=True)
    timestamp_created = models.DateField(auto_now_add=True)
    comment = models.TextField(default=None, blank=True, null=True)
    note = models.TextField(default=None, blank=True, null=True)

    def __str__(self):
        return f"{self.name} {self.legal_form}"

    class Meta:
        verbose_name_plural = 'Companies'
        ordering = ('name',)