我做了一个类似 ifttt.com 的项目
为此,我使用 FormWizard
。
实际上,只有 RSS
和 Evernote
我可以像 FormWizard 预期的那样设置 FORMS
和 TEMPLATES
,这是我的 urls.py
和 View .py
:
urls.py
# wizard
url(r'^service/create/$', UserServiceWizard.as_view([RssForm, EvernoteForm,
ServicesDescriptionForm]), name='create_service'),
views.py
from th_rss.forms import RssForm
from th_evernote.forms import EvernoteForm
from django_th.forms.base import ServicesDescriptionForm
FORMS = [("rss", RssForm),
("evernote", EvernoteForm),
("services", ServicesDescriptionForm), ]
TEMPLATES = {
'0': 'rss/wz-rss-form.html',
'1': 'evernote/wz-evernote-form.html',
'2': 'services_wizard/wz-description.html'}
class UserServiceWizard(SessionWizardView):
instance = None
def get_form_instance(self, step):
if self.instance is None:
self.instance = TriggerService()
return self.instance
def done(self, form_list, **kwargs):
trigger = self.instance
trigger.provider = UserService.objects.get(
name='ServiceRss',
user=self.request.user)
trigger.consummer = UserService.objects.get(name='ServiceEvernote',
user=self.request.user)
trigger.user = self.request.user
trigger.status = True
# save the trigger
trigger.save()
#...then create the related services from the wizard
for form in form_list:
if form.cleaned_data['my_form_is'] == 'rss':
from th_rss.models import Rss
Rss.objects.create(
name=form.cleaned_data['name'],
url=form.cleaned_data['url'],
status=1,
trigger=trigger)
if form.cleaned_data['my_form_is'] == 'evernote':
from th_evernote.models import Evernote
Evernote.objects.create(
tag=form.cleaned_data['tag'],
notebook=form.cleaned_data['notebook'],
status=1,
trigger=trigger)
return HttpResponseRedirect('/')
def get_template_names(self):
return [TEMPLATES[self.steps.current]]
但实际上该项目只处理 2 项服务,我不想(也无法想象)为每一对新服务(如 TwitterEvernoteWizard、RssTwitterWizard、FacebookTwitterWizard 等)创建一个专用的 CBV。
所以首先,我必须按这些步骤更改流程:
- 第一页显示用户可以选择的服务
- 第二页询问用户他想从第 1 步选择的服务中获取哪些数据
- 第 3 页显示用户可以选择的服务,而无需在第 1 步中选择服务
- 第 4 页询问用户(系统将抓取的)数据将去往何处(在第 3 步选择的服务中)
- 第 5 个(也是最后一个)页面显示用于命名触发器的描述字段。
用一个具体的例子给出:
- 第 1 页我选择 Twitter
- 第2页我选择从时间线抓取数据
- 第 3 页我选择 Facebook
- 第4页我选择把资料上墙
- 第 5 页我输入“这是我从 Twitter 到 Facebook 的触发器”;)
因此,在这个过程中,我需要能够动态更改 FORMS 的内容,以使用我之前选择的服务中的 FormWizard 名称填充它。 TEMPLATES 字典也一样。
如您所见,在向导开始时,我无法提前知道将选择哪个服务。
这就是为什么我需要动态填充 FORMS
和 TEMPLATES
如果有人知道如何执行此操作或可以建议一种继续进行的方法,我将不胜感激。
问候
注意:我使用的是 Django 1.4
最佳答案
我是这样处理的
首先,urls.py:
url(r'^service/create/$','django_th.views.get_form_list', name='create_service'),
然后在 views.py 中:
我做到了:
def get_form_list(request, form_list=None):
if form_list is None:
form_list = [ProviderForm, DummyForm, ConsummerForm, DummyForm, \
ServicesDescriptionForm]
return UserServiceWizard.as_view(form_list=form_list)(request)
这允许定义 5 个步骤:
- 3 个已知表单(
ProviderForm
、ConsummerForm
、ServicesDescriptionForm
- 2 个未知的(
DummyForm
实际上是两次)将在下面动态处理
提供 DummyForm
的 forms.py :
class DummyForm(forms.Form):
pass
下一步就是从ProviderForm中获取数据,从中获取我选择的服务,加载这个选择服务的for:
在我的 views.py 中:
class UserServiceWizard(SessionWizardView):
def __init__(self, **kwargs):
self.form_list = kwargs.pop('form_list')
return super(UserServiceWizard, self).__init__(**kwargs)
def get_form_instance(self, step):
if self.instance is None:
self.instance = UserService()
return self.instance
def get_context_data(self, form, **kwargs):
data = self.get_cleaned_data_for_step(self.get_prev_step(
self.steps.current))
if self.steps.current == '1':
service_name = str(data['provider']).split('Service')[1]
#services are named th_<service>
#call of the dedicated <service>ProviderForm
form = class_for_name('th_' + service_name.lower() + '.forms',
service_name + 'ProviderForm')
elif self.steps.current == '3':
service_name = str(data['consummer']).split('Service')[1]
#services are named th_<service>
#call of the dedicated <service>ConsummerForm
form = class_for_name('th_' + service_name.lower() + '.forms',
service_name + 'ConsummerForm')
context = super(UserServiceWizard, self).get_context_data(form=form,
**kwargs)
return context
这里:
__init__
从我在urls.py
中定义的 - 在
get_context_data
中,我需要从我在ProviderForm
和消费者表格
。由于该服务名为“FoobarService”,我拆分“服务”以调用服务Foobar(Consummer|Provider)Form
和class_for_name()
下面的形式:
get_form_list
函数加载数据
class_for_name
:
def class_for_name(module_name, class_name):
m = importlib.import_module(module_name)
c = getattr(m, class_name)
return c
最后:
有了所有这些,我就可以在任何步骤动态地动态更改表单,事实上,我决定在步骤 1 和 3 中执行此操作,但可以针对任何步骤进行调整;)
关于python - 动态表单向导,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18836547/