【问题标题】:Django SessionWizardView current step missing from form_list after Ajax requestAjax请求后form_list中缺少Django SessionWizardView当前步骤
【发布时间】:2015-10-06 02:06:29
【问题描述】:

我有一个SessionWizardView 流程,其中包含有条件的额外步骤,在第一步结束时基本上会询问“您是否要添加另一个人”,因此我的条件是通过检查上一步的清理数据来生成的;

def function_factory(prev_step):
    """ Creates the functions for the condition dict controlling the additional
    entrant steps in the process.

    :param prev_step: step in the signup process to check
    :type prev_step: unicode
    :return: additional_entrant()
    :rtype:
    """
    def additional_entrant(wizard):
        """
        Checks the cleaned_data for the previous step to see if another entrant
        needs to be added
        """
        # try to get the cleaned data of prev_step
        cleaned_data = wizard.get_cleaned_data_for_step(prev_step) or {}

        # check if the field ``add_another_person`` was checked.
        return cleaned_data.get(u'add_another_person', False)

    return additional_entrant

def make_condition_stuff(extra_steps, last_step_before_repeat):
    cond_funcs = {}
    cond_dict = {}
    form_lst = [
        (u"main_entrant", EntrantForm),
    ]

    for x in range(last_step_before_repeat, extra_steps):
        key1 = u"{}_{}".format(ADDITIONAL_STEP_NAME, x)
        if x == 1:
            prev_step = u"main_entrant"
        else:
            prev_step = u"{}_{}".format(ADDITIONAL_STEP_NAME, x-1)
        cond_funcs[key1] = function_factory(prev_step)
        cond_dict[key1] = cond_funcs[key1]
        form_lst.append(
            (key1, AdditionalEntrantForm)
        )

    form_lst.append(
        (u"terms", TermsForm)
    )

    return cond_funcs, cond_dict, form_lst

last_step_before_extras = 1
extra_steps = settings.ADDITIONAL_ENTRANTS

cond_funcs, cond_dict, form_list = make_condition_stuff(
    extra_steps,
    last_step_before_extras
)

我还有一个字典,它将步骤数据存储在可通过会话 cookie 访问的键后面,该 cookie 还包含用户输入的人员详细信息的列表。在第一个表单之后,这个列表呈现为一个选择框&在选择时触发一个 Ajax 调用到带有 kwargs 的 SessionWizard,这触发了对返回 JsonResponse 的方法的调用;

class SignupWizard(SessionWizardView):
    template_name = 'entrant/wizard_form.html'
    form_list = form_list
    condition_dict = cond_dict
    model = Entrant
    main_entrant = None
    data_dict = dict()

    def get_data(self, source_step, step):
        session_data_dict = self.get_session_data_dict()
        try:
            data = session_data_dict[source_step].copy()
            data['event'] = self.current_event.id
            for key in data.iterkeys():
                if step not in key:
                    newkey = u'{}-{}'.format(step, key)
                    data[newkey] = data[key]
                    del data[key]
        except (KeyError, RuntimeError):
            data = dict()
            data['error'] = (
                u'There was a problem retrieving the data you requested. '
                u'Please resubmit the form if you would like to try again.'
            )

        response = JsonResponse(data)
        return response

    def dispatch(self, request, *args, **kwargs):
        response = super(SignupWizard, self).dispatch(
            request, *args, **kwargs
        )
        if 'get_data' in kwargs:
            data_id = kwargs['get_data']
            step = kwargs['step']
            response = self.get_data(data_id, step)

        # update the response (e.g. adding cookies)
        self.storage.update_response(response)
        return response

    def process_step(self, form):
        form_data = self.get_form_step_data(form)
        current_step = self.storage.current_step or ''
        session_data_dict = self.get_session_data_dict()

        if current_step in session_data_dict:
            # Always replace the existing data for a step.
            session_data_dict.pop(current_step)

        if not isinstance(form, TermsForm):
            entrant_data = dict()
            fields_to_remove = [
                'email', 'confirm_email', 'password',
                'confirm_password', 'csrfmiddlewaretoken'
            ]
            for k, v in form_data.iteritems():
                entrant_data[k] = v
            for field in fields_to_remove:
                if '{}-{}'.format(current_step, field) in entrant_data:
                    entrant_data.pop('{}-{}'.format(current_step, field))
                if '{}'.format(field) in entrant_data:
                    entrant_data.pop('{}'.format(field))

            for k in entrant_data.iterkeys():
                new_key = re.sub('{}-'.format(current_step), u'', k)
                entrant_data[new_key] = entrant_data.pop(k)

            session_data_dict[current_step] = entrant_data
            done = False
            for i, data in enumerate(session_data_dict['data_list']):
                if data[0] == current_step:
                    session_data_dict['data_list'][i] = (
                        current_step, u'{} {}'.format(
                            entrant_data['first_name'],
                            entrant_data['last_name']
                        )
                    )
                    done = True

            if not done:
                session_data_dict['data_list'].append(
                    (
                        current_step, u'{} {}'.format(
                            entrant_data['first_name'],
                            entrant_data['last_name']
                        )
                    )
                )

        return form_data

如果您在不触发 Ajax 调用的情况下单步执行表单,则表单将提交并且条件 dict 会按预期运行。但是,如果触发了 Ajax 并将数据返回到表单,一旦您提交表单,会话数据似乎已经消失。有没有办法改变我的设置方式,以便get_data() 可以将数据返回到页面,而不会破坏会话?

我在开发服务器上将SESSION_ENGINE 设置为cached_db,但我遇到了一个问题,即当您提交第一个条件表单并且系统调用get_next_step() 后跟get_form_list() 和条件check 不再返回第一个条件表单,所以我留下了我的默认表单列表和ValueError,因为current_step 不再是form_list 的一部分。

所以,回顾一下,我逐步完成我的第一个表单,使用“add_another_person”字段触发第一个条件表单,该字段按预期呈现表单,此时form_list 看起来像这样;

form_list   
    u'main_entrant' <class 'online_entry.forms.EntrantForm'>
    u'additional_entrant_1' <class 'online_entry.forms.EntrantForm'>
    u'terms' <class 'online_entry.forms.TermsForm'>

但是additional_entrant_1一触发Ajax方法,然后被提交,form_list就遍历条件dict&看起来像这样;

form_list   
    u'main_entrant' <class 'online_entry.forms.EntrantForm'>
    u'terms' <class 'online_entry.forms.TermsForm'>

这可能是会话存储或会话失效的问题吗?

【问题讨论】:

  • 用于确定向导中是否包含步骤的条件应在整个向导中保持一致。我的意思是,您可以决定进一步的步骤,但不能决定前面的步骤。检查您上一步的条件是否在下一步突然发生变化。
  • @LorenzoPeña 我认为我的条件是一致的,第一个表单具有相同的字段,因此条件与其余表单相同,直到最后一个表单,因为这只是一个条款和条件复选框。 wizard.get_cleaned_data_for_step() 是否有可能每次都无法按预期返回数据?
  • 我不这么认为。您的步骤条件是基于在同一向导中收集的信息,还是基于外部参数?
  • @LorenzoPeña 条件基于向导表单中的信息。除了最后一个向导步骤之外,还有一个布尔字段,显示为所有复选框。这是每个条件从上一步的清理数据中获取的字段。
  • 调试提示:尝试在每一步打印get_cleaned_data_for_step 的结果。如果条件停止评估为真,则数据会以某种方式更改。这可以帮助确定在哪里。

标签: python django session


【解决方案1】:

我总是忽略简单的解释。

SessionWizardViewget() 请求重置了存储,而我进行的 Ajax 调用作为 get 请求访问了视图,重置了存储,但也传回了我的信息。

因此,通过简单地覆盖 get() 方法,我解决了这个问题;

def get(self, request, *args, **kwargs):
    if 'get_data' in kwargs:
        data_id = kwargs['get_data']
        step = kwargs['step']
        response = self.get_data(data_id, step)
    else:
        self.storage.reset()

        # reset the current step to the first step.
        self.storage.current_step = self.steps.first
        response = self.render(self.get_form())
    return response

【讨论】:

    猜你喜欢
    • 2015-07-02
    • 1970-01-01
    • 2014-05-02
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    相关资源
    最近更新 更多