Django: wiele modeli w jednym szablonie przy użyciu formularzy [zamknięte]

114

Tworzę aplikację do śledzenia zgłoszeń do pomocy technicznej i mam kilka modeli, które chciałbym utworzyć na jednej stronie. Bilety należą do klienta za pośrednictwem klucza obcego. Notatki należą również do biletów za pośrednictwem klucza obcego. Chciałbym mieć możliwość wyboru Klienta (to jest cały oddzielny projekt) LUB utworzenie nowego Klienta, następnie utworzenie Biletu i na koniec utworzenie Notatki przypisanej do nowego zgłoszenia.

Ponieważ jestem dość nowy w Django, mam tendencję do pracy iteracyjnej, za każdym razem wypróbowując nowe funkcje. Grałem z ModelForms, ale chcę ukryć niektóre pola i przeprowadzić złożoną walidację. Wygląda na to, że poziom kontroli, którego szukam, wymaga zestawów formularzy lub robienia wszystkiego ręcznie, wraz z żmudną, ręcznie kodowaną stroną szablonu, której staram się unikać.

Czy brakuje mi jakiejś uroczej funkcji? Czy ktoś ma dobre referencje lub przykład korzystania z zestawów formularzy? Spędziłem cały weekend nad dokumentacją API dla nich i nadal nie mam pojęcia. Czy to problem projektowy, jeśli zepsuję i ręcznie zakoduję wszystko?

neoice
źródło
najpierw należy zweryfikować formularz klienta i jeśli był ważny, utworzyć kopię z request.POST (new_data = request.POST.copy ()). a następnie uzyskać identyfikator klienta (z zatwierdzonego formularza klienta) i aktualizując nowe_dane, identyfikator klienta wartość do pola klucza obcego (może klient w Twoim modelu). i na koniec rozważ new_data, aby zweryfikować swój drugi formularz (bilety)
Negar37,

Odpowiedzi:

87

To naprawdę nie jest zbyt trudne do zaimplementowania z ModelForms . Powiedzmy, że masz formularze A, B i C. Drukujesz każdy z formularzy i stronę, a teraz musisz obsłużyć POST.

if request.POST():
    a_valid = formA.is_valid()
    b_valid = formB.is_valid()
    c_valid = formC.is_valid()
    # we do this since 'and' short circuits and we want to check to whole page for form errors
    if a_valid and b_valid and c_valid:
        a = formA.save()
        b = formB.save(commit=False)
        c = formC.save(commit=False)
        b.foreignkeytoA = a
        b.save()
        c.foreignkeytoB = b
        c.save()

Oto dokumenty do niestandardowej weryfikacji.

Jason Christa
źródło
2
przy okazji, nie sądzę, aby zestawy formularzy były dobrym rozwiązaniem opisanego problemu. Zawsze używałem ich do reprezentowania wielu instancji modelu. Np. Masz formularz kandydata i chcesz, aby 3 odwołania do Ciebie utworzyły zestaw formularzy zawierający 3 wystąpienia modelu referencyjnego.
Jason Christa,
1
zwróć uwagę, że sposób, w jaki to robisz, nie powoduje zwarcia wywołania .is_valid (). Jeśli chcesz go zewrzeć, musisz opóźnić wywołanie funkcji .is_valid () do momentu pojawienia się „i”.
Lie Ryan
66

Byłem mniej więcej w tej samej sytuacji dzień temu, a oto moje 2 centy:

1) Znalazłem prawdopodobnie najkrótszą i najbardziej zwięzłą demonstrację wielokrotnego wpisu modelu w pojedynczej formie: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .

W skrócie: stwórz formularz dla każdego modelu, prześlij je do szablonu w jednym <form>, używając prefixkeyarg i miej walidację uchwytu widoku. Jeśli istnieje zależność, po prostu upewnij się, że zapisałeś model „nadrzędny” przed zależnością i użyj identyfikatora rodzica dla klucza obcego przed zatwierdzeniem zapisania modelu „potomnego”. Link zawiera demo.

2) Być może zestawy formularzy dają się do tego przebić, ale o ile się zagłębiłem, zestawy formularzy służą głównie do wprowadzania wielokrotności tego samego modelu, który można opcjonalnie powiązać z innym modelem / modelami za pomocą obcych kluczy. Wydaje się jednak, że nie ma domyślnej opcji wprowadzania danych więcej niż jednego modelu i wydaje się, że nie do tego służy zestaw form.

Gnudiff
źródło
26

Niedawno miałem problem i właśnie wymyśliłem, jak to zrobić. Zakładając, że masz trzy klasy, Primary, B, C i że B, C mają klucz obcy do podstawowego

    class PrimaryForm(ModelForm):
        class Meta:
            model = Primary

    class BForm(ModelForm):
        class Meta:
            model = B
            exclude = ('primary',)

    class CForm(ModelForm):
         class Meta:
            model = C
            exclude = ('primary',)

    def generateView(request):
        if request.method == 'POST': # If the form has been submitted...
            primary_form = PrimaryForm(request.POST, prefix = "primary")
            b_form = BForm(request.POST, prefix = "b")
            c_form = CForm(request.POST, prefix = "c")
            if primary_form.is_valid() and b_form.is_valid() and c_form.is_valid(): # All validation rules pass
                    print "all validation passed"
                    primary = primary_form.save()
                    b_form.cleaned_data["primary"] = primary
                    b = b_form.save()
                    c_form.cleaned_data["primary"] = primary
                    c = c_form.save()
                    return HttpResponseRedirect("/viewer/%s/" % (primary.name))
            else:
                    print "failed"

        else:
            primary_form = PrimaryForm(prefix = "primary")
            b_form = BForm(prefix = "b")
            c_form = Form(prefix = "c")
     return render_to_response('multi_model.html', {
     'primary_form': primary_form,
     'b_form': b_form,
     'c_form': c_form,
      })

Ta metoda powinna umożliwić wykonanie dowolnej weryfikacji wymaganej, a także wygenerowanie wszystkich trzech obiektów na tej samej stronie. Użyłem również javascript i pól ukrytych, aby umożliwić generowanie wielu obiektów B, C na tej samej stronie.


źródło
3
W tym przykładzie, jak ustawiasz klucze obce dla modeli B i C, aby wskazywały model podstawowy?
Użytkownik
Mam tylko dwa modele, które chcę pokazać na tym samym formularzu. Ale nie otrzymuję instrukcji exclude = ('primary',). Co jest podstawowe? Jeśli masz 2 modele CustomerConfig i Contract. Kontrakt ma klucz obcy do CustomerConfig. Na przykład customer_config = models.ForeignKey („CustomerPartnerConfiguration”) Co to jest „podstawowa”?
pitchblack408
10

MultiModelForm ze django-betterformsto wygodny wrapper robić to, co jest opisane w odpowiedzi Gnudiff użytkownika . Otacza zwykłe litery ModelFormw jedną klasę, która jest przezroczysta (przynajmniej do podstawowego użytku) używana jako pojedyncza forma. Skopiowałem przykład z ich dokumentów poniżej.

# forms.py
from django import forms
from django.contrib.auth import get_user_model
from betterforms.multiform import MultiModelForm
from .models import UserProfile

User = get_user_model()

class UserEditForm(forms.ModelForm):
    class Meta:
        fields = ('email',)

class UserProfileForm(forms.ModelForm):
    class Meta:
        fields = ('favorite_color',)

class UserEditMultiForm(MultiModelForm):
    form_classes = {
        'user': UserEditForm,
        'profile': UserProfileForm,
    }

# views.py
from django.views.generic import UpdateView
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
from django.contrib.auth import get_user_model
from .forms import UserEditMultiForm

User = get_user_model()

class UserSignupView(UpdateView):
    model = User
    form_class = UserEditMultiForm
    success_url = reverse_lazy('home')

    def get_form_kwargs(self):
        kwargs = super(UserSignupView, self).get_form_kwargs()
        kwargs.update(instance={
            'user': self.object,
            'profile': self.object.profile,
        })
        return kwargs
jozxyqk
źródło
Właśnie zobaczyłem django-betterformsi jego klasę MultiModelForm, zanim natknąłem się na odpowiedź. Ich rozwiązanie wygląda naprawdę dobrze, ale wygląda na to, że nie było aktualizowane od jakiegoś czasu. Czy nadal używasz tego @jozxyqk? Jakieś problemy?
enchance
@enchance minęło już kilka lat. Wtedy uznałem to za wygodne i jedną z lepszych opcji. Jeśli nie masz ochoty na to, oszczędza trochę czasu. Mogę sobie wyobrazić, że kiedy chcesz zacząć dostosowywać i robić nietrywialne formularze, łatwiej byłoby stworzyć własne. Łatwe mieszanie form i kontekstów w widokach to pierwsza funkcja, której naprawdę brakuje mi w django.
jozxyqk
Dzięki za odpowiedź. Rozważam po prostu rozwidlenie tego i może zaktualizuję kilka rzeczy po drodze. Z tego, co widziałem do tej pory, działa dobrze. Masz rację, to ogromna oszczędność czasu.
enchance
5

Obecnie mam funkcjonalne obejście problemu (przechodzi moje testy jednostkowe). Moim zdaniem to dobre rozwiązanie, gdy chcesz dodać tylko ograniczoną liczbę pól z innych modeli.

Czy coś mi umyka ?

class UserProfileForm(ModelForm):
    def __init__(self, instance=None, *args, **kwargs):
        # Add these fields from the user object
        _fields = ('first_name', 'last_name', 'email',)
        # Retrieve initial (current) data from the user object
        _initial = model_to_dict(instance.user, _fields) if instance is not None else {}
        # Pass the initial data to the base
        super(UserProfileForm, self).__init__(initial=_initial, instance=instance, *args, **kwargs)
        # Retrieve the fields from the user model and update the fields with it
        self.fields.update(fields_for_model(User, _fields))

    class Meta:
        model = UserProfile
        exclude = ('user',)

    def save(self, *args, **kwargs):
        u = self.instance.user
        u.first_name = self.cleaned_data['first_name']
        u.last_name = self.cleaned_data['last_name']
        u.email = self.cleaned_data['email']
        u.save()
        profile = super(UserProfileForm, self).save(*args,**kwargs)
        return profile
Paul Bormans
źródło
3

„Chcę ukryć niektóre pola i przeprowadzić skomplikowaną weryfikację”.

Zaczynam od wbudowanego interfejsu administratora.

  1. Zbuduj ModelForm, aby wyświetlić żądane pola.

  2. Rozszerz formularz o reguły walidacji w formularzu. Zwykle jest to cleanmetoda.

    Upewnij się, że ta część działa dość dobrze.

Gdy to zrobisz, możesz odejść od wbudowanego interfejsu administratora.

Następnie możesz wygłupiać się z wieloma, częściowo powiązanymi formularzami na jednej stronie internetowej. To jest zestaw szablonów do prezentacji wszystkich formularzy na jednej stronie.

Następnie musisz napisać funkcję widoku, aby odczytać i zweryfikować różne elementy formularza i wykonać różne obiekty saves ().

„Czy to problem projektowy, jeśli zepsuję i ręcznie zakoduję wszystko?” Nie, to po prostu dużo czasu bez większych korzyści.

S.Lott
źródło
Nie wiem jak, więc nie rób tego
orokusaki
1
@orokusaki: Czego więcej byś chciał? To wydaje się opisywać rozwiązanie. Co więcej należy powiedzieć? Pytanie jest niejasne, więc trudno jest podać rzeczywisty kod. Zamiast narzekać, przedstaw sugestie dotyczące ulepszeń. Co sugerujesz?
S.Lott,