2009-07-30 19 views
22

Mam następujące ustawienia administratora, aby móc dodawać/edytować użytkownika i jego profil w tym samym czasie.Jak wymagać wstawiania w Administratorze Django?

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) 

Problem polega na tym, że do dodania użytkownika potrzebne są dwa pola w formularzu śródtekstowym profilu. Formularz wbudowany nie jest sprawdzany, chyba że wprowadzono dane wejściowe. Czy mimo to konieczne jest wstawienie linijki, aby nie można było pozostawić pustą?

Odpowiedz

29

Wzięłam radę Carla i wprowadziłam znacznie lepszą implementację niż ta, o której wspominałem w swoim komentarzu do jego odpowiedzi. Oto moje rozwiązanie:

Z mojego 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 

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

to nie dokładnie to, co chcę, to sprawia validate profilu inline formset. W związku z tym, że w formularzu profilu są wymagane pola, zostanie on zatwierdzony i nie powiedzie się, jeśli wymagane informacje nie zostaną wprowadzone w formularzu śródliniowym.

+1

Jeśli używasz 'GenericInlineModelAdmin', zamień' BaseInlineFormSet' na 'BaseGenericInlineFormSet'. – L42y

+1

Dzięki! Jeśli jednak formularz nie zawiera wymaganych pól, nadal nie będzie sprawdzany i zapisywany, gdy będzie pusty. Naklej małpi formularz za pomocą 'form.has_changed = lambda: True', aby go zachować, mimo że jest pusty. – bouke

9

Prawdopodobnie możesz to zrobić, ale będziesz musiał zabrudzić sobie ręce w kodzie typu formset/inline.

Po pierwsze, myślę, że chcesz, aby zawsze była jedna forma w zestawie w tym przypadku, i nigdy więcej niż jeden, więc będziesz chciał ustawić max_num = 1 i extra = 1 w swojej linii profilu.

Twoim głównym problemem jest to, że BaseFormSet._construct_form passes empty_permitted=True dla każdej "dodatkowej" (tj. Pustej) postaci w zestawie. Ten parametr informuje formularz o pomijaniu sprawdzania poprawności, jeśli jest niezmieniony. Musisz tylko znaleźć sposób ustawienia empty_permitted = False dla formularza.

Możesz wstawić use your own BaseInlineFormset subclass w linii, co może pomóc. Zauważając, że _construct_form pobiera ** kwargs i pozwala to przesłonić kwargs przekazane do poszczególnych instancji Form, możesz przesłonić _construct_forms w podklasie Formset i przekazać empty_permitted = False w każdym wywołaniu _construct_form. Wadą jest to, że polegasz na wewnętrznych API (i musiałbyś przepisać _construct_forms).

Alternatywnie, można spróbować przesłanianie metody get_formset na swoim ProfileInline, a po wywołaniu get_formset rodzica, ręcznie grzebać w postaci wewnętrznej zwróconej formset:

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 

bawić i zobaczyć, co można zrobić pracę !

+0

dzięki za informacje.Wymyśliłem rozwiązanie, ale jest ono bardzo hackowe i nie jestem z niego szczególnie dumny. W efekcie zastąpiłem add_view dla ModelAdmin i skopiowałem cały kod z domyślnego widoku i modyfikując wartości zestawu. Rzucę okiem na twoje sugestie, aby zobaczyć, czy mogę wdrożyć je w czystszy sposób. Dzięki za wskazówki! –

7

Najprostszym i najbardziej naturalnym sposobem na to jest poprzez 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 

(Zainspirowany this Matthew Flanagan's snippet i komentarz Mitar jest poniżej, przetestowany do pracy w Django 1.11 i 2.0).

+2

Idealny! Zmieniłem ją trochę, użyłem 'if not self.is_valid():' zamiast ręcznie przechodzić 'self.errors' i używać' self.model._meta.verbose_name'. – Mitar

16

Teraz z Django 1.7 można użyć parametru min_num. Nie potrzebujesz już klasy RequiredInlineFormSet.

Zobacz 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

Kontroluje to tylko liczbę wyświetlanych rzędów. Nie wymaga to wypełniania żadnych. – spookylukey

+5

Faktycznie, jeśli wbudowany ma jedno z wymaganych pól, kontroluje również, czy samo w sobie jest wypełnione. – quick

+1

Potwierdzona praca :) – Geotob

Powiązane problemy