2010-01-02 8 views
22

Dati i seguenti modelli:Django: L'inizializzazione di un FormSet di moduli personalizzati con le istanze

class Graph(models.Model): 
    owner = models.ForeignKey(User) 

    def __unicode__(self): 
     return u'%d' % self.id 

class Point(models.Model): 
    graph = models.ForeignKey(Graph) 
    date = models.DateField(primary_key = True) 
    abs = models.FloatField(null = True) 
    avg = models.FloatField(null = True) 

    def __unicode__(self): 
     return u'%s' % self.date 

Sto cercando di creare un modulo per la modifica gli elenchi di punti. I tag input HTML richiedono attributi aggiuntivi da impostare, in modo Sto usando il seguente modulo personalizzato:

class PointForm(forms.ModelForm): 
    graph = forms.ModelChoiceField(queryset = Graph.objects.all(), 
            widget = forms.HiddenInput()) 
    date = forms.DateField(widget = forms.HiddenInput(), label = 'date') 
    abs = forms.FloatField(widget = forms.TextInput(
             attrs = {'class': 'abs-field'}), 
          required = False) 

    class Meta: 
     model = Point 
     fields = ('graph', 'date', 'abs') # Other fields are not edited. 

    def pretty_date(self): 
     return self.data.strftime('%B') 

A questo punto non so come passare istanze della classe Point per un FormSet:

def edit(request): 
    PointFormSet = forms.formsets.formset_factory(PointForm, extra = 0) 
    if request.method == 'POST': 
     return 

    # Receive 3 points to edit from the database. 
    graph, res = Graph.objects.get_or_create(id = 1) 
    one_day = datetime.timedelta(days = 1) 
    today  = datetime.date.today() 
    do_edit = [] 
    for date in [today - (x * one_day) for x in range(3)]: 
     point, res = Point.objects.get_or_create(graph = graph, date = date) 
     do_edit.append(point) 

    formset = PointFormSet(????) # How is this initialized with the points? 

ho trovato un hack che funziona un po ', ma conduce ad errori in seguito quando si cerca di elaborare i dati POST risultante:

do_edit = [] 
for date in [today - (x * one_day) for x in range(3)]: 
    point, res = Point.objects.get_or_create(graph = graph, date = date) 
    data   = point.__dict__.copy() 
    data['graph'] = graph 
    do_edit.append(data) 

formset = PointFormSet(initial = do_edit) 

Come è questo fatto in modo corretto?

Per il riferimento, il mio modello assomiglia a questo:

<form action="" method="post"> 
{{ formset.management_form }} 
<table> 
    <tbody> 
    {% for form in formset.forms %} 
     <tr> 
      <td>{{ form.graph }} {{ form.date }} {{ form.pretty_date }}:</td> 
      <td width="100%">{{ form.abs }}</td> 
     </tr> 
    {% endfor %} 
    </tbody> 
</table> 
</form> 

risposta

23

Il trucco è quello di utilizzare un "ModelFormset" invece di un formset in quanto consentono di inizializzazione con un set di query. I documenti sono here, quello che fai è fornire un form = * quando si crea il modello formset e queryset = * quando si istanzia il formset. La forma = * argomentazione non è ben documentata (ha dovuto scavare un po 'nel codice per assicurarsi che sia effettivamente lì).

def edit(request): 
    PointFormSet = modelformset_factory(Point, form = PointForm) 
    qset = Point.objects.all() #or however your getting your Points to modify 
    formset = PointFormset(queryset = qset) 
    if request.method == 'POST': 
     #deal with posting the data 
     formset = PointFormset(request.POST) 
     if formset.is_valid(): 
      #if it is not valid then the "errors" will fall through and be returned 
      formset.save() 
     return #to your redirect 

    context_dict = {'formset':formset, 
        #other context info 
        } 

    return render_to_response('your_template.html', context_dict) 

Così il codice attraversa facilmente. Se la richiesta è GET, il modulo istanziato viene restituito all'utente. Se la richiesta è un POST e il modulo è not .is_valid(), gli errori "rientrano" e vengono restituiti nello stesso modello. Se la richiesta è un POST e i dati sono validi, il formset viene salvato.

Spero che questo aiuti.

-Will

+1

Ahhhh, grazie mille, funziona magnificamente. Correzione minore: durante la convalida dei dati POST, l'inizializzazione appare come segue: "PointFormset (request.POST, queryset = qset)". Altrimenti riceverai questo errore: "Il punto con questa data esiste già". – knipknap

+0

doh ... buona presa ... Ho dimenticato che quando si utilizza il modello_formset e ho fornito i dati necessari per fornire anche il set di query ... Non ho mai avuto un utilizzo effettivo per questo caso d'uso. – JudoWill

0

Se si dispone di un solo possibile valore che si desidera impostare, o forse una chiusa di valori, è possibile impostare loro dopo i post degli utenti i dati al server utilizzando commit=False

Si prega di considerare il seguente codice:

class UserReferralView(View): 
    ReferralFormSet = modelformset_factory(ReferralCode, 
              form=ReferralTokenForm, extra=1) 

    def get(self, request): 
     pass 

    def post(self, request): 
     referral_formset = UserUpdateView.ReferralFormSet(request.POST) 

     if referral_formset.is_valid(): 
      instances = referral_formset.save(commit=False) 
      for instance in instances: 
       instance.user = request.user 
       instance.save() 
      return redirect(reverse('referrals.success_view')) 
     else: 
      return redirect(reverse('referrals.failure_view')) 
Problemi correlati