2010-04-27 14 views
5

Ich habe zwei Modelle (ModelParent und ModelChild) mit gleichen m2m Felder auf Subject-Modell. ModelChild hat einen Fremdschlüssel für ModelParent und ModelChild ist für ModelParent auf der Verwaltungsseite als Inline definiert.django - Wie man ModelAdmin und seine Inlines überprüfen?

### models.py ### 
    class Subject(Models.Model): 
    pass 

    class ModelParent(models.Model): 
    subjects_parent = ManyToManyField(Subject) 

    class ModelChild(models.Model): 
    parent = ForeignKey(ModelParent) 
    subjects_child = ManyToManyField(Subject) 

### admin.py ### 
    class ModelChildInline(admin.TabularInline): 
     model = ModelChild 

    class ModelParentAdmin(admin.ModelAdmin): 
    inlines = [ModelChildInline] 

    admin.site.register(ModelParent, ModelParentAdmin) 

ich eine wichtige Einschränkung habe verweist aber ModelChild der subjects_child Feld darf nicht jedes Thema, dass subject_parent mit seinem subjects_parent tut.

Also, wenn ich das gleiche Thema (in subject_parent und subject_child) auf der Admin-Seite für beide Modelle auswählen, wie kann ich dies überprüfen? Wenn nur ein Feld geändert wird, validieren Sie es gegen die db, aber was passiert, wenn sich beide ändern (subject_parent und subject_child)? Wie kann ich beide Formulare vor dem Speichern validieren?

+0

Warum validieren Sie es in der clean() für das Formular, nicht die field_specific sauber? Auf diese Weise werden alle Felder, die Sie überprüfen müssen, vorgereinigt. Die einzige Sache, die ich nicht sicher bin, ist, ob Sie beide Sätze von Daten oder nur die Daten für das ModelParent in der sauberen() ... –

+1

Thx für Vorschlag erreichen können. Ich habe das schon gemacht, aber mit Formularen kann ich nur das Hauptformular (Formular für ModelParentAdmin) und das Iniline-Formset separat validieren (nur gegen die db). Der einzige Ort, an dem ich auf sie zugreifen kann, ist die ModelParentAdmin-Klasse. Aber diese Klasse hat keine clean() -Methode. Ich kann mir vorstellen, sie zu validieren, bevor sie in der Methode save_formsets (...) dieser Klasse verwendet werden, aber wenn ValidationError ("error") ausgelöst wird, fängt es nichts ein. – blazt

Antwort

5

ich eine neue Klasse mit dem Namen ModelAdminWithInline von admin.ModelAdmin und modifizierte Methoden add_view geerbt haben (...) und change_view (...) Funktion is_cross_valid (self, Form, Formularsätze) zu nennen, Hier können Sie alle Formulare zusammen validieren. Beide Funktionen hatte:

#... 
if all_valid(formsets) and form_validated: 
#... 

geändert:

#... 
formsets_validated = all_valid(formsets) 
cross_validated = self.is_cross_valid(form, formsets) 
if formsets_validated and form_validated and cross_validated: 
#... 

Die neue Funktion is_cross_valid (...) wie folgt definiert ist:

def is_cross_valid(self, form, formsets): 
    return True 

so die neue Klasse sollte genau arbeiten das gleiche wie ModelAdmin, wenn Sie die Funktion is_cross_valid (...) nicht ändern.

Jetzt sieht mein admin.py wie folgt aus:

###admin.py### 
class ModelAdminWithInline(admin.ModelAdmin): 
    def is_cross_valid(self, form, formsets): 
    return True 

    def add_view(self, request, form_url='', extra_context=None): 
    #modified code 

    def change_view(self, request, object_id, extra_context=None): 
    #modified code 

class ModelChildInline(admin.TabularInline): 
    model = ModelChild 

class ModelParentAdmin(ModelAdminWithInline): 
    inlines = [ModelChildInline] 

    def is_cross_valid(self, form, formsets): 
    #Do some cross validation on forms 
    #For example, here is my particular validation: 
    valid = True 

    if hasattr(form, 'cleaned_data'): 

     subjects_parent = form.cleaned_data.get("subjects_parent") 

     #You can access forms from formsets like this: 
     for formset in formsets: 
     for formset_form in formset.forms: 
      if hasattr(formset_form, 'cleaned_data'): 

      subjects_child = formset_form.cleaned_data.get("subjects_child") 
      delete_form = formset_form.cleaned_data.get("DELETE") 

      if subjects_child and (delete_form == False): 
       for subject in subjects_child: 
       if subject in subjects_parent: 
        valid = False 
        #From here you can still report errors like in regular forms: 
        if "subjects_child" in formset_form.cleaned_data.keys(): 
        formset_form._errors["subjects_child"] = ErrorList([u"Subject %s is already selected in parent ModelParent" % subject]) 
        del formset_form.cleaned_data["subjects_child"] 
        else: 
        formset_form._errors["subjects_child"] += ErrorList(u"Subject %s is already selected in parent ModelParent" % subject]) 

     #return True on success or False otherwise. 
     return valid 

admin.site.register(ModelParent, ModelParentAdmin) 

Die Lösung ein wenig hackish ist, aber es funktioniert :). Die Fehler entsprechen den regulären Klassen ModelForm und ModelAdmin. Django 1.2 (welches in Kürze veröffentlicht werden sollte) sollte eine Modellvalidierung haben, also hoffe ich, dass dieses Problem dann besser gelöst werden kann.

2

Die Admin-Klassen haben keine clean() -Methode. Ihre Formen tun es. Jede Admin-Klasse hat einen Parameter namens form. Sie erweitern einfach das Standardformular (das normale ModelAdmin-Formular), implementieren Sie die clean() -Methode und fügen Sie das Formular zur admin-Klasse hinzu. Beispiel:

class SomeForm(ModelForm): 
    #some code 
    def clean(self): 
    #some code 
class SomeAdminClass(ModelAdmin): 
#some code 
form = SomeForm 
#more code 
+2

habe ich schon gemacht, aber damit kann ich nur die Formulare gegen die DB _separately_ validieren. Was passiert, wenn Sie ModelParent injects_parent und ModelChild.sujects_child auf einer Admin-Seite das gleiche Thema hinzufügen (dies geschieht natürlich mit Inlines). Ein Formular weiß nicht, dass das andere das gleiche Thema ausgewählt hat, so dass beide gerettet werden. Dies liegt daran, dass die Validierung beider Formulare vor dem Speichern in der Datenbank erfolgt, sodass keiner von ihnen die Änderungen des anderen sieht. – blazt

+0

Ich sehe jetzt. Dies verkompliziert die Dinge ein wenig.Diese Schutzebene muss in der Schicht implementiert werden, die beide Formulare verwenden. Ich kenne die direkte Lösung für Ihr Problem nicht, aber diese Art von Schutz muss entweder direkt in der Modellschicht oder im DBMS implementiert werden. Sehen Sie sich die Django-Dokumentation oder im schlimmsten Fall die DBMS-Dokumentation an. – Klop

Verwandte Themen