Question

J'ai la configuration administrateur suivante pour pouvoir ajouter / modifier un utilisateur et son profil en même temps.

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)

Le problème est que j’ai besoin de deux des champs du formulaire en ligne Profil lors de l’ajout de l’utilisateur. Le formulaire en ligne ne valide pas sauf si une entrée est entrée. Y a-t-il un moyen de rendre l’inline requis, afin qu’il ne puisse pas être laissé en blanc?

Était-ce utile?

La solution

J’ai suivi le conseil de Carl et fait une mise en œuvre bien meilleure que celle de hack-ish que j’ai mentionnée dans mon commentaire pour sa réponse. Voici ma solution:

De mon formulaires.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

Et le 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)

Cela fait exactement ce que je veux, cela permet de valider l'ensemble de formulaires Profile inline. Donc, puisqu'il y a des champs obligatoires dans le formulaire de profil, il sera validé et échouera si les informations requises ne sont pas entrées dans le formulaire en ligne.

Autres conseils

Maintenant, avec Django 1.7, vous pouvez utiliser le paramètre min_num . Vous n'avez plus besoin de la classe RequiredInlineFormSet .

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

Vous pouvez probablement le faire, mais vous devrez vous salir les mains dans le code formset / inline.

Tout d'abord, je pense que vous voulez qu'il y ait toujours un formulaire dans le formset dans ce cas, et jamais plus d'un, de sorte que vous souhaitiez définir max_num = 1 et extra = 1 dans votre profilInline.

Votre principal problème est que BaseFormSet._construct_form passe empty_permitted = Vrai à chaque " extra " (c'est-à-dire vide) dans le formset. Ce paramètre indique au formulaire de contourner la validation si elle est inchangée. Vous devez juste trouver un moyen de définir empty_permitted = False pour le formulaire.

Vous pouvez utiliser votre propre sous-classe BaseInlineFormset dans votre en ligne, de sorte que cela pourrait aider. En remarquant que _construct_form prend ** kwargs et permet de remplacer les kwargs transmis aux instances de formulaire individuelles, vous pouvez remplacer _construct_forms dans votre sous-classe Formset et le faire passer à empty_permitted = False à chaque appel de _construct_form. L'inconvénient est que vous utilisez des API internes (et que vous devez réécrire _construct_forms).

Vous pouvez également essayer de remplacer la méthode get_formset sur votre ProfileInline, et après avoir appelé le get_formset du parent, pointez manuellement sur le formulaire à l'intérieur du formulaire retourné:

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

Jouez et voyez ce que vous pouvez faire pour travailler!

La méthode la plus simple et la plus naturelle consiste à utiliser 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

(Inspiré de ce Matthew L'extrait de Flanagan et le commentaire de Mitar ci-dessous, qui ont été testés pour fonctionner dans Django 1.11 et 2.0).

Vous devez définir min_num en ligne et validate_min dans l'ensemble de formulaires.

https://docs.djangoproject.com/ fr / 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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top