I have a view :
class SomeView(FormView):
form_class = SomeForm
template_name = 'app/template.html'
def get(self, request, *args, **kwargs):
form = self.form_class
return render(request, self.template_name, {form:'form'})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_Valid():
#do something here
return HttpResponseRedirect("Go somewhere from here after processing")
In the post I need to make an API call which takes data from this form. Instead of this though, I want to redirect user to another view, where he can view all the data from the form and say confirm
On submit of this confirm view I should actually make this API call.
How do I pass the form data to another view ? I can simply store the form data in a dict and pass it on from the URL, but that might expose some crucial data at the browser level.
Is there a safer way to do this ?
Thanks
Yes, there is a litle helper from django:
https://docs.djangoproject.com/en/1.5/ref/contrib/formtools/form-preview/
Related
I'm working with Pinax-Stripe library, and I want to create a custom account for the logged in user. I'm trying to rewrite the CreateCustomAccountView() as a function based view. The reason for that, is that I don't understand where CreateBankAccountView comes from and it's nowhere to be seen in the GitHub code.
So for simplicity, I have the following form:
class Form(DynamicForm):
# some form fields
def __init__(self, *args, **kwargs):
self.request = kwargs.pop("request")
views.py
def view(request):
if request.method == 'POST':
form = Form(request.POST)
else:
form = Form()
return render(request, 'dashboard/template.html', {'form': form})
I do have request in my template processor. I'm not sure why this happens and how it's possible to instantiate an empty form?
Since your form pops request without a default,
self.request = kwargs.pop("request")
you must pass the request to the form. For example:
if request.method == 'POST':
form = Form(request.POST, request=request)
else:
form = Form(request=request)
The issue you see with the class based view is legit and I guess that developer suddenly overlooked that part of the code.
If you still want to use class-based view try to replace:
form_kwargs = super(
CreateBankAccountView, self
).get_form_kwargs(
*args, **kwargs
)
With:
form_kwargs = super(CreateCustomAccountView, self).get_form_kwargs()
The class is CreateCustomAccountView, and get_form_kwargs() takes no arguments.
The basic problem is that I can not find out a way of taking the form data to pass on to the next page?
Extra information:
I read somewhere that I should use session, which seems sensible, however, serialization appears not to work with forms without a model, however, I could be wrong.
When I click submit the error is AttributeError: 'BoundField' object has no attribute '_meta'.
Form
class ItemCounterForm(Form):
item = CharField(max_length=30)
counter = IntegerFeild(min_value=1, max_value=1000)
def __init__(self, *args, **kwargs):
super(ItemCounterForm, self).__init__(*args, **kwargs)
View
class ItemCounterView(View):
FormSet = formset_factory(ItemCounterForm)
def get(self, request, *args, **kwargs):
context = {
'form': self.FormSet()
}
return render(request, 'app:template', context)
def post(self, request, *args, **kwargs):
form = self.FormSet(self.request.POST or None)
if form.is_valid():
for single_form in form:
data_form = serializers.serialize('json', single_form)
request.session['form'] = data_form
return HttpResponseRedirect(reverse_lazy('app:next_page'))
else:
context = {
'form': self.FormSet()
}
return render(request, 'app:template', context)
Thank you for your time.
If you are not intending on saving your data to the database. The simplest solution would be to pass it as parameters in the url something like the following:
url(r'^'your_url_name'/(?:(?P<item>[a-z]+)/)?(?:(?P<counter>[0-9]+)/)?$', views.'your_view_name'),
That is in case any of the parameters could be blank or null. If that is not your case remove the extra question mark symbols. For defining your view you would pass on addition of the request parameter the item and counter parameter.
The validation you could do perform it on the client side. Cheers.
I have the following view in my django app.
def edit(request, collection_id):
collection = get_object_or_404(Collection, pk=collection_id)
form = CollectionForm(instance=collection)
if request.method == 'POST':
if 'comicrequest' in request.POST:
c = SubmissionLog(name=request.POST['newtitle'], sub_date=datetime.now())
c.save()
else:
form = CollectionForm(request.POST, instance=collection)
if form.is_valid():
update_collection = form.save()
return redirect('viewer:viewer', collection_id=update_collection.id)
return render(request, 'viewer/edit.html', {'form': form})
It displays a form that allows you to edit a collection of images. The footer of my html contains a form that allows you to request a new image source from the admin. It submits to a different data model than the CollectionForm. Since this is in the footer of every view, I want to extract lines 5-7 of the code and turn it into a decorator. Is this possible and if so how might I go about doing that?
I would make a new view to handle the post of the form. And then stick a blank form instance in a context processor or something, so you can print it out on every page.
If you do want to make a decorator, i would suggest using class based views. That way, you could easily make a base view class that handles the form, and every other view could extend that.
EDIT:
Here's the docs on class based views: https://docs.djangoproject.com/en/dev/topics/class-based-views/intro/
Note, I would still recommend having a separate view for the form POST, but here's what your solution might look like with class based views:
class SubmissionLogFormMixin(object):
def get_context_data(self, **kwargs):
context = super(SubmissionLogFormMixin, self).get_context_data(**kwargs)
# since there could be another form on the page, you need a unique prefix
context['footer_form'] = SubmissionLogForm(self.request.POST or None, prefix='footer_')
return context
def post(self, request, *args, **kwargs):
footer_form = SubmissionLogForm(request.POST, prefix='footer_')
if footer_form.is_valid():
c = footer_form.save(commit=False)
c.sub_date=datetime.now()
c.save()
return super(SubmissionLogFormMixin, self).post(request, *args, **kwargs)
class EditView(SubmissionLogFormMixin, UpdateView):
form_class = CollectionForm
model = Collection
# you can use SubmissionLogFormMixin on any other view as well.
Note, that was very rough. Not sure if it will work perfectly. But that should give you an idea.
I am trying to process two forms in a Django class based view. The site contains a form called form (based on GET) for narrowing the list results of the ListView and the second form status_form (based on POST).
Both forms are required since the ListView returns a list of items. Form lets the user restrict the choices and status_forms lets the user flag incorrect items via a modal form (therefore it needs to be in the same template).
My trouble is that ListView does not come with the method post, however FormView does. My class List inherits from both classes, but when I execute the class I get the error message:
Attribute Error: 'List' object has no attribute 'status_form'
How should I change my implementation to allow the second form been processed via the post method?
class List(PaginationMixin, ListView, FormMixin):
model = ListModel
context_object_name = 'list_objects'
template_name = 'pages/list.html'
paginate_by = 10 #how may items per page
def get(self, request, *args, **kwargs):
self.form = ListSearchForm(self.request.GET or None,)
return super(List, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.status_form = StatusForm(self.request.POST or None)
if self.status_form.is_valid():
...
else:
return super(List, self).post(request, *args, **kwargs)
def get_queryset(self):
# define the queryset
...
# when done, pass to object_list
return object_list
def get_context_data(self, **kwargs):
context = super(List, self).get_context_data(**kwargs)
context.update(**kwargs)
context['form'] = self.form
context['status_form'] = self.status_form # Django is complaining that status_form is not existing, result since the post method is not executed
return context
# Django is complaining that status_form does not exist,
# result since the post method is not executed
context['status_form'] = self.status_form
Because you didn't define self.status_from in the first place.
You have defined it in get_context_data, and it's accessible from there.
You can access you object from get_context_data in your post method;
context = self.get_context_data(**kwargs)
status_form = context['status_form']
Also consider that you can define your status_form directly in post method itself without getting it from self or get_context_data.
Redesign you views to separate each Form processing in separate Views then tight them with each-other.
Views redesign:
In nutshell, let each view to do one job. You can create a View just for processing your status_form and name it like StatusFormProcessView then on your List view return it on its post method
class List(ListView);
def post(self, request, *args, **kwargs):
return StatusFormView.as_view()(request) # What ever you need be pass to you form processing view
This is just an example of it, need more work to be real.
For another example; On my website index page I have a search form. when user POST or GET the search form, The processing of searching doesn't exist in my IndexView, instead I handle the whole form stuff in separate view, If form should process on GET method, I'll override get() method, If form should process on POST, I'll override post() method to send search_form data to the view that is responsible for handling of processing the search_form.
Comments response
status_form = context['status_form']
shouldn't it be
context['status_form'] = status_form
after I created it ?
You want to get status_form from context, So you need to
status_form = context['status_form']
Anyway, your form data are available on self.request.POST
in my way of perfectionism, I'm here to ask more questions about the not-so-well-documented class-based views.
I spend like 5 hours learning about class-based views, lurking into the code and I got a question.
Maybe what I'm trying to do is stupid, and if so, just say that.
I will put a simple example:
class SearchFormView(FormView):
template_name = 'search/search.html'
form_class = SearchForm
def get(self, request, *args, **kwargs):
form = SearchForm(self.request.GET or None)
if form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return super(SearchFormView, self).get(request, *args, **kwargs)
This is a perfect valid class (it is, right?).
You have a form, and you make a GET request with a query parameter.
Works like a charm.
But lets imagine... I validate the query input to prevent some type of attack and I see that the query is malicious so I put a validation error.
With the old functions, I have a form instance (empty) and I put data in it and validation errors if needed. I always return that instance, if empty (first request) or if it filled with errors (the case of the malicious query).
The problem is with class-based views. In my get method I work with an extra instance of SearchForm so if I put validation stuff would be there and if I call get on the father it will use the instance on "form_class" that would be empty.
So, I think that there should be a way where I use the same form always, I mean: I call the request method, I pick the form_class (not create a new form), pass the data, validate and the father will return that form with the validation stuff.
Im not sure if I explained this correctly. So in short, Im creating a copy of the form in the get but I return the father get who have another copy that will be empty, so my when I display the template, there will be no errors because the form sended is empty.
Any ideas? Thanks.
Your problem is that super(SearchFormView, self).get(request, *args, **kwargs) renders its own form and own context. It's only a 3 line view function, so you should really be overriding what you need to change its behavior.
def get(self, request, *args, **kwargs):
form = SearchForm(self.request.GET or None)
if form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return self.render_to_response(self.get_context_data(form=form))
Update: alternate idea if you'd like to continue using the super call
def get(self, request, *args, **kwargs):
self.form = SearchForm(self.request.GET or None)
if self.form.is_valid():
self.mystuff = Stuff.objects.filter(title__icontains=form.cleaned_data['query'])[:10]
return super(SearchFormView, self).get(request, *args, **kwargs)
def get_form(self, form_class):
"""
Returns an instance of the form to be used in this view.
"""
return getattr(self, 'form', None) or form_class(**self.get_form_kwargs())
The problem appears to be the fact that Django class based views only populate the form kwargs if the HTTP method is POST or PUT:
class FormMixin(object):
def get_form_kwargs(self):
"""
Returns the keyword arguments for instanciating the form.
"""
kwargs = {'initial': self.get_initial()}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
I found this a bit peculiar also, since I have on occasion used a form in a GET request (eg. a "search" form), which needed to perform some basic validation. I just override the get_form_kwargs() method on such views, to also populate the kwargs['data'] item, even when the HTTP method is GET.