Django: initialiser un FormSet de formulaires personnalisés avec des instances

étant donné les modèles suivants:

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

j'essaie de créer une forme pour éditer des listes de Points. Les balises D'entrée HTML nécessitent des attributs supplémentaires à définir, donc j'utilise le formulaire personnalisé suivant:

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')

À ce stade, je ne sais pas comment passer des instances de la classe Point à 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?

j'ai trouvé un hack qui fonctionne un peu, mais il conduit à des erreurs plus tard en essayant de traiter le POST résultant données:

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)

Comment est-il correctement?

Pour la référence, mon modèle ressemble à ceci:

<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>
22
demandé sur knipknap 2010-01-02 20:13:27

2 réponses

le truc est d'utiliser un "ModelFormset" au lieu d'un simple formset puisqu'ils permettent l'initialisation avec un queryset. Les docs sont ici, ce que vous faites est de fournir un form=* lors de la création du formset model et queryset=* lors de l'instanciation du formset. La forme = * argument n'est pas bien documentée (il a fallu creuser un peu dans le code pour s'assurer qu'il est bien 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)

ainsi le code passe facilement. Si la requête est un GET alors le instancié formulaire est renvoyé à l'utilisateur. Si la demande est un POSTE et le formulaire est not .is_valid() puis les erreurs "passent à travers" et sont retournées dans le même modèle. Si la requête est un POST et que les données sont valides, alors le formset est sauvegardé.

J'espère que ça aidera.

-Va

24
répondu JudoWill 2010-01-02 19:08:52

si vous n'avez qu'une seule valeur possible que vous voulez définir, ou peut-être une fermeture de VALEURs, il est possible de les définir APRÈS QUE l'Utilisateur a posté les données sur votre serveur en utilisant commit=False

s'il vous Plaît considérez le code suivant:

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'))
0
répondu Oleg Belousov 2017-03-31 18:19:24