#python #django #forms
#python #django #формы
Вопрос:
У меня есть некоторые поля формы, которые я хочу включить / исключить в зависимости от того, выполнено определенное условие или нет. Я знаю, как включать и исключать элементы формы, но мне трудно это делать, когда я хочу, чтобы ит-элементы отображались на основе результата функции.
Вот моя форма:
class ProfileForm(ModelForm):
# this_team = get Team instance from team.id passed in
# how?
def draft_unlocked(self):
teams = Team.objects.order_by('total_points')
count = 0
for team in teams:
if team.pk == this_team.pk:
break
count = 1
now = datetime.datetime.now().weekday()
if now >= count:
# show driver_one, driver_two, driver_three
else:
# do not show driver_one, driver_two, driver_three
class Meta:
model = Team
Чего я пытаюсь достичь, так это того, что, исходя из общего количества очков, команда не должна иметь возможности сменить своего водителя до указанного ими дня. Например, последняя команда в турнирной таблице может добавить / удалить гонщика в понедельник, предпоследняя команда может добавить / удалить во вторник и так далее…
Итак, первая проблема — как мне получить экземпляр Team внутри самой формы по идентификатору, который был передан. И как мне включить / исключить на основе результата draft_unlocked().
Или, возможно, есть лучший способ сделать все это?
Всем большое спасибо.
Ответ №1:
На самом деле это довольно просто (настройки условного поля) — вот краткий пример:
from django.forms import Modelform
from django.forms.widgets import HiddenInput
class SomeForm(ModelForm):
def __init__(self, *args, **kwargs):
# call constructor to set up the fields. If you don't do this
# first you can't modify fields.
super(SomeForm, self).__init__(*args, **kwargs)
try:
# make somefunc return something True
# if you can change the driver.
# might make sense in a model?
can_change_driver = self.instance.somefunc()
except AttributeError:
# unbound form, what do you want to do here?
can_change_driver = True # for example?
# if the driver can't be changed, use a input=hidden
# input field.
if not can_change_driver:
self.fields["Drivers"].widget = HiddenInput()
class Meta:
model = SomeModel
Итак, ключевые моменты из этого:
self.instance
представляет связанный объект, если форма связана. Я полагаю, что это передается в качестве именованного аргумента, следовательно, вkwargs
, который родительский конструктор использует для созданияself.instance
.- Вы можете изменить свойства поля после вызова родительского конструктора.
- виджеты — это способ отображения форм. HiddenInput в основном означает
<input type="hidden" .../>
.
Есть одно ограничение; я могу изменить входные данные, чтобы изменить значение, если я изменяю отправленные данные POST / GET. Если вы не хотите, чтобы это происходило, что-то, что следует учитывать, переопределяет метод проверки формы (clean ()). Помните, что все в Django — это просто объекты, что означает, что вы действительно можете изменять объекты класса и добавлять к ним данные случайным образом (хотя они не будут сохраняться). Итак, в вашем __init__
вы могли бы:
self.instance.olddrivers = instance.drivers.all()
Затем в вашем чистом методе для указанной формы:
def clean(self):
# validate parent. Do this first because this method
# will transform field values into model field values.
# i.e. instance will reflect the form changes.
super(SomeForm, self).clean()
# can we modify drivers?
can_change_driver = self.instance.somefunc()
# either we can change the driver, or if not, we require
# that the two lists are, when sorted, equal (to allow for
# potential non equal ordering of identical elements).
# Wrapped code here for niceness
if (can_change_driver or
(sorted(self.instance.drivers.all()) ==
sorted(self.instance.olddrivers))):
return True
else:
raise ValidationError() # customise this to your liking.
Ответ №2:
Вы можете сделать то, что вам нужно, добавив свой собственный init, куда вы можете передать идентификатор при создании экземпляра класса form:
class ProfileForm(ModelForm):
def __init__(self, team_id, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
this_team = Team.objects.get(pk=team_id)
teams = Team.objects.order_by('total_points')
count = 0
for team in teams:
if team.pk == this_team.pk:
break
count = 1
now = datetime.datetime.now().weekday()
if now >= count:
# show driver_one, driver_two, driver_three
else:
# do not show driver_one, driver_two, driver_three
class Meta:
model = Team
#views.py
def my_view(request, team_id):
profile_form = ProfileForm(team_id, request.POST or None)
#more code here
Надеюсь, это вам поможет.