2013-11-15 96 views
7

背景:我的Django應用程序位於預先存在的Postgresql數據庫的頂部。這個數據庫有一個非常複雜的觸發器和約束網絡。如何在Django Admin中向用戶顯示數據庫錯誤

問題:在Django Admin中,如果用戶在保存時導致DatabaseError失敗,我想以一種用戶友好的格式將錯誤返回給他們,類似於內建的forms.ValidationError。

實施例(這不起作用,它會導致一個500)

def save_model(self, request, obj, form, change): 
    try: 
     obj.save() 
    except DatabaseError as e: 
     raise forms.ValidationError(e) 

預期結果:

顯示給用戶在管理員, 「Database Error: ID 58574 - Price is outside customers requested range. Cannot add or update a child row: a foreign key constraint fails」。

+0

'價格超出客戶要求range':你能告訴我在尋找的東西更通用,對於任何樣板工程模型(和您所指定的約束) – karthikr

+0

/約束。 – keithhackbarth

回答

4

@twil - 感謝您的幫助。你把我放在正確的軌道上。非常感謝您的幫助。但是,該解決方案並沒有開箱即用。實際上沒有在我的測試用例中顯示錯誤或者使用change_view。這是我最終想要的。

from django.contrib.admin import ModelAdmin 
from django.db import DatabaseError, IntegrityError 
from django.contrib import messages 


class ShowValidationAdmin(ModelAdmin): 

    def add_view(self, request, form_url='', extra_context=None): 
     try: 
      return super(ShowValidationAdmin, self).add_view(request, form_url, extra_context) 
     except (IntegrityError, DatabaseError) as e: 

      request.method = 'GET' 
      messages.error(request, e.message) 
      return super(ShowValidationAdmin, self).add_view(request, form_url, extra_context) 

    def change_view(self, request, object_id, form_url='', extra_context=None): 
     try: 
      return super(ShowValidationAdmin, self).change_view(request, object_id, form_url, extra_context) 
     except (IntegrityError, DatabaseError) as e: 

      request.method = 'GET' 
      messages.error(request, e.message) 
      return super(ShowValidationAdmin, self).change_view(request, object_id, form_url, extra_context) 

注意:這個版本似乎也適用於跨版本(django 1.3 - 1.6)。讓我知道是否有人有更好的方法。我會等待獎勵賞金。

+2

您的解決方案更短,但用戶友好性更低,因爲您正在使用'request.method ='GET''取消更改。用戶將不得不補充所有必要的字段,而不是隻修復錯誤的字段。 – twil

5

如果可能,您需要稍微更改邏輯。你需要的是自定義AdminModel.form。所有的驗證都應該在那裏完成。見注爲save_model()

ModelAdmin.save_model()和ModelAdmin.delete_model()必須保存/刪除 對象,他們不是否決的目的,相反,它們允許你 執行額外的操作。

但如果你的情況是這樣,你不能做表格內的所有驗證我會繼承ModelAdmin和覆蓋def add_view()def change_view()def changelist_view()像這樣:

from django.contrib import admin 
from django import forms 
from django.contrib.admin import helpers 
from django.contrib.admin.options import csrf_protect_m, IS_POPUP_VAR 
from django.utils.translation import ugettext as _ 
from django.utils.encoding import force_text 

# for nonfield errors to show correctly 
from django.forms.forms import NON_FIELD_ERRORS 

from .models import TestModel 


class TestModelAdmin(admin.ModelAdmin): 

    def save_model(self, request, obj, form, change): 

     raise Exception('test exception') 

    @csrf_protect_m 
    def add_view(self, request, form_url='', extra_context=None): 
     try: 
      return super(TestModelAdmin, self).add_view(request, form_url, extra_context) 
     except Exception as e: 
      pass 

     # mimic parent class on error 

     model = self.model 
     opts = model._meta 

     ModelForm = self.get_form(request) 
     formsets = [] 
     inline_instances = self.get_inline_instances(request, None) 
     form = ModelForm(request.POST, request.FILES) 
     form.is_valid() 

     # make faked nonfield error 
     # see http://stackoverflow.com/questions/8598247/how-to-append-error-message-to-form-non-field-errors-in-django 
     form._errors[NON_FIELD_ERRORS] = form.error_class([e.message]) 

     # We may handle exception here (just to save indentation) 
     adminForm = helpers.AdminForm(form, list(self.get_fieldsets(request)), 
      self.get_prepopulated_fields(request), 
      self.get_readonly_fields(request), 
      model_admin=self) 
     media = self.media + adminForm.media 

     inline_admin_formsets = [] 
     for inline, formset in zip(inline_instances, formsets): 
      fieldsets = list(inline.get_fieldsets(request)) 
      readonly = list(inline.get_readonly_fields(request)) 
      prepopulated = dict(inline.get_prepopulated_fields(request)) 
      inline_admin_formset = helpers.InlineAdminFormSet(inline, formset, 
       fieldsets, prepopulated, readonly, model_admin=self) 
      inline_admin_formsets.append(inline_admin_formset) 
      media = media + inline_admin_formset.media 

     context = { 
      'title': _('Add %s') % force_text(opts.verbose_name), 
      'adminform': adminForm, 
      'is_popup': IS_POPUP_VAR in request.REQUEST, 
      'media': media, 
      'inline_admin_formsets': inline_admin_formsets, 
      'errors': helpers.AdminErrorList(form, formsets), 
      'app_label': opts.app_label, 
      'preserved_filters': self.get_preserved_filters(request), 
     } 
     context.update(extra_context or {}) 
     return self.render_change_form(request, context, form_url=form_url, add=True) 

admin.site.register(TestModel, TestModelAdmin) 

models.py

from django.db import models 

class TestModel(models.Model): 

    text = models.TextField() 

你看,有沒有簡單的方法掛在save_model()裏面,所以你必須複製粘貼部分的表單準備代碼。

0

試試這個:

from django.core.exceptions import ValidationError 
    def save_model(self, request, obj, form, change): 
     try: 
      obj.save() 
     except DatabaseError as e: 
      raise ValidationError(e) 
+0

它在我的應用程序中拋出了500錯誤。 – d33tah

相關問題