2010-04-27 8 views
5

Tengo dos modelos (ModelParent y ModelChild) con los mismos campos m2m en el modelo de asunto. ModelChild tiene una clave externa en ModelParent y ModelChild se define como en línea para ModelParent en la página de administración.django - ¿Cómo verificar Check ModelAdmin y sus líneas?

### 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) 

tengo una restricción importante, sin embargo, el campo de subjects_child ModelChild no debe hacer referencia a cualquier tema que subject_parent hace con su subjects_parent.

Entonces, si selecciono el mismo Asunto (en subject_parent y subject_child) en la página de administración para ambos modelos, ¿cómo puedo validar esto? Si solo cambia un campo, valídelo contra el db, pero ¿qué ocurre si ambos cambian (subject_parent y subject_child)? ¿Cómo puedo validar ambos formularios juntos antes de guardar?

+0

¿Por qué no validarlo en el clean() para el formulario, no en el field_specific clean? De esta forma, todos los campos que deberá verificar se habrán limpiado previamente. Lo único de lo que no estoy seguro es si puede alcanzar ambos conjuntos de datos, o solo los datos para el ModelParent en el clean() ... –

+1

Thx para sugerencia. Ya lo hice, pero con formularios solo puedo validar formularios principales (formularios para ModelParentAdmin) e inilines formset por separado (solo frente a db). El único lugar donde puedo acceder juntos es en la clase ModelParentAdmin. Pero esa clase no tiene un método clean(). Puedo pensar validarlos antes de svaing en el método save_formsets (...) de esta clase, pero al levantar ValidationError ("error") nada lo atrapa. – blazt

Respuesta

5

He heredado una nueva clase denominada ModelAdminWithInline de admin.ModelAdmin y modificado métodos add_view (...) y change_view (...) para llamar a la función is_cross_valid (auto, forma, formsets), donde puedes validar todos los formularios juntos. Ambas funciones tenían:

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

cambiado a:

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

La nueva función is_cross_valid (...) se define así:

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

por lo que la nueva clase debería funcionar exactamente lo mismo que ModelAdmin si no cambia la función is_cross_valid (...).

Ahora mi admin.py se parece a esto:

###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) 

La solución es un poco hacker pero funciona :). Los errores aparecen igual que con las clases regulares de ModelForm y ModelAdmin. Django 1.2 (que debería publicarse en breve) debería tener la validación del modelo, por lo que espero que este problema pueda resolverse mejor.

2

Las clases de administrador no tienen el método clean(). Sus formas lo hacen. Cada clase de administrador tiene un parámetro llamado formulario. Simplemente extienda el formulario predeterminado (es el formulario ModelAdmin normal), implemente el método clean() y agregue el formulario a la clase de administrador. Ejemplo:

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

Ya lo hice, pero con esto solo puedo validar los formularios contra el DB _separately_. ¿Qué ocurre si agrega el mismo asunto a ModelParent.subjects_parent y ModelChild.sujects_child en una sola página de administración (esto sucede con las líneas en línea, por supuesto). Un formulario no sabe que el otro ha seleccionado el mismo tema por lo que ambos se guardan. Esto se debe a que la validación de ambas formas ocurre antes de guardarse en la BD, por lo que ninguna de ellas ve los cambios que la otra hizo. – blazt

+0

Veo ahora. Esto complica las cosas un poco.Este nivel de protección debe implementarse en la capa, que ambas formas usan. No conozco la solución directa a su problema, pero este tipo de protección debe implementarse directamente en la capa de modelo o en el DBMS. Mire la documentación de django o, en el peor de los casos, la documentación de DBMS. – Klop

Cuestiones relacionadas