Раньше я использовал для создания форм в Django не самый лучший способ, но, надеюсь, то как я делаю их теперь - гораздо правильнее.
Решение заключается в использовании type(). Если вы так и делаете, думаю вы не найдете здесь ничего нового. Если же вы возитесь с 'self.fields["name"]' в ваших формах - читайте дальше.
Рассмотрим на примере: сделаем систему опросов. Пусть это будет что-то на подобии игры "Кто хочет стать миллионером?". Т.е. у вас есть вопрос и 4 ответа. Итак две модели...
from django.db import models class Question(models.Model): test = models.CharField(max_length=128) class Answer(models.Model): question = models.ForeignKey(Question) test = models.CharField(max_length=20) is_correct = models.BooleanField(default=False)
По существу, мы хотим такую форму, которая содержит как вопрос, так и ответы, а так же проверяет правильность ответа. Это можно сделать следующим образом.
from django import forms class QuizForm(forms.Form): def __init__(self,question, *args, **kwargs): super(QuizForm, self).__init__(*args, **kwargs) self.fields['question'] = forms.IntegerField(widget= \ forms.HiddenInput, initial=question.id) self.fields['answers'] = forms.ModelChoiceField(queryset= \ question.answers_set)
Не самый лучший вариант. Как вариант генерацию полей можно вынести из __init__, но это не сильно улучшит общую каритну - просто позволит вызывать инициализацию позже. И не забывайте, что это очень простой пример, может в реальности вам понадобиться генерировать класс с 20 полями?
И так, как мы можем решить эту проблему с помощью type()? Отвлечемся и вспомним что мы знаем об этой функции. Есть два способа ее использования, во-первых, можно вызвать type(object) и вернется тип object. Во-вторых, можно динамически создавать классы, использую type() как конструктор.
Для начала, взглянем на следующий пример, который приводит к точно такому же результату, что и предыдущий.
def quiz_form_factory(question): properties = { 'question' : forms.IntegerField(widget=forms.HiddenInput, \ initial=question.id), 'answers' : forms.ModelChoiceField(queryset= \ question.answers_set) } return type('QuizForm', (forms.Form,), properties)
Смотрите, как все просто. Для того, чтобы создать новый класс, функции type передаются три параметра: название класса, набор родительских классов и словарь со свойствами нового класса.
Кроме того немного измениться и способ использования такого класса.
# начинаем со случайного вопроса question = Question.objects.all().order_by('?')[0] # метод A quiz_form = QuizForm(question) # или с POST данными quiz_form = QuizForm(question, request.POST) # метод B QuizForm = quiz_form_factory(question) quiz_form = QuizForm() # или с POST данными quiz_form = QuizForm(request.POST)
Возможно, если бы я выбрал более сложный пример, эффект от такого использования был бы больше, использовалось бы меньше кода и он выглядел бы красивее.
Основное преимущество для меня в том, что этот способ позволяет писать более хороший, структурированный код. Так же его достоинство в том, что type() создает самую обычную Django форму.


Как по мне первый вариант намного правильней и читабельней!
а вообще лучше так
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()