Django: più modelli in un modello utilizzando i moduli [chiuso]


114

Sto creando un'app di monitoraggio dei ticket di supporto e ho alcuni modelli che vorrei creare da una pagina. I biglietti appartengono a un cliente tramite una ForeignKey. Le note appartengono anche ai biglietti tramite una ForeignKey. Vorrei avere la possibilità di selezionare un cliente (che è un progetto completamente separato) OPPURE creare un nuovo cliente, quindi creare un biglietto e infine creare una nota assegnata al nuovo biglietto.

Dato che sono abbastanza nuovo su Django, tendo a lavorare in modo iterativo, provando ogni volta nuove funzionalità. Ho giocato con ModelForms ma voglio nascondere alcuni campi ed eseguire una convalida complessa. Sembra che il livello di controllo che cerco richieda dei formet o che faccia tutto a mano, completo di una pagina modello noiosa e codificata a mano, che sto cercando di evitare.

C'è qualche bella caratteristica che mi manca? Qualcuno ha un buon riferimento o un esempio per l'utilizzo dei formset? Ho trascorso un intero fine settimana sui documenti API per loro e sono ancora all'oscuro. È un problema di progettazione se scompongo tutto e codifico manualmente tutto?


in un primo momento dovresti convalidare il tuo modulo cliente e se era valido, creane una copia da request.POST (new_data = request.POST.copy ()). e poi ottieni l'ID cliente (dal modulo cliente convalidato) e con l'aggiornamento new_data, fai ID cliente un valore per il campo foreignkey (forse cliente nel tuo modello). Infine, considera new_data per convalidare il tuo secondo modulo (biglietti)
Negar37

Risposte:


87

Questo non è davvero troppo difficile da implementare con ModelForms . Quindi supponiamo che tu abbia i moduli A, B e C.Stampi ciascuno dei moduli e la 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()

Ecco i documenti per la convalida personalizzata.


2
a proposito, non penso che i formset siano una buona soluzione al problema che hai descritto. Li ho sempre usati per rappresentare più istanze di un modello. Ad esempio, hai un modulo di candidatura e vuoi 3 riferimenti per creare un formset che ha 3 istanze del modello di riferimento.
Jason Christa,

1
nota che, con il modo in cui lo fai, la chiamata .is_valid () non è in cortocircuito. Se vuoi cortocircuirlo, dovrai ritardare la chiamata della funzione .is_valid () fino a quando non vengono visualizzati "e".
Lie Ryan

66

Ero nella stessa situazione un giorno fa, ed ecco i miei 2 centesimi:

1) Ho trovato probabilmente la dimostrazione più breve e concisa di immissione di più modelli in forma singola qui: http://collingrady.wordpress.com/2008/02/18/editing-multiple-objects-in-django-with-newforms/ .

In poche parole: creare un modulo per ogni modello, inviarli entrambi al modello in un unico <form>, utilizzando prefixkeyarg e avere la convalida dell'handle di visualizzazione. Se c'è dipendenza, assicurati di salvare il modello "genitore" prima di dipendente e usa l'ID genitore per la chiave esterna prima di eseguire il salvataggio del modello "figlio". Il collegamento ha la demo.

2) Forse i formset possono essere battuti per farlo, ma per quanto ho approfondito, i formset sono principalmente per l'immissione di multipli dello stesso modello, che possono essere opzionalmente legati a un altro modello / modelli da chiavi esterne. Tuttavia, non sembra esserci alcuna opzione predefinita per l'immissione di più dati di un modello e non è per questo che il formset sembra essere inteso.


26

Di recente ho avuto qualche problema e ho appena capito come farlo. Supponendo che tu abbia tre classi, Primary, B, C e che B, C abbiano 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 consentirti di eseguire qualsiasi convalida richiesta, oltre a generare tutti e tre gli oggetti sulla stessa pagina. Ho anche usato javascript e campi nascosti per consentire la generazione di più oggetti B, C sulla stessa pagina.


3
In questo esempio, come imposti le chiavi esterne per i modelli B e C in modo che puntino al modello primario?
Utente

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. Il contratto ha la chiave esterna per CustomerConfig. Ad esempio customer_config = models.ForeignKey ('CustomerPartnerConfiguration') Che cos'è 'primary'?
pitchblack408

10

Il MultiModelForm di django-betterformsè un comodo wrapper per fare ciò che è descritto nella risposta di Gnudiff . Racchiude i messaggi di posta elettronica regolari ModelFormin una singola classe che viene utilizzata in modo trasparente (almeno per l'utilizzo di base) come una singola forma. Ho copiato un esempio dai loro documenti di seguito.

# 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

Ho appena visto django-betterformse la sua classe MultiModelForm prima di trovare la tua risposta. La loro soluzione sembra davvero buona ma sembra che non sia stata aggiornata da un po '. Stai ancora usando questo @jozxyqk? Alcun problema?
enchance

@inchance sono passati alcuni anni. Allora l'ho trovato conveniente e una delle migliori opzioni in circolazione. Se non ti piacciono troppo, risparmi tempo. Posso immaginare che quando vuoi iniziare a personalizzare e creare moduli non banali, sarebbe più facile crearne uno tuo. Mescolare facilmente forme e contesti nelle viste è la prima caratteristica che penso di essermi persa in django.
jozxyqk

Grazie per la risposta amico. Sto valutando solo di biforcarlo e forse aggiornare alcune cose lungo la strada. Da quello che ho visto finora, funziona benissimo. Hai ragione, è un enorme risparmio di tempo.
enchance

5

Al momento ho una soluzione alternativa funzionale (supera i miei test unitari). È una buona soluzione a mio parere 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

3

"Voglio nascondere alcuni campi ed eseguire una convalida complessa."

Comincio con l'interfaccia di amministrazione integrata.

  1. Costruisci il ModelForm per mostrare i campi desiderati.

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

    Assicurati che questa parte funzioni abbastanza bene.

Fatto ciò, puoi allontanarti dall'interfaccia di amministrazione incorporata.

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

Quindi devi scrivere la funzione view per leggere e convalidare le varie cose del form e fare i vari oggetti save ().

"È un problema di progettazione se scompongo tutto e codifico a mano?" No, è solo un sacco di tempo per non molto beneficio.


Non so come, quindi non farlo
orokusaki

1
@orokusaki: cosa vorresti di più? Questo sembra descrivere una soluzione. Cos'altro si dovrebbe dire? La domanda è vaga, quindi è difficile fornire il codice effettivo. Piuttosto che lamentarsi, fornire un suggerimento per il miglioramento. Cosa suggerisci?
S.Lott

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.