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.

Django ModelForm et modelformset_factory(), comment imposer la valeur de certains champs ?

+3 votes

La vue est accessible à chaque personne enregistrée, et pour initialiser cette dernière je récupère les pubkeys de la Person concernée :

pubkey_formset = PubKeyFormSet(queryset=my_person.pubkey_set.all())

Seulement, lorsque l'utilisateur souhaite en ajouter une nouvelle, django échoue évidement puisque la nouvelle instance n'a pas de Person renseignée :

IntegrityError at /believe/pubkeys/
(1048, "Column 'person_id' cannot be null")

Ce que je souhaite, c'est pouvoir imposer la Person (ou le person_id) des nouvelles instances qui vont être crées lors du :

pubkey_formset.save()

Quel est la bonne pratique pour ce type de besoins ; me manque-t'il pas grand chose ou bien j'ai carrément fait fausse route ?


Les modèles :

from django.db import models

class Person(models.Model):
    uid = models.CharField(max_length=255, unique=True)

class PubKey(models.Model)
    person = models.ForeignKey(Person)
    pub_key = models.TextField(null=True, blank=True, unique=False)

Les formulaires :

from django.forms import ModelForm
from believe.models import PubKey

class PubKeyForm(ModelForm):

    class Meta:
        model = PubKey
        fields = ('pub_key', )

La vue (actuelle) :

from django.forms.models import modelformset_factory
from django.shortcuts import render, redirect

def person__pub_keys(request):

    # init
    my_person = Person.objects.get(uid=request.user.username)
    PubKeyFormSet = modelformset_factory(PubKey, form=PubKeyForm, extra=1, can_delete=True)

    # Get ?
    if request.method != 'POST':
        pubkey_formset = PubKeyFormSet(queryset=my_person.pubkey_set.all())

    # POST ?
    else:
        pubkey_formset = PubKeyFormSet(request.POST, request.FILES)
        if pubkey_formset.is_valid():
            pubkey_formset.save()
            return redirect('person__pub_keys')

    return render(request, 'believe/person__pub_keys.html', {
        'pubkey_formset': pubkey_formset,
    })

le template :

        <form class="smart-form" method="POST">
            {% csrf_token %}
            {% for form in pubkey_formset %}
                {{ form.as_table }}
                <br />
            {% endfor %}
            {{ pubkey_formset.management_form }}

            <footer>
                <button class="btn btn-primary" type="submit"><i class="fa fa-save"></i> Save </button>
            </footer>
        </form>
demandé 12-Mai-2015 par davixx (124 points)

Je suis dans le rue alors je te donnerai un lien vers ce que je fais sur un projet pour mettre comme valeur par défaut la date du jour a des rendez voys https://github.com/foxmask/dj-diabetes/blob/master/dj_diabetes/models/__init__.py

C'est le initMixin qui fait le job et est utiliser dans mon modelfotm
Si besoin de détails let me know ;)

Je t'avoue que je ne comprends pas trop comment ton initMixin résout mon problème de formset, j'ai aussi regardé dans ton repository, le seul formset se trouve ici : https://github.com/foxmask/dj-diabetes/blob/master/dj_diabetes/views/exams.py et ne semble pas être lié.

Mais y a probablement quelque chose qui m'échappe... tu veux bien expliquer plus en détails stp ?

Mon mixin initialise des valeurs par défaut bien avant le save, tu veux ajouter des valeurs lors du save , j'ai lu de travers. Je te fais une autre réponse de suite ;)

En effet, c'est lors du .save() du formset (et donc de chacun des form) ; et la valeur est dynamique. Merci, j'attends ta nouvelle réponse.

Pour les données existantes, si on veut les modifier,
il ne faudrait pas exclure du form PubKeyForm, la FK person, et dans le formulaire, le rendre hidden comme ceci la donnée serait dispo de bout en bout.

Apres pour les nouvelles données il faudrait mettre dans la fonction save() dans du form PubKeyForm par exemple comme ceci

class PubKeyForm (ModelForm):
    def save(self, person=None):
         self.myobject = super(PubKeyForm , self).save(commit=False)
         self.myobject.person = person
         self.myobject.save()

qui serait déclanché lors du form_valid dans le forms.py

def form_valid(self, form):
    self.object = form.save(person=self.request.user)

Pour les nouvelles données : la méthode form_valid() doit aussi être implémenté (et non surchargée) dans la classe PubKeyForm ? à priori, dans ce cas ça "solutionnerait le problème pour l'ajout"

Pour les données existantes, du coup, qu'empêche l'utilisateur de forger un nouvelle ID et de mettre une clef à lui, au nom d'un autre utilisateur ? Problème :/

form_valid ca va dans la views.py, pour les données existantes, dans le form avec la methode clean_person_id tu vérifies que person_id du form = self.request.user

et aussi, si un gugus forge l'id du user avec la protection csrf dans le formulaire, la validation echouera

1 Réponse

0 votes

avec le form_valid qui gere :

  • POST
  • GET
  • renvoie les données saisies quand le données renseignées sont invalides

    def form_valid(self, form):
        formset = ExamDetailsFormSet((self.request.POST or None),
                     instance=self.object)
        if formset.is_valid():
            self.object = form.save(user=self.request.user)
            formset.instance = self.object
            formset.save()
    
        return HttpResponseRedirect(reverse('exams'))
    

le get_context_data qui gere les données à remettre au contexte selon le cas :

def get_context_data(self, **kw):
    context = super(ExamsContextDataMixin, self).get_context_data(**kw)
    if self.request.POST:
        context['examsdetails_form'] = ExamDetailsFormSet(self.request.POST)
    else:
        context['examsdetails_form'] = ExamDetailsFormSet(instance=self.object)
répondu 12-Sep-2015 par foxmask (2,880 points)
...