django - 带有UniqueConstraint的完整性错误django
问题描述
我有以下模型片段:
class InvoiceReference(TemplateMixin, models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
reference_prefix = models.CharField(
max_length=100,
validators=(RegexValidator(regex='^\S+$',
message='reference prefix must not include spaces'),)
)
reference_offset = models.PositiveIntegerField(default=0)
reference_suffix = models.CharField(
max_length=100,
validators=(RegexValidator(regex='^\S+$',
message='reference suffix must not include spaces'),)
)
reference_separator = models.CharField(max_length=1)
class Meta:
constraints = [
models.UniqueConstraint(fields=('user', 'reference_prefix', 'reference_suffix', 'reference_separator',), name='unique_reference')
]
我们的想法是让没有一个用户不能多次使用相同的reference_prefix、reference_separator和reference_suffix。(如果不同的用户使用相同的 reference_prefix、reference_separator 和 reference_suffix 组合,则可以。)
我目前正在使用 django-admin 模型表单,其中我已经排除了用户字段并在 save_model 中附加了一个用户:
class InvoiceReferenceAdmin(admin.ModelAdmin):
model = InvoiceReference
exclude = ('user',)
def save_model(self, request, obj, form, change):
if not obj.pk:
obj.user = request.user
super().save_model(request=request, obj=obj, form=form, change=change)
但是,当我尝试保存模型时,这会导致“完整性错误”。
追溯:
Traceback (most recent call last):
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/backends/sqlite3/base.py", line 383, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: invoice_invoice.user_id, invoice_invoice.reference_prefix, invoice_invoice.reference_suffix, invoice_invoice.reference_separator
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "django/core/handlers/exception.py", line 34, in inner
response = get_response(request)
File "django/core/handlers/base.py", line 115, in _get_response
response = self.process_exception_by_middleware(e, request)
File "django/core/handlers/base.py", line 113, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "django/contrib/admin/options.py", line 606, in wrapper
return self.admin_site.admin_view(view)(*args, **kwargs)
File "django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "django/contrib/admin/sites.py", line 223, in inner
return view(request, *args, **kwargs)
File "django/contrib/admin/options.py", line 1645, in add_view
return self.changeform_view(request, None, form_url, extra_context)
File "django/utils/decorators.py", line 45, in _wrapper
return bound_method(*args, **kwargs)
File "django/utils/decorators.py", line 142, in _wrapped_view
response = view_func(request, *args, **kwargs)
File "django/contrib/admin/options.py", line 1529, in changeform_view
return self._changeform_view(request, object_id, form_url, extra_context)
File "django/contrib/admin/options.py", line 1572, in _changeform_view
self.save_model(request, new_object, form, not add)
File "/Users/leonhughes/dev/django/invoiceweb/invoice/admin.py", line 42, in save_model
super().save_model(request=request, obj=obj, form=form, change=change)
File "django/contrib/admin/options.py", line 1088, in save_model
obj.save()
File "django/db/models/base.py", line 741, in save
force_update=force_update, update_fields=update_fields)
File "django/db/models/base.py", line 779, in save_base
force_update, using, update_fields,
File "django/db/models/base.py", line 870, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "django/db/models/base.py", line 908, in _do_insert
using=using, raw=raw)
File "django/db/models/manager.py", line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "django/db/models/query.py", line 1186, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File "django/db/models/sql/compiler.py", line 1375, in execute_sql
cursor.execute(sql, params)
File "django/db/backends/utils.py", line 99, in execute
return super().execute(sql, params)
File "django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "django/db/backends/utils.py", line 76, in _execute_with_wrappers
return executor(sql, params, many, context)
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/utils.py", line 89, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "django/db/backends/sqlite3/base.py", line 383, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: invoice_invoice.user_id, invoice_invoice.reference_prefix, invoice_invoice.reference_suffix, invoice_invoice.reference_separator
我在 django 文档中看到了这一点
通常在 full_clean() 期间不会检查约束,并且不会引发 ValidationErrors。相反,您将在 save() 上收到数据库完整性错误。没有条件的唯一约束(即非部分唯一约束)在这方面是不同的,因为它们利用现有的 validate_unique() 逻辑,从而启用两阶段验证。除了 save() 上的 IntegrityError 之外,当违反 UniqueConstraint 时,还会在模型验证期间引发 ValidationError。
由于我没有设置条件,不应该首先提出 ValidationError 吗?
解决方案
实现与您的结果相似的一种方法是user
在您的管理表单中创建一个隐藏字段,然后覆盖ModelAdmin.get_changeform_initial_data
以将当前用户作为此隐藏输入的值传递。
这意味着用户将被包含在唯一的验证检查中,因为该字段不会从表单中排除。编辑现有对象时忽略初始数据,因此您不应覆盖任何现有用户关系
class InvoiceReferenceAdminForm(forms.ModelForm):
class Meta:
model = InvoiceReference
fields = '__all__'
widgets = {'user': forms.HiddenInput}
class InvoiceReferenceAdmin(admin.ModelAdmin):
model = InvoiceReference
form = InvoiceReferenceAdminForm
def get_changeform_initial_data(self, request):
return {'user': request.user}
推荐阅读
- swift - 如何获取领域 LinkingObjects 的最小/最大值
- java - 如何使用 Swing 接口从键盘读取值
- amazon-web-services - 在流式传输到 lambda 之前,AWS 在 CloudWatch 中缓冲日志
- google-play - 使用 Google Play Developer API 创建新的应用列表
- r - 更新 R & R 包时闪亮的应用程序无法正常工作
- laravel - Laravel 电子邮件问候语显示名称而不是 ID
- javascript - 如何从对象数组中过滤数据
- django - 在 django 模板中拆分字符串
- angular - 如何设置服务的公共变量值,同时在 Angular 中定义提供
- c++ - 使用 fork() 和 boost::asio 的进程池