Inserting initial data into a SessionWizardView fo

2019-08-05 05:33发布

I've pretty much exhausted my resources on this subject, so I must submit a question to the community.

Scenario:

In my urls.py file i have a pattern like:

url(r'^entry_form/(?P<sheet_id>\d+)/', SheetWizard.as_view([Sheet1,Sheet2,Sheet3])),

When a user visits a url like "127.0.0.1/myapp/entry_form/77" I am trying to have Django render Sheet1 but with one of the fields having the value "77" initially entered.

Theroy:

My forms.py file looks similar to:

class Sheet1(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(Sheet1, self).__init__(*args, **kwargs)
        #create a helper object
        self.helper = FormHelper(self)
        #dont render the form tags
        self.helper.form_tag = False
        #Make a nice layout
        self.helper.layout = Layout(
             TabHolder(
                             Tab(
                                 'Sheet Information', #Tab name text
                                     PrependedText('sheet_field', 'Django-'), #The field with prepended text
                             )
             )
        #Now set the initial value of the field to sheet_id from url pattern
        self.fields['sheet_field'].initial = str(sheet_id)+'-'+ str( time() ).replace('.','_')
        #??? Not sure where to get sheet_id from???

Notice the last line has a variable named "sheet_id", that should be the value "77" coming from the url pattern entered by the user.

Problem:

So far I am unsure of how to access the value "sheet_id" from the url pattern in my forms.py or views.py files. Due to this being a class based view I can not simply create the keyword "sheet_id=None", aka something like this just doesn't work:

class SheetWizard(SessionWizardView, sheet_id=None):
    #this breaks

I have been able to get some data into views.py using request.GET and a url like "127.0.0.1/myapp/entry_form/?sheet_id=77" but I have no idea how to pipe that into the first form of the SessionWizardView user session.

It would be greatly appreciated if someone could help me out. And thanks for all your kind wisdom!

2条回答
【Aperson】
2楼-- · 2019-08-05 05:58

Thanks to mariodev for pointing me in the right direction.

I found this combination of code to work for my application:

[urls.py]

url(r'^entry_form/(?P<sheet_id_initial>\d+)/', SheetWizard.as_view([Sheet1,Sheet2,Sheet3])), 

[views.py]

class SheetWizard(SessionWizardView):
    #some code here
    def dispatch(self, request, *args, **kwargs):
        self.sheet_id_initial = kwargs.get('sheet_id_initial', None)
        return super(SheetWizard, self).dispatch(request, *args, **kwargs)

    def get_form_initial(self, step):
        initial = self.initial_dict.get(step, {})
        if int(step) == 0:
            initial.update({ 'sheet_id_initial': self.sheet_id_initial })
        return initial

[forms.py]

class Sheet1(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(Sheet1, self).__init__(*args, **kwargs)
        #create a helper object
        self.helper = FormHelper(self)
        #dont render the form tags
        self.helper.form_tag = False
        #Make a nice layout
        self.helper.layout = Layout(
             TabHolder(
                             Tab(
                                 'Sheet Information', #Tab name text
                                     PrependedText('sheet_field', 'Django-'), #The field with prepended text
                             )
             )
        #finally lets set the initial values
        if 'initial'in kwargs:
            initial = kwargs.pop('initial')
            print initial
            if 'sheet_id_initial' in initial:
                sheet_id_initial = initial.pop('sheet_id_initial')
                #this is where the value from sheet_id_initial is initialized in the field :)
                self.fields['sheet_id_initial'].initial = str(sheet_id_initial)+'-'+ str( time() ).replace('.','_')

However this does create a new issue. I can now visit the first form and the value from sheet_id_initial in the url pattern has been added in the form field as expected, but when the submit button is pressed a 404 page not found is returned due to POST not adding the sheet_id_initial portion to the request url.

[returned page]

""""

Page not found (404) Request Method: POST Request URL: http://192.ip.address.1:8001/sheets/entry_form/

Using the URLconf defined in first_project.urls, Django tried these URL patterns, in this order:

^admin/
^Users/
^sheets/ ^entry_form/(?P<sheet_id_initial>\d+)/

The current URL, sheets/entry_form/, didn't match any of these.

You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.

""""

[ADDITIONAL INFO]

To change the way from which to attain the sheet_id variable from either a URL pattern or POST data is rather straight forward:

First change the regex pattern in urls.py to a simple match like:

urlpatterns = patterns('',
    url(r'^entry_form/', SheetWizard.as_view([Sheet1,Sheet2,Sheet3])), 
    #url(r'^entry_form/(?P<sheet_id_initial>\d+)/', SheetWizard.as_view([Sheet1,Sheet2,Sheet3])), #'sheet_id_initial' would be picked up by "self.sheet_id_initial = kwargs.get('sheet_id_initial', None)" in SheetWizard.dispatch()

Then in views.py change the assignment in the dispatch override method like so:

class SheetWizard(SessionWizardView):
    def dispatch(self, request, *args, **kwargs):
        #self.sheet_id = kwargs.get('sheet_id_initial', None) #used to grab sheet_id_initial from url pattern
        if self.request.GET.has_key('sheet_id_initial'):
            self.sheet_id_initial = self.request.GET['sheet_id_initial'] #used to grab sheet_id_initial from QueryDict
        return super(SheetWizard, self).dispatch(request, *args, **kwargs)

Finally if you're getting MultiValueDictKeyError, I added a try-except block to get_form_initial() that tests for the existence of the the variable declared from the dispatch method if the POST data was present (but I'm not sure if this is the best way to handle this..)

so in views.py add these lines to get_form_initial():

class SheetWizard(SessionWizardView):
    def get_form_initial(self, step):
        initial = self.initial_dict.get(step, {})
        if step == "0":
            try:
                self.sheet_id_initial #check for variable existence 
                #if we get past this line then the variable existed and we can update initial data
                initial.update({ 'sheet_id_initial': self.sheet_id_initial })
            except:
                #variable didn't exist so lets just move on..
                pass                 
        return initial

[UPDATE - Best Fix So Far]

This sessionwizard and class based views can be a bit tricky. I found that both of those previous approaches have some flaw in one way or another and realized I was over complicating things a bit. But most of the previous code was essential but instead of POST or URL patterns I'm storing the POST data in a session and accessing it across forms at different URLs!!

now I have a form that the user starts from at say the address: "http://192.ip.address.1:8001/sheets/start/" which has two fields in the form: "part_id_initial" and "sheet_id_initial"

when submit is pressed the user is redirected to: "http://192.ip.address.1:8001/sheets/entry_form/" however there is a restriction of HTTP that POST data cannot go with redirects.

so you can save the session POST data and use it in the another form by modifying the above code like so:

urls.py

url(r'^start/', 'process_forms.views.GetStarted'),
url(r'^entry_form/', SheetWizard.as_view([Sheet1,Sheet2,Sheet3])), 

views.py

def GetStarted(request):
    form = gettingStarted(request.POST or None)
    if request.method == 'POST':
        if form.is_valid():
            data = form.cleaned_data
            request.session['_old_post'] = request.POST #Store POST to be passed to SheetWizard
            return redirect('/sheets/entry_form/')            
    return render_to_response('get_started.html',
                              locals(),
                              context_instance=RequestContext(request))

class SheetWizard(SessionWizardView):
    def dispatch(self, request, *args, **kwargs):
        old_post = request.session.get('_old_post') #Retrieve old POST data to set initial values on session form 
        if old_post.has_key('sheet_id_initial') and old_post.has_key('part_id_initial'):

            self.sheet_id_initial = old_post['sheet_id_initial'] #used to grab sheet_id from QueryDict
            self.part_id_initial = old_post['part_id_initial'] #used to grab sheet_id from QueryDict
        return super(SheetWizard, self).dispatch(request, *args, **kwargs)

The rest is pretty much exactly the same as above. Saving the POST data in to the session has been cleanest and most elegant fix I've found thus far.

Hope this helps!!

查看更多
成全新的幸福
3楼-- · 2019-08-05 06:07

Use the wizard's dispatch method:

def dispatch(self, request, *args, **kwargs):
    self.sheet_id = kwargs.get('sheet_id', None)
    return super(SheetWizard, self).dispatch(request, *args, **kwargs)

Then use self.sheet_id within get_form_initial method to populate initial value for your form.

查看更多
登录 后发表回答