验证使用内联管理员创建的相关模型的崩溃

验证使用内联管理员创建的相关模型的崩溃

问题描述:

我创建了一对一模型来扩展现有模型类型的功能,但我希望它仅允许在某些情况下创建扩展模型。我通过在Extended模型上的full_clean中投入ValidationError来强制执行此限制。如果我使用Extended的ModelAdmin直接创建扩展模型(它突出显示a字段,如果它是错误的类型),但是当我使用StackedInline联机Extended创建A的ModelAdmin和A是错误的类型时,赶上ValidationError和我得到的消息A server error occurred. Please contact the administrator.验证使用内联管理员创建的相关模型的崩溃

这是我怎么模型建立:

# models.py 
from django.db import models 

class A(models.Model): 
    type = models.IntegerField(...) 

class Extended(models.Model) 
    a = models.OneToOneField(A) 

    def clean_fields(self, **kwargs): 
     if self.a.type != 3: 
      raise ValidationError({'a': ["a must be of type 3"]}) 
     super(Extended, self).clean_fields(**kwargs) 

    def save(self, *args, **kwargs): 
     self.full_clean() 
     super(Extended, self).save(*args, **kwargs) 

# admin.py 
from django.contrib import admin 

class ExtendedInline(admin.StackedInline): 
    model = Extended 

@admin.register(A) 
class AAdmin(admin.ModelAdmin): 
    inlines = (ExtendedInline,) 

完整回溯:

Traceback (most recent call last): 
    File "/usr/local/lib/python2.7/wsgiref/handlers.py", line 85, in run 
    self.result = application(self.environ, self.start_response) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/staticfiles/handlers.py", line 63, in __call__ 
    return self.application(environ, start_response) 
    File "/usr/local/lib/python2.7/site-packages/whitenoise/base.py", line 66, in __call__ 
    return self.application(environ, start_response) 
    File "/usr/local/lib/python2.7/site-packages/django/core/handlers/wsgi.py", line 189, in __call__ 
    response = self.get_response(request) 
    File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 218, in get_response 
    response = self.handle_uncaught_exception(request, resolver, sys.exc_info()) 
    File "/usr/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 132, in get_response 
    response = wrapped_callback(request, *callback_args, **callback_kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py", line 618, in wrapper 
    return self.admin_site.admin_view(view)(*args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 110, in _wrapped_view 
    response = view_func(request, *args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func 
    response = view_func(request, *args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/sites.py", line 233, in inner 
    return view(request, *args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1521, in change_view 
    return self.changeform_view(request, object_id, form_url, extra_context) 
    File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 34, in _wrapper 
    return bound_func(*args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 110, in _wrapped_view 
    response = view_func(request, *args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 30, in bound_func 
    return func.__get__(self, type(self))(*args2, **kwargs2) 
    File "/usr/local/lib/python2.7/site-packages/django/utils/decorators.py", line 145, in inner 
    return func(*args, **kwargs) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1470, in changeform_view 
    self.save_related(request, form, formsets, not add) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1104, in save_related 
    self.save_formset(request, form, formset, change=change) 
    File "/usr/local/lib/python2.7/site-packages/django/contrib/admin/options.py", line 1092, in save_formset 
    formset.save() 
    File "/usr/local/lib/python2.7/site-packages/django/forms/models.py", line 636, in save 
    return self.save_existing_objects(commit) + self.save_new_objects(commit) 
    File "/usr/local/lib/python2.7/site-packages/django/forms/models.py", line 767, in save_new_objects 
    self.new_objects.append(self.save_new(form, commit=commit)) 
    File "/usr/local/lib/python2.7/site-packages/django/forms/models.py", line 900, in save_new 
    obj.save() 
    File "/code/app/models.py", line 162, in save 
    self.full_clean() 
    File "/usr/local/lib/python2.7/site-packages/django/db/models/base.py", line 1171, in full_clean 
    raise ValidationError(errors) 
ValidationError: {'a': [u'a must be of type 3']} 

我目前使用Django 1.8版

+0

您不应该像这样重写'clean_fields' - 它会阻止常规字段验证。该代码属于'clean()'方法。不过,我认为这不会解决您的问题。 – Alasdair

+0

@Alasdair我也叫'super(...)。clean_fields',但我没有在这个例子中包含它。 – frank

虽然不理想,spinkus发布an answer这也是固定我的崩溃。它涉及到在重写的AAdmin方法changeform_view

@admin.register(A) 
class AAdmin(admin.ModelAdmin): 
    inlines = (ExtendedInline,) 

    def changeform_view(self, request, object_id=None, form_url='', extra_context=None): 
     # Need to override to catch ValidationError in pre_save and save hooks. 
     try: 
      return super(AAdmin, self).changeform_view(request, object_id, form_url, extra_context) 
     except ValidationError as e: 
      self.message_user(request, '\n'.join(e.messages), level=messages.ERROR) 
      return HttpResponseRedirect(form_url) 

这导致AAdmin在表格的顶部,而不是崩溃来显示错误消息。不幸的是,它清除了用户的其他更改,并且不会执行字段级突出显示。

+0

如果这项工作,你可以接受你自己的答案! – ppython

在模型验证程序中发生的对full_clean()的调用中不包含内联外​​键,因此您的ValidationError未被表单的is_valid()调用捕获。

从Django中/表格/ models.py:

def _post_clean(self): 
    opts = self._meta 

    exclude = self._get_validation_exclusions() 

    try: 
     self.instance = construct_instance(self, self.instance, opts.fields, exclude) 
    except ValidationError as e: 
     self._update_errors(e) 

    # Foreign Keys being used to represent inline relationships 
    # are excluded from basic field value validation. This is for two 
    # reasons: firstly, the value may not be supplied (#12507; the 
    # case of providing new values to the admin); secondly the 
    # object being referred to may not yet fully exist (#12749). 
    # However, these fields *must* be included in uniqueness checks, 
    # so this can't be part of _get_validation_exclusions(). 
    for name, field in self.fields.items(): 
     if isinstance(field, InlineForeignKeyField): 
      exclude.append(name) 

    try: 
     self.instance.full_clean(exclude=exclude, validate_unique=False) 
    except ValidationError as e: 
     self._update_errors(e) 

    # Validate uniqueness if needed. 
    if self._validate_unique: 
     self.validate_unique() 

他们被夹在保存(),而不是(你叫不排除full_clean),这是为时已晚。

将您的验证清理()代替:

def clean(self): 
    if self.a.type != 3: 
     raise ValidationError({'a': ["a must be of type 3"]}) 

那么就没有必要从您保存方法调用full_clean。这种方法是任何验证这种类型应该去as per the docs