2009-07-30 9 views
22

Tengo la siguiente configuración de administrador para poder agregar/editar un usuario y su perfil al mismo tiempo.¿Cómo solicito una línea en Django Admin?

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active', 
     'last_login', 'delete_obj'] 
    list_display_links = ['username'] 
    list_filter = ['is_active'] 
    fieldsets = (
     (None, { 
      'fields': ('first_name', 'last_name', 'email', 'username', 
       'is_active', 'is_superuser')}), 
     ) 
    ordering = ['last_name', 'first_name'] 
    search_fields = ['first_name', 'last_name'] 

admin.site.register(User, UserProfileAdmin) 

El problema es que necesito dos de los campos en la forma de perfil en línea que se requieren al agregar al usuario. La forma en línea no valida a menos que se ingrese la entrada. ¿Hay alguna forma de hacer que el en línea sea requerido, para que no pueda dejarse en blanco?

Respuesta

29

seguí el consejo de Carl e hizo una mejor aplicación a continuación, el hack-ish he mencionado en mi comentario a su respuesta. Aquí está mi solución:

Desde mi forms.py:

from django.forms.models import BaseInlineFormSet 


class RequiredInlineFormSet(BaseInlineFormSet): 
    """ 
    Generates an inline formset that is required 
    """ 

    def _construct_form(self, i, **kwargs): 
     """ 
     Override the method to change the form attribute empty_permitted 
     """ 
     form = super(RequiredInlineFormSet, self)._construct_form(i, **kwargs) 
     form.empty_permitted = False 
     return form 

Y el admin.py

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 
    extra = 1 
    max_num = 1 
    formset = RequiredInlineFormSet 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    list_display = ['edit_obj', 'name', 'username', 'email', 'is_active', 
     'last_login', 'delete_obj'] 
    list_display_links = ['username'] 
    list_filter = ['is_active'] 
    fieldsets = (
     (None, { 
      'fields': ('first_name', 'last_name', 'email', 'username', 
       'is_active', 'is_superuser')}), 
     (('Groups'), {'fields': ('groups',)}), 
    ) 
    ordering = ['last_name', 'first_name'] 
    search_fields = ['first_name', 'last_name'] 


admin.site.register(User, UserProfileAdmin) 

Esto es exactamente lo que quiero, que hace que el perfil de validación en línea juego de formularios. Entonces, dado que hay campos obligatorios en el formulario de perfil, se validará y fallará si la información requerida no se ingresa en el formulario en línea.

+1

Si está utilizando 'GenericInlineModelAdmin', reemplace' BaseInlineFormSet' con 'BaseGenericInlineFormSet'. – L42y

+1

Gracias! Sin embargo, si su formulario no tiene campos obligatorios, aún no se validará y guardará cuando esté vacío. Monkey-patch el formulario usando 'form.has_changed = lambda: True' para tenerlo guardado a pesar de estar vacío. – bouke

9

probablemente usted puede hacer esto, pero usted tiene que ensuciarse las manos en el código juego de formularios/línea.

En primer lugar, creo que siempre debe haber un formulario en el formset y nunca más de uno, por lo que querrá establecer max_num = 1 y extra = 1 en su ProfileInline.

su problema central es que BaseFormSet._construct_form passes empty_permitted=True a cada formulario "extra" (es decir, vacío) en el juego de formularios. Este parámetro le dice a la forma que elude la validación si no cambia. Solo necesita encontrar una forma de establecer empty_permitted = False para el formulario.

Puede use your own BaseInlineFormset subclass en su línea, por lo que podría ayudar. Al darse cuenta de que _construct_form toma ** kwargs y permite que para anular las kwargs pasado a las instancias de forma individual, se puede anular _construct_forms en la subclase formset y han pasar empty_permitted = False en cada llamada a _construct_form. La desventaja es que dependes de API internas (y tendrías que volver a escribir _construct_forms).

Alternativamente, usted podría intentar reemplazando el método get_formset en su ProfileInline, y después de llamar get_formset de los padres, empuja manualmente en la forma en el interior del conjunto de formularios devueltos:

def get_formset(self, request, obj=None, **kwargs): 
    formset = super(ProfileInline, self).get_formset(request, obj, **kwargs) 
    formset.forms[0].empty_permitted = False 
    return formset 

Juega un poco y ver lo que puede hacer el trabajo !

+0

Gracias por la información.Encontré una solución, pero es muy hack-ish, y no estoy particularmente orgulloso de ello. Terminé anulando el add_view para ModelAdmin y copiando todo el código de la vista predeterminada y modificando los valores del formset. Voy a ver sus sugerencias para ver si puedo implementarlo de una manera más limpia. Gracias por los clientes potenciales! –

7

La forma más fácil y más natural de hacerlo es a través de fomset clean():

class RequireOneFormSet(forms.models.BaseInlineFormSet): 
    def clean(self): 
     super().clean() 
     if not self.is_valid(): 
      return 
     if not self.forms or not self.forms[0].cleaned_data: 
      raise ValidationError('At least one {} required' 
            .format(self.model._meta.verbose_name)) 

class ProfileInline(admin.StackedInline): 
    model = Profile 
    formset = RequireOneFormSet 

(Inspirado por this Matthew Flanagan's snippet y comentario de Mitar a continuación, probado para trabajar en Django 1.11 y 2.0).

+2

¡Perfecto! Lo cambié un poco, usé 'if not self.is_valid():' en lugar de pasar manualmente 'auto.errors' y usé' self.model._meta.verbose_name'. – Mitar

16

Ahora con Django 1.7 se puede utilizar el parámetro min_num. Ya no necesitas la clase RequiredInlineFormSet.

Ver https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.min_num

class ProfileInline(admin.StackedInline): 
    """ 
    Allows profile to be added when creating user 
    """ 
    model = Profile 
    extra = 1 
    max_num = 1 
    min_num = 1 # new in Django 1.7 


class UserProfileAdmin(admin.ModelAdmin): 
    """ 
    Options for the admin interface 
    """ 
    inlines = [ProfileInline] 
    ... 


admin.site.register(User, UserProfileAdmin) 
+0

Esto solo controla el número de líneas que se muestran. No requiere que se completen. – spookylukey

+5

En realidad, si en línea tiene uno de los campos requeridos, también controla que la línea en sí misma esté completa. – quick

+1

Trabajo confirmado :) – Geotob