2013-11-15 5 views
7

Hintergrund: meine Django-Anwendung sitzt auf einer bereits vorhandenen Postgresql-Datenbank. Diese Datenbank hat ein sehr komplexes Netzwerk von Triggern und Einschränkungen.Wie Datenbankfehler Benutzer in Django Admin angezeigt werden

Frage: Wenn ein Benutzer im Django Admin beim Speichern einen DatabaseError verursacht, möchte ich den Fehler in einem benutzerfreundlichen Format anzeigen, ähnlich wie bei den eingebauten Forms.ValidationError.

Beispiel (das nicht funktioniert, es verursacht eine 500)

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

Erwartetes Ergebnis: "Database Error: ID 58574 - Price is outside customers requested range. Cannot add or update a child row: a foreign key constraint fails"

an Benutzer Gezeigte in Admin,

+0

'Preis außerhalb der Kunden angefordert range' ist: Können Sie die Modelle zeigen (und die Einschränkung von Ihnen angegebenen) – karthikr

+0

Ich bin auf der Suche nach etwas mehr Generika, die für jedes Modell funktioniert/Zwang. – keithhackbarth

Antwort

4

@twil - Danke für Ihre Hilfe. Sie bringen mich auf die richtige Spur. Schätze wirklich deine Hilfe. Die Lösung funktionierte jedoch nicht aus der Box. Habe in meinem Testfall keine Fehler angezeigt oder arbeite mit change_view. Hier ist der Wunsch, mit dem ich gearbeitet habe.

Hinweis: Diese Version scheint auch Cross-Version zu arbeiten (django 1.3 - 1.6). Lassen Sie mich wissen, ob jemand einen besseren Ansatz hat. Ich warte darauf, Kopfgeld zu vergeben.

+2

Ihre Lösung ist kürzer, aber weniger benutzerfreundlich, da Sie Änderungen mit 'request.method = 'GET' verlieren. Der Benutzer muss alle notwendigen Felder auffüllen und nicht nur die falschen korrigieren. – twil

5

Sie müssen Ihre Logik wenn möglich leicht ändern. Was Sie brauchen, ist benutzerdefinierte AdminModel.form. Alle Validierung sollte dort erfolgen. Siehe Hinweis für save_model():

ModelAdmin.save_model() und ModelAdmin.delete_model() muss speichern/löschen das Objekt, sie sind nicht für Veto Zwecke, sondern sie ermöglichen es Ihnen, zusätzliche Operationen ausführen.

Aber wenn Ihre Umstände sind so, dass Sie nicht alle Validierung in der Form tun kann ich ModelAdmin Unterklasse würde, und überschreiben def add_view(), def change_view() und def changelist_view() wie so:

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) 

Mein models.py:

from django.db import models 

class TestModel(models.Model): 

    text = models.TextField() 

Sie sehen, es gibt keine einfache Möglichkeit zum Einhängen innerhalb save_model(), so dass Sie Teil kopieren müssen Formularvorbereitungscode.

0

Try this:

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

Es warf einen 500 Fehler in meiner Anwendung. – d33tah

Verwandte Themen