Ошибка целостности django с UniqueConstraint - PullRequest
0 голосов
/ 05 августа 2020

У меня есть следующий фрагмент модели:

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:

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 (). UniqueConstraints без условия (т. Е. Неполные уникальные ограничения) отличаются в этом отношении тем, что они используют существующие validate_unique () logi c и, таким образом, обеспечивают двухэтапную проверку. В дополнение к IntegrityError при save (), ValidationError также возникает во время проверки модели при нарушении UniqueConstraint.

Поскольку у меня нет установленных условий, не следует ли сначала вызвать ValidationError?

1 Ответ

0 голосов
/ 05 августа 2020

Один из способов достичь такого же результата, как ваш, - сделать 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}
...