Поле выбора динамической модели в наборе форм django с использованием нескольких элементов выбора
Я разместил этот вопрос в списке пользователей django, но пока не получил ответа.
У меня есть модели, которые выглядят примерно так:
class ProductGroup(models.Model):
name = models.CharField(max_length=10, primary_key=True)
def __unicode__(self): return self.name
class ProductRun(models.Model):
date = models.DateField(primary_key=True)
def __unicode__(self): return self.date.isoformat()
class CatalogItem(models.Model):
cid = models.CharField(max_length=25, primary_key=True)
group = models.ForeignKey(ProductGroup)
run = models.ForeignKey(ProductRun)
pnumber = models.IntegerField()
def __unicode__(self): return self.cid
class Meta:
unique_together = ('group', 'run', 'pnumber')
class Transaction(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
item = models.ForeignKey(CatalogItem)
quantity = models.IntegerField()
price = models.FloatField()
Допустим, в любой момент времени существует около 10 ProductGroups и 10-20 соответствующих ProductRuns. Каждая группа имеет 20-200 различных номеров продуктов (pnumber), поэтому существует по крайней мере несколько тысяч CatalogItems.
Я работаю над формами для модели транзакций. Вместо одного меню выбора с несколькими тысячами CatalogItems для поля ForeignKey я хочу заменить три раскрывающихся меню для group, run и pnumber, которые однозначно идентифицируют CatalogItem. Я также хотел бы ограничить варианты во вторых двух раскрывающихся списках теми прогонами и числами, которые доступны для выбранной в данный момент группы продуктов (я могу обновить их через AJAX, если пользователь меняет группу продуктов, но важно, чтобы начальная загрузка страницы, как описано, не полагаясь на AJAX).
Какой лучший способ сделать это?
В качестве отправной точки, вот что я пробовал / рассматривал до сих пор:
Мой первый подход состоял в том, чтобы исключить поле внешнего ключа элемента из формы, добавить замещающие раскрывающиеся списки, переопределив метод add_fields набора форм, а затем извлечь данные и заполнить поля вручную в экземплярах модели перед их сохранением. Это просто и довольно просто, но это не очень многоразово, и я не думаю, что это правильный способ сделать это.
Мой второй подход состоял в том, чтобы создать новое поле, которое наследует и MultiValueField и ModelChoiceField, и соответствующий подкласс MultiWidget. Это похоже на правильный подход. Как сказал Малкольм Трединник в дискуссии пользователей django, "ум" поля лежит в классе Field ".
У меня проблема, когда / где получить списки вариантов из БД. Код, который у меня сейчас есть, делает это в поле __init__
, но это означает, что я должен знать, с какой ProductGroup я имею дело, прежде чем я смогу даже определить класс Form, так как мне нужно создать экземпляр поля, когда я определяю форму. Таким образом, у меня есть фабричная функция, которую я вызываю в последнюю минуту с моей точки зрения - после того, как я знаю, какие у меня есть CatalogItems и в какую группу продуктов они входят - для создания классов form / formset и создания их экземпляров. Это работает, но мне интересно, есть ли лучший способ. В конце концов, поле должно быть в состоянии определить правильный выбор намного позже, как только оно узнает его текущее значение.
Другая проблема заключается в том, что моя реализация ограничивает весь набор форм транзакциями, относящимися к (CatalogItems из) одной ProductGroup.
Третья возможность, которую я развлекаю, - поместить все это в класс Widget. Как только у меня появится экземпляр связанной модели, или cid, или какой-либо другой виджет, я могу получить группу ProductGroup и создать выпадающие списки. Это решило бы проблемы с моим вторым подходом, но не похоже на правильный подход.
2 ответа
В итоге я придерживался второго подхода, но теперь я убежден, что это был Короткий путь, который был очень длинным. Мне пришлось немного покопаться во внутренностях ModelForm и FormField, и сложность IMO перевешивает минимальные преимущества.
То, что я написал в вопросе о первом подходе: "Это просто и довольно просто", должно было стать подсказкой.
Один из способов установки выбора полей формы в наборе форм - это инициализация форм и перезапись выбора self.fields['field_name'].., Но так как требуется более динамичный подход, вот что работает в представлении:
from django.forms.models import modelformset_factory
user_choices = [(1, 'something'), (2, 'something_else')] # some basic choices
PurchaserChoiceFormSet = modelformset_factory(PurchaserChoice, form=PurchaserChoiceForm, extra=5, max_num=5)
my_formset = PurchaserChoiceFormSet(self.request.POST or None, queryset=worksheet_choices)
# and now for the magical for loop
for choice_form in my_formset:
choice_form.fields['model'].choices = user_choices
Я не смог найти ответ на этот вопрос, но попробовал, и он работает в Django 1.6.5. Я понял это, так как формы и циклы for, кажется, хорошо сочетаются вместе:)