动态FormWizard [英] Dynamic FormWizard

查看:147
本文介绍了动态FormWizard的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我制作了一个像 ifttt.com 一样的项目。



为此,我使用 FormWizard



实际上,只有2个服务是 RSS Evernote



我可以设置 FORMS TEMPLATES 像FormWizard所预期的,这里是一个和平的我的 urls.py views.py



urls.py / p>

 #wizard 
url(r'^ service / create / $',UserServiceWizard.as_view([RssForm,EvernoteForm,
ServicesDescriptionForm]),name ='create_service'),

views.py

 从th_rss.forms导入RssForm 
from th_evernote.forms import EvernoteForm
from django_th .forms.base import ServicesDescriptionForm

FORMS = [(rss,RssForm),
(eVideos,EvernoteForm),
(services,ServicesDescriptionForm),]

TEMPLATES = {
'0':'rss / wz-rss-form.html',
'1' '
'2':'services_wizard / wz-description.html'}


class UserServiceWizard(SessionWizardView):
instance = None

def get_form_instance(self,step):

如果self.instance为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 ='ServiceETube',
user = self.request .user)
trigger.user = self.request.user
trigger.status = True
#保存触发器
tr igger.save()
#...然后从向导中创建相关的服务
form_list中的表单:
如果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)
如果form.cleaned_data ['my_form_is'] =='eFacebook':
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个服务,我不想(并且无法想像)为每一对新服务创建一个专用的CBV,如TwitterEVideosWizard,RssTwitterWizard,FacebookTwitterWizard等。



所以,首先,我将不得不通过这些步骤改变过程:




  • 1rst页面显示用户可以选择的服务

  • 第二页向用户询问他在第1步选择服务时要抓取的数据

  • 第3页显示用户可以选择的服务,而不选择一个步骤1

  • 第4页向用户询问数据(系统将抓住)将去的位置(在步骤3中的选择服务中)

  • 第5页(和最后一页)显示描述字段来命名触发器。



一个具体的例子:




  • 第1页我选择Twitter

  • 第2页我选择从时间线获取数据

  • pa ge 3我选择Facebook

  • 第4页我选择将数据放在墙上

  • 第5页我把这是从Twitter的触发器Facebook;)



所以通过这个过程,我需要能够动态地更改FORMS的内容,使用从服务的FormWizard我选择了一步之前。
TEMPLATES dict相同。



如您所见,在向导的起点,我无法预先知道哪个服务将选择。
这就是为什么我需要动态地填充 FORMS TEMPLATES



如果有人知道如何做到这一点,或者只是建议一种方法继续下去,我会很感激。



敬意



注意:我使用Django 1.4

解决方案

处理它



首先, urls.py

 code> url(r'^ service / create / $','django_th.views.get_form_list',name ='create_service'),

然后在 views.py 中:



我做了:

  def get_form_list(request,form_list = None):
如果form_list为None:
form_list = [ProviderForm,DummyForm,ConsummerForm,DummyForm, \
ServicesDescriptionForm]
返回UserServiceWizard.as_view(form_list = form_list)(请求)



这个许可证s定义5个步骤:




  • 3已知表单( ProviderForm ConsummerForm ServicesDescriptionForm

  • 2未知的( DummyForm 实际上两次)将在



forms.py 它提供了 DummyForm

  class DummyForm(forms.Form) 
pass

下一步是从ProviderForm获取数据,获取我选择的服务并从我们的 views.py 中加载所选服务的





$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $




self.form_list = kwargs.pop 'form_list')
return super(UserServiceWizard,self).__ init __(** kwargs)

def get_form_instance(self,step):
如果self.instance是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))
如果self.steps.current =='1':
service_name = str(data ['provider'])。split('Service')[ 1]
#services被命名为th_< service>
#call的专用< 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被命名为th_< service>
#call的专用< 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 __ 加载数据从 get_form_list 函数我在 urls.py

  • get_context_data 我需要在步骤1和3中从我在的下拉列表中选择的服务更改 DummyForm ProviderForm ConsummerForm 。由于该服务被命名为FoobarService,因此我将服务分成以 Foobar(Consummer | Provider)表单 > class_for_name()以下:



class_for_name

$ b

  def class_for_name(module_name,class_name):
m = importlib.import_module(module_name)
c = getattr (m,class_name)
return c

最后



所有这一切,我能够在任何步骤动态地更改表单,实际上我决定为第1步和第3步,但可以适应任何步骤;)


I made a project that works like ifttt.com does.

To do so I use FormWizard.

Actually, that works fine with the only 2 services which are RSS and Evernote

I could set the FORMS and TEMPLATES like expected by the FormWizard, here is a peace of my urls.py and views.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]]

But as actually the project handles only 2 services, I dont want (and cant imagine) to create one dedicated CBV for each couple of new service like TwitterEvernoteWizard, RssTwitterWizard, FacebookTwitterWizard and so on.

So first of all, I will have to change the process by those steps :

  • 1rst page displays the services the user can choose
  • 2nd page asks to the user what datas he wants to grab from choosen service at step 1
  • 3rd page displays the services the user can choose without the one choosen un step1
  • 4th page asks to the user where the datas (that the system will grab) will go (in the choosen service at step3)
  • 5th (and last) page displays a description field to name the trigger.

With a concret exemple that will give :

  • page 1 I choose Twitter
  • page 2 I choose to grab data from timeline
  • page 3 I choose Facebook
  • page 4 I choose to put the data on the wall
  • page 5 I put "Here is my trigger from twitter to facebook" ;)

So with this process I need to be able to dynamically change the content of FORMS to populate it with the name of the FormWizard from the Service I chose one step earlier. Same for TEMPLATES dict.

As you can see, at the beggining of the Wizard, I'm unable to know, by advance, which service will be selected. This is why I need to dynamicallly populate FORMS and TEMPLATES

If anyone knows how to do this or can just suggest a way to proceed, I will appreciate.

regards

notice : I use Django 1.4

解决方案

here is how I finished to handle it

first, urls.py :

url(r'^service/create/$','django_th.views.get_form_list', name='create_service'),

then in views.py :

I did :

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)

this permits to define 5 steps with :

  • 3 knowns Form (ProviderForm, ConsummerForm, ServicesDescriptionForm
  • 2 unknown ones (DummyForm twice in fact) that will be handle dynamically below

the forms.py which provides the DummyForm :

class DummyForm(forms.Form):
    pass

the next step is to get the data from ProviderForm, get the service I selected from it, and load the for of this selected service :

in my 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

here :

  • __init__ load the data from get_form_list function I defined in urls.py
  • in get_context_data I need to change the DummyForm in step 1 and 3 from the service I chose in the dropdown of ProviderForm and ConsummerForm. As the service is named 'FoobarService', I split 'Service' to make the call of the form of the service Foobar(Consummer|Provider)Form with class_for_name() below :

class_for_name :

def class_for_name(module_name, class_name):
   m = importlib.import_module(module_name)
   c = getattr(m, class_name)
   return c

Finally :

with all of this I'm able to dynamically change the form on the fly at any step, in fact I decide to do it for step 1 and 3 but that can be adapted for any step ;)

这篇关于动态FormWizard的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆