Fix constraint for address model (#5076)

* Fix constraint for address model

- Do not handle at database level
- Add a "validate_unique" method to the address model
- Fixes https://github.com/inventree/InvenTree/issues/5070

* Remove unique constraint rule

* Unit test fix

- Adjust unit test around new code

* Further unit test updates
This commit is contained in:
Oliver 2023-06-20 16:57:20 +10:00 committed by GitHub
parent b700b44c53
commit 303305e05f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 22 additions and 11 deletions

View File

@ -30,8 +30,4 @@ class Migration(migrations.Migration):
('company', models.ForeignKey(help_text='Select company', on_delete=django.db.models.deletion.CASCADE, related_name='addresses', to='company.company', verbose_name='Company')),
],
),
migrations.AddConstraint(
model_name='address',
constraint=models.UniqueConstraint(condition=models.Q(('primary', True)), fields=('company',), name='one_primary_per_company'),
),
]

View File

@ -306,9 +306,6 @@ class Address(models.Model):
class Meta:
"""Metaclass defines extra model options"""
constraints = [
UniqueConstraint(fields=['company'], condition=Q(primary=True), name='one_primary_per_company')
]
verbose_name_plural = "Addresses"
@staticmethod
@ -316,6 +313,16 @@ class Address(models.Model):
"""Return the API URL associated with the Contcat model"""
return reverse('api-address-list')
def validate_unique(self, exclude=None):
"""Ensure that only one primary address exists per company"""
super().validate_unique(exclude=exclude)
if self.primary:
# Check that no other primary address exists for this company
if Address.objects.filter(company=self.company, primary=True).exclude(pk=self.pk).exists():
raise ValidationError({'primary': _('Company already has a primary address')})
company = models.ForeignKey(Company, related_name='addresses',
on_delete=models.CASCADE,
verbose_name=_('Company'),

View File

@ -5,7 +5,6 @@ from decimal import Decimal
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.utils import IntegrityError
from django.test import TestCase
from part.models import Part
@ -199,17 +198,23 @@ class AddressTest(TestCase):
c2 = Company.objects.create(name='Test Corp2.', description='We make stuff good')
Address.objects.create(company=self.c, primary=True)
Address.objects.create(company=self.c, primary=False)
self.assertEqual(Address.objects.count(), 2)
# Testing the constraint itself
# Intentionally throwing exceptions breaks unit tests unless performed in an atomic block
with transaction.atomic():
self.assertRaises(IntegrityError, Address.objects.create, company=self.c, primary=True, confirm_primary=False)
with self.assertRaises(ValidationError):
addr = Address(company=self.c, primary=True, confirm_primary=False)
addr.validate_unique()
Address.objects.create(company=c2, primary=True, line1="Hellothere", line2="generalkenobi")
with transaction.atomic():
self.assertRaises(IntegrityError, Address.objects.create, company=c2, primary=True)
with self.assertRaises(ValidationError):
addr = Address(company=c2, primary=True, confirm_primary=False)
addr.validate_unique()
self.assertEqual(Address.objects.count(), 3)
def test_first_address_is_primary(self):
@ -219,7 +224,10 @@ class AddressTest(TestCase):
self.assertTrue(addr.primary)
self.assertRaises(IntegrityError, Address.objects.create, company=self.c, primary=True)
# Create another address, which should error out if primary is not set to False
with self.assertRaises(ValidationError):
addr = Address(company=self.c, primary=True)
addr.validate_unique()
def test_model_str(self):
"""Test value of __str__"""