2009-02-20 18 views
91

Sto costruendo un'app per il monitoraggio dei ticket di supporto e ho alcuni modelli che vorrei creare da una pagina. I biglietti appartengono a un cliente tramite ForeignKey. Le note appartengono anche ai biglietti tramite ForeignKey. Mi piacerebbe avere l'opzione di selezionare un cliente (che è un progetto completamente separato) OPPURE creare un nuovo cliente, quindi creare un ticket e infine creare una nota assegnata al nuovo ticket.Django: più modelli in un modello utilizzando i moduli

Dato che sono abbastanza nuovo per Django, tendo a lavorare in modo iterativo, provando ogni volta nuove funzionalità. Ho giocato con ModelForms ma voglio nascondere alcuni campi e fare alcune convalide complesse. Sembra che il livello di controllo che sto cercando richieda formulari o esegua tutto manualmente, completo di una noiosa pagina di modello con codice a mano, che sto cercando di evitare.

C'è qualche caratteristica interessante che mi manca? Qualcuno ha un buon riferimento o esempio per l'utilizzo di formulari? Ho trascorso un intero weekend sui documenti API per loro e sono ancora senza tracce. È un problema di progettazione se abbatto e codice a mano tutto?

risposta

2

"Voglio nascondere alcuni campi e fare alcune convalide complesse."

Inizio con l'interfaccia di amministrazione integrata.

  1. Costruire il ModelForm per visualizzare i campi desiderati.

  2. Estendere il modulo con le regole di convalida all'interno del modulo. Di solito questo è un metodo clean.

    Assicurarsi che questa parte funzioni ragionevolmente bene.

Una volta eseguita questa operazione, è possibile spostarsi dall'interfaccia di amministrazione integrata.

Quindi si può scherzare con più moduli parzialmente correlati su una singola pagina web. Questo è un mucchio di materiale modello per presentare tutti i moduli su una singola pagina.

Quindi è necessario scrivere la funzione di visualizzazione per leggere e convalidare le varie forme e fare i vari oggetti salvati().

"È un problema di progettazione se si guasta e codice tutto manualmente?" No, è solo un sacco di tempo per non trarne molti vantaggi.

+0

Non so come, pertanto, non farlo – orokusaki

+1

@orokusaki: Che altro volete? Questo sembra descrivere una soluzione. Che altro dovrebbe essere detto? La domanda è vaga, quindi è difficile fornire un codice reale. Piuttosto che lamentarsi, si prega di fornire un suggerimento per il miglioramento. Che cosa suggerisci? –

71

Questo non è davvero difficile da implementare con ModelForms. Quindi diciamo che hai i moduli A, B e C. Stampi ciascuno dei moduli e della pagina e ora devi gestire il 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 sono i documenti per la convalida personalizzata.

+2

btw, non penso che i forms siano una buona soluzione al problema che hai descritto. Li ho sempre usati per rappresentare più istanze di un modello. Per esempio. hai un modulo per il richiedente e desideri 3 referenze per creare un formset che abbia 3 istanze del modello di riferimento. –

+0

nota che, nel modo in cui lo fai, la chiamata .is_valid() non è cortocircuitata. Se si desidera cortocircuitarlo, è necessario ritardare la chiamata della funzione .is_valid() fino a "and". –

22

Recentemente ho avuto qualche problema e ho appena capito come farlo. Supponendo di avere tre classi, primarie, B, C e che B, C hanno una chiave esterna per primaria

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, 
     }) 

Questo metodo dovrebbe permettere di fare tutto ciò che la convalida si richiede, così come la generazione di tutti e tre gli oggetti la stessa pagina. Ho anche utilizzato javascript e campi nascosti per consentire la generazione di più oggetti B, C sulla stessa pagina.

+2

In questo esempio, come si impostano le chiavi esterne per i modelli B e C per puntare al modello Primario? – User

+0

Ho solo due modelli che voglio mostrare sullo stesso modulo. Ma non ottengo l'istruzione exclude = ('primary',). Cos'è il primario? Se hai 2 modelli CustomerConfig e Contract. Contratto ha la chiave esterna per CustomerConfig. Come customer_config = models.ForeignKey ('CustomerPartnerConfiguration') Che cos'è 'primary'? – pitchblack408

62

ho appena è stato in circa la stessa situazione un giorno fa, e qui sono i miei 2 centesimi:

1) ho trovato forse la manifestazione più breve e concisa di entrata modello multiplo in forma singola qui: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/.

In breve: creare un modulo per ogni modello, inviarli entrambi a un modello in un singolo <form>, utilizzando prefix keyarg e avere la convalida di handle di visualizzazione. Se esiste una dipendenza, assicurati di salvare prima il modello "genitore" e utilizzare l'ID del genitore per la chiave esterna prima di eseguire il salvataggio del modello "figlio". Il link ha la demo.

2) Forse formsets possono essere battuti in questo modo, ma per quanto Scavai in, formsets sono principalmente per l'inserimento multipli dello stesso modello, che possono essere facoltativamente legato ad un altro modello/modelli da chiavi esterne. Tuttavia, sembra che non ci siano opzioni predefinite per l'inserimento di più dati di un modello e non è ciò per cui il formset sembra essere pensato.

3

Attualmente ho una soluzione funzionale (supera i miei test di unità). È una buona soluzione secondo me quando si desidera aggiungere solo un numero limitato di campi da altri modelli.

Mi manca qualcosa qui?

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

Il MultiModelForm da django-betterforms è un wrapper conveniente per fare ciò che è descritto nella Gnudiff's answer. Comprende i normali ModelForm s in una singola classe che è trasparente (almeno per l'utilizzo di base) utilizzata come modulo singolo. Ho copiato un esempio dai loro documenti qui sotto.

# 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 
Problemi correlati