Issue
Goal
The following should raise a ValidationError
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1', 'name2'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
In plain English, if an element in a model's ArrayField
matches an element in the database table a ValidationError
should be raised
Failed Solutions
ArrayField docs don't mention the unique
keyword so I tried doing this a couple ways (these are minimal code examples).
Adding unique=True
to the base_field
didn't seem to the anything at all after running makemigrations
and migrate
# models.py
...
class MyModel(models.Model)
title = ArrayField(
models.CharField(max_length=255, unique=True),
)
...
# shell_plus from Django-extensions
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1'])
>>> m1.save()
>>> m2.save()
# no errors raised
I tried adding unique=True
to only the ArrayField
. Django raises an error if an array is exactly the same.
# models.py
...
class MyModel(models.Model)
title = ArrayField(
models.CharField(max_length=255),
unique=True,
)
# shell_plus from Django-extensions
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
>>> m3 = MyModel(names=['name1', 'name2'])
>>> m3.save()
# no error raised
The above makes sense when I think it about. I then tried to add unique=True
to both the base_field
and to the ArrayField
but the behavior didn't change. I ran makemigrations
and migrate
.
class MyModel(models.Model)
title = ArrayField(
models.CharField(max_length=255, unique=True),
unique=True,
)
# shell_plus from Django-extensions
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
>>> m3 = MyModel(names=['name1', 'name2'])
>>> m3.save()
# no error raised
TL;DR
Can I make the following raise an error with unique=True
or do I need to write my own validators?
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1', 'name2'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
Solution
This is a minimal example of what I ended up doing
# models.py
...
UNIQUE_ARRAY_FIELDS = ('name',)
class MyManager(models.Manager):
def prevent_duplicates_in_array_fields(self, model, array_field):
def duplicate_check(_lookup_params):
fields = self.model._meta.get_fields()
for unique_field in UNIQUE_ARRAY_FIELDS:
unique_field_index = [getattr(field, 'name', '') for field in fields]
try:
# if model doesn't have the unique field, then proceed to the next loop iteration
unique_field_index = unique_field_index.index(unique_field)
except ValueError:
continue
all_items_in_db = [item for sublist in self.values_list(fields[unique_field_index].name).exclude(**_lookup_params) for item in sublist]
all_items_in_db = [item for sublist in all_items_in_db for item in sublist]
if not set(array_field).isdisjoint(all_items_in_db):
raise ValidationError('{} contains items already in the database'.format(array_field))
if model.id:
lookup_params = {'id': model.id}
else:
lookup_params = {}
duplicate_check(lookup_params)
...
class MyModel(models.Model):
name = ArrayField(
models.CharField(max_length=255),
default=list
)
objects = MyManager()
...
def save(self, *args, **kwargs):
self.full_clean()
MyModel.objects.prevent_duplicates_in_array_fields(self, self.name)
super().save(*args, **kwargs)
...
Answered By - baqyoteto
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.