简体   繁体   English

在Ajax请求之后,Django SessionWizardView从form_list中缺少当前步骤

[英]Django SessionWizardView current step missing from form_list after Ajax request

I've got a SessionWizardView process with conditional extra steps which at the end of the first step essentially asks 'do you want to add another person' so my condition is generated by checking the cleaned data for the previous step; 我有一个带有条件额外步骤的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
)

I've also got a dictionary which stores step data behind a key accessible via the session cookie, which also holds a list of the people's details entered by a user. 我还有一个字典,用于存储通过会话cookie访问的密钥后面的步骤数据,该会话cookie还包含用户输入的人员详细信息列表。 After the first form, this list is rendered as a select box & on selection triggers an Ajax call to the SessionWizard with kwargs which triggers the call to a method which returns a JsonResponse ; 在第一个表单之后,此列表呈现为一个选择框&on selection触发对带有kwargs的SessionWizard的Ajax调用,该kwargs触发对返回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

If you step through the form without triggering the Ajax call then the form submits and the condition dict behaves as expected. 如果您在不触发Ajax调用的情况下单步执行表单,则表单将提交,条件dict将按预期运行。 But if the Ajax is triggered and the data is returned to the form, once you submit the form the session data appears to have gone. 但是,如果触发Ajax并将数据返回到表单,则一旦提交表单,会话数据就会消失。 Is there a way to change the way I've got this setup so that get_data() can return the data to the page, without destroying the session? 有没有办法改变我的设置方式,以便get_data()可以将数据返回到页面,而不会破坏会话?

I've got the SESSION_ENGINE set to cached_db on a dev server but I've got an issue whereby when you submit the first conditional form and the system calls get_next_step() followed by get_form_list() and the condition check no longer returns that first conditional form so I'm left with my default form list and a ValueError raised because current_step is no longer part of form_list . 我已经在开发服务器cached_db SESSION_ENGINE设置为cached_db但是我遇到了一个问题,即当您提交第一个条件表单并且系统调用get_next_step()后跟get_next_step() get_form_list()并且条件检查不再返回第一个条件因为current_step不再是form_list一部分,所以我留下了我的默认表单列表并引发了ValueError

So, to recap, I step through my first form, trigger the first conditional form using the 'add_another_person' field which renders the form as expected at which point form_list looks like this; 因此,回顾一下,我逐步浏览我的第一个表单,使用'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'>

But as soon as additional_entrant_1 triggers the Ajax method, then is submitted, the form_list runs through the condition dict & looks like this; 但是一旦additional_entrant_1触发Ajax方法,然后提交, form_list运行条件dict并且看起来像这样;

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

Could this be an issue with the session storage or the session becoming invalid? 这可能是会话存储或会话变为无效的问题吗?

I always overlook the simple explanation. 我总是忽略这个简单的解释。

The get() request of the SessionWizardView resets the storage, and the Ajax call I was making hit the view as a get request, resetting the storage, but also passing back my information. SessionWizardViewget()请求重置存储,我正在制作的Ajax调用作为get请求命中视图,重置存储,但也传回我的信息。

So with a simple override of the get() method I've resolved this; 因此,通过简单覆盖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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM