Python 使ArrayField的base_字段对于Django中的表是唯一的 球门

Python 使ArrayField的base_字段对于Django中的表是唯一的 球门,python,django,Python,Django,以下内容应引发ValidationError 简而言之,如果模型的ArrayField中的元素与数据库表中的元素匹配,则应引发ValidationError 失败的解决方案 不要提到unique关键字,所以我尝试了几种方法,这些都是最少的代码示例 在运行makemigrations和migrate之后,将unique=True添加到base_字段似乎根本不起任何作用 我尝试只向ArrayField添加unique=True。如果数组完全相同,Django将引发错误 # models.py ...

以下内容应引发ValidationError

简而言之,如果模型的ArrayField中的元素与数据库表中的元素匹配,则应引发ValidationError

失败的解决方案 不要提到unique关键字,所以我尝试了几种方法,这些都是最少的代码示例

在运行makemigrations和migrate之后,将unique=True添加到base_字段似乎根本不起任何作用

我尝试只向ArrayField添加unique=True。如果数组完全相同,Django将引发错误

# models.py
...
class MyModel(models.Model)
    title = ArrayField(
        models.CharField(max_length=255),
        unique=True,
    )
当我想到这一点时,上面的话是有道理的。然后我尝试将unique=True添加到base_字段和ArrayField,但行为没有改变。我运行makemigrations和migrate

TL;博士 我是否可以使用unique=True使以下内容引发错误,或者我是否需要编写自己的验证器

>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1', 'name2'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...

这是我最后所做工作的一个最简单的例子

# 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)
    ...
# 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
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
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1', 'name2'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
# 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)
    ...