[英]How to show database errors to user in Django Admin
背景:我的 Django 應用程序位於預先存在的 Postgresql 數據庫之上。 這個數據庫有一個非常復雜的觸發器和約束網絡。
問題:在 Django Admin 中,如果用戶在保存時導致數據庫錯誤,我想以用戶友好的格式向他們顯示錯誤,類似於內置的 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
。”
如果可能,您需要略微更改邏輯。 您需要的是自定義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()
里面沒有簡單的方法可以掛鈎,所以你必須復制粘貼部分表格准備代碼。
@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)。 如果有人有更好的方法,請告訴我。 我等着獎勵賞金。
嘗試這個:
from django.core.exceptions import ValidationError
def save_model(self, request, obj, form, change):
try:
obj.save()
except DatabaseError as e:
raise ValidationError(e)
老問題,但還沒有人提到ModelAdmin.message_user() ,它也使用 消息框架。
例如:
from django.contrib import admin, messages
class MyModelAdmin(admin.ModelAdmin):
...
def save_model(self, request, *args, **kwargs):
try:
super().save_model(request, *args, **kwargs)
except DatabaseError as e:
messages.set_level(request=request, level=messages.ERROR)
self.message_user(request=request, message=e, level=messages.ERROR)
這將顯示您的消息如下(假設這是e
中的錯誤字符串):
在save_model()中,您可以單獨和不同地處理DatabaseError異常以添加或更改 object ,如下所示:
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change): # Here
last_part_of_path = request.path.split('/')[-2]
if last_part_of_path == "add":
try: # ↓ I intentionally raise an exception
raise DatabaseError("Add Error")
obj.save()
except DatabaseError as e:
messages.set_level(request, messages.ERROR)
messages.error(request, e)
if last_part_of_path == "change":
try: # ↓ I intentionally raise an exception
raise DatabaseError("Change Error")
obj.save()
except DatabaseError as e:
messages.set_level(request, messages.ERROR)
messages.error(request, e)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.