Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Mais aussi sur les technos front comme React, Angular, Typescript et Javascript en général.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Gestion dynamique de formset

+4 votes

Sur la base de django-formset-js 0.4.3 j'essaie de mettre en place une gestion dynamique d'ajout d'un formset. J'ai exploité la doc comme j'ai pu.

Mon problème est que rien ne se passe lorsque je clique sur le bouton 'add another'. Je ne sais pas pourquoi, je ne trouve pas mon erreur.
J'ai ajouté dans settings.py les éléments suivants:
'jquery',
'djangoformsetjs',

j'ai aussi ce paramétrage:
STATIC_URL = '/static/'

ma page html

{% block content %}
    <div class="col-md-6" id="meal-content-form">
        <form method="post" role="form" class="form-horizontal" action="">
        {% csrf_token %}
        {{ form.non_field_errors }}
        <fieldset>
        {% if action == 'add_visite' %}
        <legend><span class="glyphicon glyphicon-pushpin"></span> {% trans "Consultation" %}    </legend>
        {% else %}
        <legend><span class="glyphicon glyphicon-pushpin"></span> {% trans 'Gestion des consultations' %}</legend>
        {% endif %}
        <div class="panel panel-default">
            {{ form.as_p }}
        </div>
        </fieldset>
        <legend><span class="glyphicon glyphicon-pushpin"></span> {% trans "Actes de consultation" %}</legend>
            <div id="actedetails_form" data-formset-prefix="{{ actedetails_form.prefix }}">
                {{ actedetails_form.management_form }}

                <div data-formset-body>
                    <!-- New forms will be inserted in here -->
                    {% for form in actedetails_form %}

                        <div data-formset-form>
                            {{ form }}
                            <button type="button" data-formset-delete-button>Delete form</button>
                        </div>

                    {% endfor %}
                </div>
                <!-- The empty form template. By wrapping this in a <script> tag, the
                __prefix__ placeholder can easily be replaced in both attributes and
                any scripts -->
                <script type="form-template" data-formset-empty-form>
                {% escapescript %}
                    <div data-formset-form>
                        {{ actedetails_form.empty_form }}
                        <button type="button" data-formset-delete-button>Delete form</button>
                    </div>
                {% endescapescript %}
                </script>

            <!-- This button will add a new form when clicked -->
            <input type="button" value="Add another" data-formset-add>

            <script>jQuery(function($) {
                $("#actedetails_form").formset({
                    animateForms: true
                });
            });</script>

        </div>
        <div class="form-group">
            <div class="col-sm-offset-4 col-sm-4">
                {% if action == 'add_visite' %}
                <button class="btn btn-primary">{% trans "Valider" %}</button>
                {% else %}
                    <button class="btn btn-primary">{% trans "Edit it" %}</button>
                {% endif %}
            </div>
        </div>
        </form>
    </div>
    <script src="{{ STATIC_URL }}js/jquery.js"></script>
    <script src="{{ STATIC_URL }}js/jquery.formset.js"></script>
{% endblock %}

Je ne maîtrise pas encore ce qui touche à javascript, jquery, html.
J'ai l'impression qu'il n’exécute pas la commande js.
Losrque je clique sur le bouton, il "clignote", ce qui me fait dire que le clique à fonctionné, mais rien ne se passe, pas de message dans ma console.

mon formset

ActevisiteFormSet = inlineformset_factory(Consultation, Acte_visite, form=SelectActevisiteForm,
                                      fk_name="consultation", extra= 1)

Si vous avez besoin d'autres détails, n'hésitez pas!
Toutes suggestions seront super!
Avant ça j'ai tenter de prendre exemple sur le tuto "Django, gérer les FormSets dynamiquement"
mais en vain, même résultat.

demandé 27-Avr-2015 par Victor (218 points)

avant de rajouter des couleurs et de l'animation ; est-ce que le formset tout nu fonctionne ?

L'instance que je souhaite supprimer n'est pas encore dans la base. C'est donc normal que mon pk soit None.
Mais cela ne me paraît pas logique d'enregistrer dans la base via un save () puis de les supprimer un par un en bouclant sur ma liste de deletedforms en appelant delete ()?! Je voudrais deleter avant mon save. Actuellement mon bouton Delete supprimer mon champ de l'écran mais l'enregistre quand même dans la base malgré le fait qu'il soit dans deletedforms.

1 Réponse

+4 votes

Il faut que tu vérifies :
- que tes fichiers Javascript sont bien chargés (jquery.js, jquery.formset.js)
- vérifie les options de ton pluggin jquery formset (en particulier form, le paramètre pour trouver le formulaire)
- vérifie que ton event est bien déclenché quand tu cliques sur ajouter un formulaire avec un log dans ton navigateur : console.log

$('#formset').on('formAdded', function(event) {
    newForm = event.target;
    //Do Stuff
    console.log("Ok c'est cool mon event est déclenché")
});
répondu 30-Avr-2015 par Pierre G (146 points)

J'ai un peu avancé avant ta réponse, merci!
Effectivement les fichiers n'étaient chargés. Wouaou c'est compliqué quand on ne connait rien du développement WEB. Heureusement vous êtes là.
Le problème maintenant est que pour un Add ça marche super, même chose pour le Delete du form ajouté. Mais lorsque je valide, il me prend quand même en compte le form délété avec un Maj de ma BDD. Si j'ai bien pigé ça doit venir du réindexage ou de TOTAL_FORMS. Je cherche à debbugger jquery.formset.js... Jamais fait... faut que je cherche comment faire. Je vais tester ton console.log

Par contre, j'ai du extraire

  <script type="form-template" data-formset-empty-form>
   {% escapescript %}
         <div data-formset-form>
               {{ actedetails_form.empty_form }}
               <button type="button" data-formset-delete-button>Delete form</button>
          </div>
    {% endescapescript %}
    </script>

de l'emplacement actuel pour le mettre après endblock pour qu'il soit pris en compte! sinon j'ai une erreur dans la console. Je ne sais pas pourquoi alors que le tuto le montre à cet emplacement.
Autre chose, je ne comprend pas bien quand tu dis "vérifie les options de ton pluggin jquery formset (en particulier form, le paramètre pour trouver le formulaire)"

Dans ton formsetfactory il faut que tu ajoutes en paramètre candelete=True
https://docs.djangoproject.com/fr/1.8/topics/forms/formsets/#can-delete

Tu vas pouvoir ensuite récupérer les données à supprimer via formset.deleted_forms
et faire quelque chose du style :

for obj in formset.deleted_objects:
    obj.delete()

extrait de views.py

def form_valid(self, form):
    visite = form.save(commit=False)

    if form.is_valid():
        visite.patient = self.get_object()
        visite.user = self.request.user
        visite.save()
        formset = ActevisiteFormSet((self.request.POST or None), instance=visite)

        for fs in formset.deleted_forms:
            instance = fs.instance
            print("formset.deleted_forms =", instance)
            instance.delete()

        if  formset.is_valid():
            for fs in formset:
                fs.save(user=self.request.user)
        else:
            print("erreur:",formset.errors)

    return HttpResponseRedirect(reverse('home'))

Pour répondre, j'ai 2 bontons, un pour l'ajout, l'autre pour la suppression du formset.
Concernant le bonton ajout, il fonctionne. si action sur le bonton, il y a bien duplication du formset. Si je valide, c'est ok. Tous mes formset sont pris en compte.
Par contre, concernant le bonton suppression, si je clic dessus, le formset est bien supprimé. Mais si je valide il est quand même pris en compte.
Je vois bien mes instances deleté :
formset.deletedforms = acte: OREILLE AUDIO +TYMPANO, base SS: 40.81 �, secteur 2: 70.00 � , tar SS: 0, tar S2: 0
formset.deleted
forms = acte: SOMMEIL ENDOSCOPIE SOUS SOMMEIL INDUIT, base SS: 0.00 �, secteur 2: 120.00 � , tar SS: 0, tar S2: 0

par contre , instance.delete() plante:
Acte_visite object can't be deleted because its id attribute is set to None.
Effectivement mon id est None. Je le delete avant le save.
Est ce que je dois le supprimé apres mon save??? C'est logique ça??
J'ai aussi tenté de mettre un truc du style:

for obj in formset.deleted_objects:
obj.delete()

Mais ça plante avec ce message:
'ActevisiteFormFormSet' object has no attribute 'deletedobjects'

J'imagine que ma méthode n'est pas la bonne mais mes recherches n'ont rien données.

for fs in formset.deleted_forms:
    instance = fs.instance
    print("formset.deleted_forms =", instance)
    if instance.pk is not None:
        instance.delete()

J'ai tenté de faire ça mais ça ne marche pas. Mon instance.pk est toujours None. Je fais ça avant d'appeler la méthode Save du coup ça me semble normal que mon pc soit none. Ça me paraît logique aussi de l'appeler avec mon save. Faut-il le faire après la méthode save? On va deleter directement dans la base???
Sinon j'ai trouvé une App qui semble bien fonctionner!
Django-bootstrap-dynamic-formset 0.4.3
J'ai testé ça semble fonctionner. Je dois tester sur mon code. Mais j'aurais vraiment aimer comprendre ma première methode!
J'attends vos avis ou conseils.

Est-ce que l'instance que tu supprimes est dans ta base de données ? Si elle n'est pas dans ta db elle n'a pas de primary key et c'est bien normal.

...