Pregunta

Tengo la siguiente configuración de administrador para poder agregar / editar un usuario y su perfil al mismo tiempo.

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 que dos de los campos del formulario en línea del perfil sean obligatorios al agregar el usuario. El formulario en línea no se valida a menos que se ingrese la entrada. ¿Hay alguna forma de hacer que la línea sea requerida, para que no se pueda dejar en blanco?

¿Fue útil?

Solución

Tomé el consejo de Carl e hice una implementación mucho mejor que la piratería que mencioné en mi comentario a su respuesta. Aquí está mi solución:

Desde mi formulario.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 hace exactamente lo que quiero, hace que se valide el formset en línea del perfil. 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.

Otros consejos

Ahora con Django 1.7 puede usar el parámetro min_num . Ya no necesita la clase RequiredInlineFormSet .

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

Probablemente puedas hacer esto, pero tendrás que ensuciarte las manos con el código de formset / inline.

En primer lugar, creo que desea que siempre haya una forma en el conjunto de formularios en este caso, y nunca más de una, así que querrá establecer max_num = 1 y extra = 1 en su ProfileInline.

Su problema principal es que BaseFormSet._construct_form pasa empty_permitido = Verdadero a cada " extra " (es decir, vacío) forma en el formset. Este parámetro le dice al formulario que omita la validación si no se modifica. Solo necesita encontrar una manera de establecer empty_permitted = False para el formulario.

Puede usar su propia subclase BaseInlineFormset en su en línea, por lo que podría ayudar. Al darse cuenta de que _construct_form toma ** kwargs y permite anular los kwargs pasados ??a las instancias de Formularios individuales, podría anular _construct_forms en su subclase de Formset y hacer que pase empty_permitted = False en cada llamada a _construct_form. La desventaja es que confías en las API internas (y tendrías que volver a escribir _construct_forms).

Como alternativa, puede intentar anular el método get_formset en su ProfileInline y, después de llamar a get_formset de los padres, presione manualmente el formulario dentro del formset devuelto:

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 y ve lo que puedes hacer funcionar!

La forma más fácil y 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 este Mateo El fragmento de Flanagan y el comentario de Mitar a continuación, probados para funcionar en Django 1.11 y 2.0).

Debes configurar min_num en línea y validate_min en formset.

https://docs.djangoproject.com/ es / 1.8 / topics / forms / formsets / # validate-min

class SomeInline(admin.TabularInline):
    ...
    min_num = 1

    def get_formset(self, request, obj=None, **kwargs):
        formset = super().get_formset(request, obj=None, **kwargs)
        formset.validate_min = True
        return formset
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top