python - 使 ArrayField 的 base_field 对 Django 中的表是唯一的
问题描述
目标
以下应该提出一个ValidationError
>>> m1 = MyModel(names=['name1'])
>>> m2 = MyModel(names=['name1', 'name2'])
>>> m1.save()
>>> m2.save()
django.core.exceptions.ValidationError: ...
用简单的英语来说,如果模型ArrayField
中的元素与数据库表中的元素匹配,则ValidationError
应该引发
失败的解决方案
ArrayField 文档没有提到unique
关键字,所以我尝试了几种方法(这些是最小的代码示例)。
添加unique=True
到运行后base_field
似乎什么都没有makemigrations
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
我尝试unique=True
仅添加到ArrayField
. 如果数组完全相同,Django 会引发错误。
# 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
当我想到时,以上是有道理的。然后我尝试添加unique=True
到base_field
和中,ArrayField
但行为没有改变。我跑makemigrations
了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;博士
我可以使以下引发错误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)
...
推荐阅读
- python - 如何使用 Cloud Firestore 在 django 中获取/获取表单数据?
- python - 在excel python中存储来自for循环的数据
- matlab - 在 MATLAB 中的另一个图像上覆盖具有透明区域的 png 图像
- node.js - 连接 react native 和 node js
- python - 比较另一个数据框中一列数据框的值
- python - 从 tkinter 颜色名称中获取 rgb 值
- php - 警告:为 foreach() 提供的参数无效
- javascript - 以 dd.mm.yyyy 格式获取母亲节日期
- php - PDO 正在为空值插入“null”
- c# - 如何在 WPF 中切换单个 DataGridRow 的可见性?