【问题标题】:Integrity Error django with UniqueConstraint带有UniqueConstraint的完整性错误django
【发布时间】:2020-08-04 22:00:40
【问题描述】:

我有以下型号sn-p:

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() 期间不会检查约束,并且不会 引发验证错误。相反,您会收到数据库完整性错误 在保存()。没有条件的 UniqueConstraints(即非部分 唯一约束)在这方面是不同的,因为它们 利用现有的 validate_unique() 逻辑,从而启用 两阶段验证。除了 save() 上的 IntegrityError, ValidationError 也会在模型验证期间引发,当 违反了 UniqueConstraint。

由于我没有设置条件,不应该首先引发 ValidationError 吗?

【问题讨论】:

  • 你能分享回溯吗?
  • 您已经为此('user', 'reference_prefix', 'reference_suffix', 'reference_separator') 组合添加了一条记录。
  • @WillemVanOnsem 你能解释更多吗?
  • @WillemVanOnsem 我了解,但如何在表单中将其显示为验证错误?如果我从 UniqueConstraint 中删除“用户”,我会收到表单验证错误。但是,对于“用户”,我会收到完整性错误。

标签: django django-models django-admin


【解决方案1】:

实现与您类似的结果的一种方法是在您的管理表单中将 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}

【讨论】:

  • 我以前见过类似的答案。我不希望使用自定义表单沿着这条路线走,这样可以将验证保存在一个地方以供系统的其他地方使用(最终在 django 管理员之外)
  • 此方法不干扰验证,只是一种隐藏字段的方法
猜你喜欢
  • 2019-09-06
  • 2013-03-18
  • 2015-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-06
  • 2012-11-15
相关资源
最近更新 更多