2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

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')), ('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: class Meta:
"""Metaclass defines extra model options""" """Metaclass defines extra model options"""
constraints = [
UniqueConstraint(fields=['company'], condition=Q(primary=True), name='one_primary_per_company')
]
verbose_name_plural = "Addresses" verbose_name_plural = "Addresses"
@staticmethod @staticmethod
@ -316,6 +313,16 @@ class Address(models.Model):
"""Return the API URL associated with the Contcat model""" """Return the API URL associated with the Contcat model"""
return reverse('api-address-list') 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', company = models.ForeignKey(Company, related_name='addresses',
on_delete=models.CASCADE, on_delete=models.CASCADE,
verbose_name=_('Company'), verbose_name=_('Company'),

View File

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