2009-02-20 11 views
91

Buduję aplikację do śledzenia biletów wsparcia i mam kilka modeli, które chciałbym utworzyć z jednej strony. Bilety należą do klienta za pośrednictwem klucza ForeignKey. Uwagi należą również do biletów za pośrednictwem klucza obcego. Chciałbym mieć możliwość wyboru klienta (to zupełnie oddzielny projekt) LUB stworzenie nowego klienta, a następnie utworzenie biletu i wreszcie utworzenie notatki przypisanej do nowego biletu.Django: wiele modeli w jednym szablonie przy użyciu formularzy

Ponieważ jestem dość nowy w Django, staram się pracować iteracyjnie, wypróbowując nowe funkcje za każdym razem. Grałem z ModelForms, ale chcę ukryć niektóre pola i przeprowadzić skomplikowaną walidację. Wygląda na to, że poziom kontroli, jakiej szukam, wymaga formformacji lub robienia wszystkiego ręcznie, wraz z nużącą, ręcznie kodowaną stroną szablonu, której staram się uniknąć.

Czy brakuje mi jakiejś pięknej funkcji? Czy ktoś ma dobre referencje lub przykład użycia formsetów? Spędziłem cały weekend na dokumentach API dla nich i wciąż nie mam pojęcia. Czy jest to problem z projektowaniem, jeśli się rozpadnę i wszystko koduję ręcznie?

Odpowiedz

2

"Chcę ukryć niektóre pola i przeprowadzić skomplikowaną walidację."

Zacznę od wbudowanego interfejsu administratora.

  1. Utwórz model, aby wyświetlić żądane pola.

  2. Rozszerz formularz o zasady sprawdzania poprawności w formularzu. Zwykle jest to metoda clean.

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

Po wykonaniu tej czynności można odejść od wbudowanego interfejsu administratora.

Następnie można wygłupiać z wieloma, częściowo pokrewnymi formularzami na jednej stronie internetowej. Jest to zestaw szablonów do przedstawienia wszystkich formularzy na jednej stronie.

Następnie należy napisać funkcję widoku, aby odczytać i zweryfikować różne elementy formularza i wykonać różne zapisy obiektów().

"Czy to jest kwestia projektowania, jeśli się rozpadnę i wszystko koduję ręcznie?" Nie, to tylko dużo czasu, żeby nie przynieść wiele korzyści.

+0

Nie wiem jak to zrobić, więc nie rób tego – orokusaki

+1

@orokusaki: Co jeszcze chcesz? To wydaje się opisywać rozwiązanie. Co jeszcze należy powiedzieć? Pytanie jest niejasne, więc trudno podać rzeczywisty kod. Zamiast narzekać, proszę podać sugestię ulepszeń. Co sugerujesz? –

71

To naprawdę nie jest trudne do wdrożenia z ModelForms. Więc 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() 

Here są dokumentami do niestandardowej weryfikacji.

+2

btw, nie sądzę, że formsets są dobrym rozwiązaniem problemu, który opisałeś. Zawsze używałem ich do reprezentowania wielu instancji modelu. Na przykład. masz formularz wnioskodawcy i chcesz 3 odniesienia do ciebie utworzyć zestaw składający się z 3 instancji modelu referencyjnego. –

+0

Zauważ, że w zależności od sposobu, wywołanie .is_valid() nie jest zwarte. Jeśli chcesz go sprowadzić, będziesz musiał opóźnić wywołanie funkcji .is_valid() aż do "i". –

22

Niedawno miałem pewien problem i po prostu zorientowałem się, jak to zrobić. Zakładając, że mamy trzy klasy podstawowej, B, C i B, C posiada klucz obcy do pierwotnego

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 pozwoli Ci zrobić cokolwiek walidacji wymagają, a także generowanie wszystkich trzech przedmiotów ta sama strona. Użyłem również javascript i ukrytych pól, aby umożliwić generowanie wielu obiektów B, C na tej samej stronie.

+2

W tym przykładzie w jaki sposób ustawiasz klucze obce dla modeli B i C, aby wskazywały na model podstawowy? – User

+0

Mam tylko dwa modele, które chcę pokazać w tym samym formularzu. Ale nie otrzymuję instrukcji exclude = ("primary",). Co to jest podstawowa? Jeśli masz 2 modele CustomerConfig i Contract. Kontrakt ma klucz obcy do CustomerConfig. Takich jak customer_config = models.ForeignKey ("CustomerPartnerConfiguration") Co to jest "primary"? – pitchblack408

62

właśnie było w około tej samej sytuacji dzień temu, i tutaj są moje 2 centy:

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

W skrócie: Utwórz formularz dla każdego modelu, prześlij oba do szablonu w jednym kluczu <form>, używając klucza kluczowego prefix i sprawdź poprawność uchwytów widoku. Jeśli istnieje zależność, po prostu upewnij się, że zapisałeś model "macierzysty" przed zależnością i używaj identyfikatora nadrzędnego dla klucza obcego przed zatwierdzeniem zapisu modelu "podrzędnego". Link ma wersję demonstracyjną.

2) Możliwe, że formsery mogą zostać pobite w ten sposób, ale w miarę, jak zagłębiałem się, formsety są przede wszystkim do wprowadzania wielokrotności tego samego modelu, który może być opcjonalnie związany z innym modelem/modelami za pomocą kluczy obcych. Jednak wydaje się, że nie ma domyślnej opcji wprowadzania więcej niż jednego modelu danych i to nie jest to, do czego wydaje się być przeznaczony zestaw.

3

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

Czy tu czegoś brakuje?

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 
8

MultiModelForm z django-betterforms to wygodny wrapper robić to, co jest opisane w Gnudiff's answer. Oplata regularne ModelForm s w jednej klasie, która jest przezroczysta (przynajmniej dla podstawowego użycia) 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 
Powiązane problemy