Bonjour !
Je travaille sur une petite application avec Django pour gérer les candidatures dans mon association. Chaque candidat est amené à répondre à quelques questions ouvertes (du style "Pourquoi voulez-vous nous rejoindre ?"). Les questions ne sont pas fixées en amont, je souhaite que l’administrateur puisse à volonté rajouter ou supprimer des questions.
J’ai donc mis en place les modèles suivants :
class Candidate(models.Model):
# (...)
answers = models.ManyToManyField('Question', through='Answer')
class Question(models.Model):
text = models.CharField(max_length=200)
class Answer(models.Model):
candidate = models.ForeignKey(Candidate, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=100)
L’idée est que les questions sont liées aux candidats par une relation Many-to-Many (chaque question peut être liée à plusieurs candidats, chaque candidat répond à plusieurs questions) et les réponses apportées par le candidat aux questions sont stockées par le modèle intermédiaire, Answer
ici.
J’ai aussi généré les formulaires suivants :
class CandidateForm(forms.ModelForm):
class Meta:
model = Candidate
fields = ['first_name', 'last_name', 'birth_date', 'email']
class AnswerForm(forms.ModelForm):
class Meta:
model = Answer
fields = ['question', 'text']
AnswerInlineFormset = forms.inlineformset_factory(Candidate, Answer, form=AnswerForm)
L’idée étant ici d’afficher les questions grâce à un FormSet.
Mais le problème est que je n’obtiens bien sûr pas le résultat escompté :
Le FormSet permet bien de définir la relation Many-to-Many mais pas comme je le souhaite. Je ne veux évidemment pas que le candidat puisse choisir les questions qui lui sont posées !
J’aimerai plutôt que le FormSet soit "initialisé" avec certaines questions définies dans un QuerySet. J’ai essayé d’utiliser le paramètre initial
au moment d’initialiser le FormSet dans ma vue, ce qui m’oblige aussi à définir le paramètre extra
avec la taille de mon QuerySet, pour permettre à toutes les questions de s’afficher.
class CandidateFormView(CreateView):
template_name = 'candidates/candidate.html'
form_class = CandidateForm
def get_context_data(self, **kwargs):
context = super(CandidateFormView, self).get_context_data(**kwargs)
initial = [{'question': q.text} for q in Question.objects.all()]
AnswerInlineFormset = inlineformset_factory(Candidate, Answer, form=AnswerForm, can_delete=False, extra=len(initial))
context['answers_formset'] = AnswerInlineFormset(initial=initial)
return context
J’obtiens alors un résultat qui correspond à peu près à mes attentes mais je ne suis pas convaincu que ça soit la bonne manière de procéder, j’ai un peu l’impression de bricoler et notamment de mélanger des choses qui ne devraient pas l’être, par exemple en définissant les formulaires directement dans la vue. J’ai peur également de devoir beaucoup bricoler pour le traitement qui doit suivre pour l’enregistrement, notamment pour gérer les éventuelles valeurs obligatoires…
Aussi, savez-vous si il existe une manière plus élégante de faire ce que je souhaite ? Peut-être mon modèle de données même est-il à revoir ?