Красивые динамические формы в Django

Раньше я использовал для создания форм в Django не самый лучший способ, но, надеюсь, то как я делаю их теперь - гораздо правильнее.

Решение заключается в использовании type(). Если вы так и делаете, думаю вы не найдете здесь ничего нового. Если же вы возитесь с 'self.fields["name"]' в ваших формах - читайте дальше.

Рассмотрим на примере: сделаем систему опросов. Пусть это будет что-то на подобии игры "Кто хочет стать миллионером?". Т.е. у вас есть вопрос и 4 ответа. Итак две модели...

  1. from django.db import models
  2. class Question(models.Model):
  3. test = models.CharField(max_length=128)
  4. class Answer(models.Model):
  5. question = models.ForeignKey(Question)
  6. test = models.CharField(max_length=20)
  7. is_correct = models.BooleanField(default=False)

По существу, мы хотим такую форму, которая содержит как вопрос, так и ответы, а так же проверяет правильность ответа. Это можно сделать следующим образом.

  1. from django import forms
  2. class QuizForm(forms.Form):
  3. def __init__(self,question, *args, **kwargs):
  4. super(QuizForm, self).__init__(*args, **kwargs)
  5. self.fields['question'] = forms.IntegerField(widget= \
  6. forms.HiddenInput, initial=question.id)
  7. self.fields['answers'] = forms.ModelChoiceField(queryset= \
  8. question.answers_set)

Не самый лучший вариант. Как вариант генерацию полей можно вынести из __init__, но это не сильно улучшит общую каритну - просто позволит вызывать инициализацию позже. И не забывайте, что это очень простой пример, может в реальности вам понадобиться генерировать класс с 20 полями?

И так, как мы можем решить эту проблему с помощью type()? Отвлечемся и вспомним что мы знаем об этой функции. Есть два способа ее использования, во-первых, можно вызвать type(object) и вернется тип object. Во-вторых, можно динамически создавать классы, использую type() как конструктор.

Для начала, взглянем на следующий пример, который приводит к точно такому же результату, что и предыдущий.

  1. def quiz_form_factory(question):
  2. properties = {
  3. 'question' : forms.IntegerField(widget=forms.HiddenInput, \
  4. initial=question.id),
  5. 'answers' : forms.ModelChoiceField(queryset= \
  6. question.answers_set)
  7. }
  8. return type('QuizForm', (forms.Form,), properties)

Смотрите, как все просто. Для того, чтобы создать новый класс, функции type передаются три параметра: название класса, набор родительских классов и словарь со свойствами нового класса.

Кроме того немного измениться и способ использования такого класса.

  1. # начинаем со случайного вопроса
  2. question = Question.objects.all().order_by('?')[0]
  3. # метод A
  4. quiz_form = QuizForm(question)
  5. # или с POST данными
  6. quiz_form = QuizForm(question, request.POST)
  7. # метод B
  8. QuizForm = quiz_form_factory(question)
  9. quiz_form = QuizForm()
  10. # или с POST данными
  11. quiz_form = QuizForm(request.POST)

Возможно, если бы я выбрал более сложный пример, эффект от такого использования был бы больше, использовалось бы меньше кода и он выглядел бы красивее.

Основное преимущество для меня в том, что этот способ позволяет писать более хороший, структурированный код. Так же его достоинство в том, что type() создает самую обычную Django форму.

Rate It! (Average 5.00, 1 votes)

Related Posts

1 Responses to Красивые динамические формы в Django

  1. gravatar

    Как по мне первый вариант намного правильней и читабельней!

    а вообще лучше так

    class QuizForm(forms.Form):
    question = forms.IntegerField(...)
    answers = forms.ModelChoiceField(queryset=Model.objects.none())

    def __init__(self, question, *args, **kwargs):
    super(QuizForm, self).__init__(*args, **kwargs)
    self.fields['question'].initial = question.id
    self.fields['answers'].queryset = question.answer_set.all()

Leave a Reply

Mail will not be published