Django - Как сделать динамические поля на форме Quizz App
Я начал делать систему викторины с Django и я использую Django Template, чтобы показать некоторые вопросы случайным образом на каждом видео.
Я могу сделать это без валидации форм, но не уверен, что это правильно...
На самом деле я только что установил имплементацию вопросов (администратор может добавлять вопросы к каждому видео, и это работает хорошо), так что модель выглядит так:
class Question(models.Model):
class TypeChoices(models.TextChoices):
SINGLE = "single"
MULTIPLE = "multiple"
uid = models.UUIDField(default=uuid.uuid4, primary_key=True)
playlist_media = models.ForeignKey(PlaylistMedia, on_delete=CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=255)
type = models.CharField(
max_length=10, choices=TypeChoices.choices, default=TypeChoices.SINGLE
)
options = ArrayField(models.CharField(max_length=255))
responses = ArrayField(models.CharField(max_length=255))
Так как тип вопросов может быть "одиночный" или "множественный", шаблон может показывать радио или флажки в зависимости от типа.
Вот пример одного вопроса с типом single:
Вот еще один пример с множественным типом:
Мой вопрос: Как я могу создать динамическую форму? Потому что если я создам простую форму (с помощью forms.py), я не буду знать, сколько полей пользователь может отправить. Потому что у него может быть 1 вопрос или 2 или 3 (максимум).
примечание: даже если тип один или несколько, "ответы" всегда представляют собой список.
Вот шаблон на всякий случай, чтобы вы знали, как это будет:
{% if questions %}
<div class="is-size-4 has-text-weight-medium mb-4 ">
<span class="">Quizz</span>
</div>
<form action="{% url 'learning-public-media' person_id=person.uid playlist_media_id=playlist_media.uid %}" method="POST">
{% csrf_token %}
<div class="is-flex is-flex-direction-column mb-2">
{% for question in questions %}
<div class="mb-2 is-size-5 ">
<span>{{ question.text }}</span>
</div>
{% if question.type == 'single' %}
<div class="my-1">
{% for option in question.options %}
<label class="radio m-1" >
<input type="radio" name="{{ question.uid }}" value="{{ option }}" required {% if media.done %}
disabled {% endif %} {% if media.done and option in question.responses %} checked {% endif %}
/>
{{ option }}
</label>
{% if media.done and option in question.responses %}
<img class="mx-2" src="{% static 'images/icons/checked.png' %}" width="16" height="16"/>
{% endif %}
{% endfor %}
</div>
{% else %}
<div class="is-flex is-flex-direction-column">
{% for option in question.options %}
<div class="is-flex is-flex-direction-row is-align-items-center">
<label class="checkbox my-1">
<input type="checkbox" name="{{ question.uid }}" value="{{ option }}" {% if media.done %}
disabled {% endif %} {% if media.done and option in question.responses %}
checked {% endif %} />
{{ option }}
</label>
{% if media.done and option in question.responses %}
<img class="ml-2" src="{% static 'images/icons/checked.png' %}" width="16" height="16"/>
{% endif %}
</div>
{% endfor %}
</div>
{% endif %}
{% endfor %}
</div>
<div class="mt-4">
<button class="button is-primary" {% if media.done %} disabled {% endif %}>{% if media.done %} Question
validée {% else %} Valider {% endif %}
</button>
</div>
</form>
{% endif %}
В представлении я просто принимаю POST запрос, но я еще не проверяю форму, потому что я не знаю, как это сделать с моим типом формы:
if request.method == "POST":
person_playlist_media.done_at = timezone.now()
person_playlist_media.save()
from apps.learning.tasks import update_analytics_person_playlist_media_task
update_analytics_person_playlist_media_task.delay(person_playlist_media.uid)
try:
next_media = PlaylistMedia.objects.get(
playlist=account_playlist.playlist, position=playlist_media.position + 1
)
return redirect(
"learning-public-media",
person_id=person.uid,
playlist_media_id=next_media.uid,
)
except PlaylistMedia.DoesNotExist:
return redirect(
"learning-public-playlist",
person_id=person.uid,
playlist_id=account_playlist.playlist.uid,
)
return render(request, "learning/media/index.html", context=context)