Bug fix for ensuring location and category names are unique for common parent (#4361)

* Update Meta class for StockLocation and PartCategory

* Migration files

* Add extra unique requirements to  InvenTreeTree model

- unique_together does not work as expected with null values
This commit is contained in:
Oliver 2023-02-18 11:42:53 +11:00 committed by GitHub
parent cde2050236
commit 139274f356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 18 deletions

View File

@ -501,6 +501,34 @@ class InvenTreeTree(MPTTModel):
parent: The item immediately above this one. An item with a null parent is a top-level item
"""
class Meta:
"""Metaclass defines extra model properties."""
abstract = True
class MPTTMeta:
"""Set insert order."""
order_insertion_by = ['name']
def validate_unique(self, exclude=None):
"""Validate that this tree instance satisfies our uniqueness requirements.
Note that a 'unique_together' requirement for ('name', 'parent') is insufficient,
as it ignores cases where parent=None (i.e. top-level items)
"""
super().validate_unique(exclude)
results = self.__class__.objects.filter(
name=self.name,
parent=self.parent
).exclude(pk=self.pk)
if results.exists():
raise ValidationError({
'name': _('Duplicate names cannot exist under the same parent')
})
def api_instance_filters(self):
"""Instance filters for InvenTreeTree models."""
return {
@ -539,18 +567,6 @@ class InvenTreeTree(MPTTModel):
for child in self.get_children():
child.save(*args, **kwargs)
class Meta:
"""Metaclass defines extra model properties."""
abstract = True
# Names must be unique at any given level in the tree
unique_together = ('name', 'parent')
class MPTTMeta:
"""Set insert order."""
order_insertion_by = ['name']
name = models.CharField(
blank=False,
max_length=100,

View File

@ -66,6 +66,11 @@ class PartCategory(MetadataMixin, InvenTreeTree):
default_keywords: Default keywords for parts created in this category
"""
class Meta:
"""Metaclass defines extra model properties"""
verbose_name = _("Part Category")
verbose_name_plural = _("Part Categories")
def delete_recursive(self, *args, **kwargs):
"""This function handles the recursive deletion of subcategories depending on kwargs contents"""
delete_parts = kwargs.get('delete_parts', False)
@ -154,11 +159,6 @@ class PartCategory(MetadataMixin, InvenTreeTree):
"are already assigned to it!"))
super().clean()
class Meta:
"""Metaclass defines extra model properties"""
verbose_name = _("Part Category")
verbose_name_plural = _("Part Categories")
def get_parts(self, cascade=True) -> set[Part]:
"""Return a queryset for all parts under this category.
@ -747,7 +747,7 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
return helpers.getBlankThumbnail()
def validate_unique(self, exclude=None):
"""Validate that a part is 'unique'.
"""Validate that this Part instance is 'unique'.
Uniqueness is checked across the following (case insensitive) fields:
- Name

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.16 on 2023-02-17 21:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('stock', '0092_alter_stockitem_updated'),
]
operations = [
migrations.AlterModelOptions(
name='stocklocation',
options={'verbose_name': 'Stock Location', 'verbose_name_plural': 'Stock Locations'},
),
]

View File

@ -47,6 +47,12 @@ class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree):
Stock locations can be hierarchical as required
"""
class Meta:
"""Metaclass defines extra model properties"""
verbose_name = _('Stock Location')
verbose_name_plural = _('Stock Locations')
def delete_recursive(self, *args, **kwargs):
"""This function handles the recursive deletion of sub-locations depending on kwargs contents"""
delete_stock_items = kwargs.get('delete_stock_items', False)