python - Django - 验证模型和模型表单中计算字段的唯一性
问题描述
TL;DR我的模型和表单都计算了字段的值number_as_char
。我可以避免双重工作,但在使用没有表格的模型时仍然检查唯一性吗?
我使用 Python 3 和 Django 1.11
我的模型如下所示:
class Account(models.Model):
parent_account = models.ForeignKey(
to='self',
on_delete=models.PROTECT,
null=True,
blank=True)
number_suffix = models.PositiveIntegerField()
number_as_char = models.CharField(
max_length=100,
blank=True,
default='',
unique=True)
@classmethod
def get_number_as_char(cls, parent_account, number_suffix):
# iterate over all parents
suffix_list = [str(number_suffix), ]
parent = parent_account
while parent is not None:
suffix_list.insert(0, str(parent.number_suffix))
parent = parent.parent_account
return '-'.join(suffix_list)
def save(self, *args, **kwargs):
self.number_as_char = self.get_number_as_char(
self.parent_account, self.number_suffix)
super().save(*args, **kwargs)
该字段number_as_char
不应该由用户设置,因为它是根据 selected 计算的parent_account
:它是通过链接number_suffix
所有父帐户和当前实例的字段值获得的。
这是一个包含三个帐户的示例:
ac1 = Account()
ac1.parent_account = None
ac1.number_suffix = 2
ac1.save()
# ac1.number_as_char is '2'
ac2 = Account()
ac2.parent_account = ac1
ac2.number_suffix = 5
ac2.save()
# ac2.number_as_char is '2-5'
ac3 = Account()
ac3.parent_account = ac2
ac3.number_suffix = 1
ac3.save()
# ac3.number_as_char is '2-5-1'
删除该字段并改用模型属性不是一个选项,因为我需要确保唯一性并使用该字段对查询集进行排序order_by()
。
我的表格如下所示:
class AccountForm(forms.ModelForm):
class Meta:
model = Account
fields = [
'parent_account', 'number_suffix', 'number_as_char',
]
widgets = {
'number_as_char': forms.TextInput(attrs={'readonly': True}),
}
def clean(self):
super().clean()
self.cleaned_data['number_as_char'] = self.instance.get_number_as_char(
self.cleaned_data['parent_account'], self.cleaned_data['number_suffix'])
我number_as_char
在表单中包含了小部件属性readonly
,并使用表单clean()
方法进行计算number_as_char
(必须在验证唯一性之前计算)。
这一切都有效(模型和表单),但在验证表单后,number_as_char
将通过模型save()
方法再次计算 的值。问题不大,但是有没有办法避免这种双重计算呢?
- 如果我从 forms
clean()
方法中删除计算,则不会使用新值验证唯一性(它只会检查旧值)。 - 我不想完全从模型中删除计算,因为我在没有表格的其他部分使用模型。
您有什么建议可以采取不同的方式来避免重复计算该字段吗?
解决方案
我看不到在两个地方(save()
和clean()
)执行此操作的任何方法,因为您也需要它来处理非基于表单的保存)。
但是,我可以为您的方法提供两个效率改进get_number_as_char
:
将其设为 a
cached_property
以便第二次调用它时,您只需返回一个缓存值并消除重复计算。显然,您需要注意在更新实例之前number_as_char
不要调用它,否则旧的将被缓存。只要get_number_as_char()
仅在保存/清理期间调用,这应该没问题。根据您在上面提供的信息,您不必遍历所有祖先,而可以简单地获取
number_as_char
父级并附加到它。
以下包含两者:
@cached_property
def get_number_as_char(self, parent_account, number_suffix):
number_as_char = str(number_suffix)
if parent_account is not None:
number_as_char = '{}-{}'.format(parent_account.number_as_char, number_as_char)
return number_as_char
为确保缓存不会导致问题,您可以在完成保存后清除缓存值:
def save(self, *args, **kwargs):
self.number_as_char = self.get_number_as_char(
self.parent_account, self.number_suffix)
super().save(*args, **kwargs)
# Clear the cache, in case something edits this object again.
del self.get_number_as_char
推荐阅读
- sql - sql:如何删除某些列的所有行?
- airflow - Airflow 无法从 initdb 上的自定义数据模型创建表
- mysql - 如何将触发器从一个表复制到另一个表
- nginx - .php 文件被下载而不是显示
- java - 获取图表中经过的最长路线
- python - 为什么这个记录器不创建哨兵事件?
- python - 使用 requires_sync=True 复制 blob 时出现 AzureMissingResourceHttpError,除非容器是公共的
- ios - 在 SwiftUI NavigationView 中呈现 UINavigationBar 的旧小标题
- visual-studio - IIS Express 配置中的 allowdoubleescaping = "true" 不起作用
- c - Mac OSX 应该有一个“malloc.h”文件吗?