Modifier plusieurs objets depuis un seul formulaire

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

Je suis sous Django 1.9 et ai un modèle de la forme suivante :

1
2
3
4
class Progression(models.Model):
    project = models.OneToOneField(Project)
    value = models.PositiveSmallIntegerField(default=0)
    proposition = models.PositiveSmallIntegerField(null=True, blank=True)

Quand un membre met à jour une progression, la valeur spécifiée est stockée dans l'attribut proposition. Alors, l'instance apparaît dans la liste des progressions à valider. Quand une progression est validée, on a :

1
2
progression.value = progression.proposition
progression.proposition = None

Ce que je souhaiterais, c'est avoir un formulaire sous forme de tableau, avec toutes les progressions à valider :

Projet Valeur courante Proposition Valider

Mais j'ignore comment m'y prendre. Pour l'instant, je suis parti avec des form sets :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# forms.py

class ProgressionValidationForm(forms.Form):
    validated = forms.BooleanField(label='')

# views.py

def validate(request):
    # ...
    progressions = models.Progression.objects.filter(proposition__isnull=False)
    context['progressions'] = zip(progressions, django.forms.formset_factory(
        forms.ProgressionValidationForm,
        extra=len(progressions)
    )())
    # ...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<form action="{% url 'projects:validate_progressions' %}" method="post">
  {% csrf_token %}
  <table border="1">
    <tr class="table-header">
      <th>Projet</th>
      <th>Progression courante</th>
      <th>Proposition</th>
      <th>Valider</th>
    </tr>
  {% for progression, form in progressions %}
    <tr>
      <td>{{ progression.project.name }}</td>
      <td>{{ progression.percentage }} %</td>
      <td>{{ progression.proposition }} %</td>
      <td>{{ form.as_p }}</td>
    </tr>
  {% endfor %}
  </table>

  <input type="submit" value="Envoyer"/>
</form>

Mais j'ignore ensuite comment faire la correspondance au moment de la réception, d'autant plus que la vue appelée (validate_progressions) n'est pas celle ayant généré le form set.

Merci.

Edit : j'ai aussi essayé de créer simplement mes checkboxes à la main :

1
2
3
4
5
6
7
8
  {% for progression in progressions %}
    <tr>
      <td>{{ progression.project.name }}</td>
      <td>{{ progression.percentage }} %</td>
      <td>{{ progression.proposition }} %</td>
      <td><input type="checkbox" name="{{ progression.id }}"/></td>
    </tr>
  {% endfor %}

Mais j'ignore un peu comment récupérer les données proprement par la suite, vu que je n'ai pas de formulaire Django associé.

Édité par Vayel

+0 -0
Auteur du sujet

C'est une piste que j'ai envisagée également, mais dans la mesure où ma checkbox ne correspond à aucun attribut de mon modèle, je ne vois pas tellement comment faire. A moins que je fasse un truc du genre :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# forms.py

class ProgressionValidationForm(forms.Form):
    validated = forms.BooleanField(label='')

ProgressionFormSet = modelformset_factory(
    Progression,
    form=ProgressionValidationForm,
    queryset=Progression.objects.filter(proposition__isnull=False)
)

Édité par Vayel

+0 -0
Staff

Ça me semble être une piste.

L'autre solution est effectivement d'utiliser des FormSet en excluant les champs inutiles et en ne laissant que les pk.

"I think that it’s extraordinarily important that we in computer science keep fun in computing." – Alan J. Perlis

+0 -0
Auteur du sujet

Du coup, il fallait bien passer par un ModelFormSet :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# forms.py

class ProgressionValidationForm(forms.Form):
    validated = forms.BooleanField(required=False, label='')

# views.py

def progression_formset():
    return django.forms.modelformset_factory(
        models.Progression,
        form=forms.ProgressionValidationForm,
        extra=0
    )

def validate(request):
    progressions = models.Progression.objects.filter(proposition__isnull=False)
    formset = progression_formset()(queryset=progressions)

    context = {
        'progressions': zip(progressions, formset),
        'progression_formset': formset,
    }

    return render(request, 'projects/validate.html', context)

def validate_progression(progression):
    progression.percentage = progression.proposition
    progression.proposition = None
    progression.save()

@require_http_methods(['POST'])
def validate_progressions(request):
    formset = progression_formset()(request.POST)

    if formset.is_valid():
        for progression in formset.save(commit=False):
            validate_progression(progression)

    return redirect('projects:validate')
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form action="{% url 'projects:validate_progressions' %}" method="post">
  {% csrf_token %}
  {{ progression_formset.management_form }}

  <table border="1">
    <tr class="table-header">
      <th>Projet</th>
      <th>Progression courante</th>
      <th>Proposition</th>
      <th>Valider</th>
    </tr>
  {% for progression, form in progressions %}
    <tr>
      <td><a href="{% url 'projects:preview' progression.project.id progression.project.slug %}">{{ progression.project.name }}</a></td>
      <td>{{ progression.percentage }} %</td>
      <td>{{ progression.proposition }} %</td>
      <td>{{ form.as_p }}</td>
    </tr>
  {% endfor %}
  </table>

  <input type="submit" value="Envoyer"/>
</form>

Édité par Vayel

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte