I'm new to both web development and django so maybe that's a noob question.
I want to do the following:
Ask user to fill some form and submit it.
Then, parse and format the content and display it back to the user to let him verify it.
User can accept the result or go back to the previous view, update data and resend.
This is as far as I can think:
views.py
def add_content(request):
if request.method == 'POST':
form = AddContentForm(request.POST)
if form.is_valid():
content = form.save(commit=False)
return verify_content(request, content)
else:
form = AddContentForm()
return render(request, 'myapp/add_content.html', {'form' : form})
def verify_content(request, content):
return render(request, 'myapp/verify_content.html', {'content' : content})
The verify_content template will obviously contain two buttons ('back', 'ok'), but I don't know how to pass the content object to a view for saving it in the db, or send it back to the previous view from there. Should I use js? Can i do it with just server side code?
Maybe my whole logic is wrong. Should I save the object in the db before verification and then delete it if needed (sounds ugly)? What is a good way to implement this?
Thanks in advance for your time.
You could use the users session for this:
request.session['content'] = content
and in the view where the user should verify his input do:
content = request.session['content']
and voilá you got the content between 2 views.
Django also secures that users can't tinker with its data by either saving it server side, or in a signed cookie.
I would save the form with commit=True in the add_content view, and would add a verified field or something to the model. Then you can append the pk as GET parameter to the link which will get you back to add_content view from verify. You can extract the parameter from request.GET dict.
Related
I have a basic view that retrieves some data, renders my page and sends some data to this page:
def myview(request)
one = values.objects.get(user=request.user).address
two = values.objects.get(user=request.user).number
return render(request, "main/mytemplate.html",
context={'address': one, 'numbers': two})
So the values retrieved by those two queries are shown on my page.
Now, on the same page, called mytemplate.html, i'm using another view, which is supposed to handle a form and some other operations:
def secondview(request):
if request.method == 'POST':
if 'button1' in request.POST:
form = MyForm(request.POST)
# check whether it's valid:
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
return HttpResponseRedirect(request.path_info)
else:
form = MyForm()
return HttpResponse('it works!')
How can i use the data retrieved by those two queries in the second view? The queries are executed when the page is loaded by the first view. Then, in the same page the second view is used. I want to use the two variables one and two in the second view. Is there a way to do this in Django?
Why don't you make the same queries in the second view? Because i would like the second form to be as fast as possible in terms of reload, without having to do a DB query each time that view is used. Also, since i already retrieved those values when the page is opened, it would be a waste to do that again.
I don't know if this question is clear enough, but the core of it is: can i pass variables/data between two views in django?
You have few options:
Simplest way: include this data in request to the second view (as part of the form data, see an example below). You might even use a single view: if POST was send - store data else do request and show it on a page.
Use cache for that (see an example below) - But I'd recommend to use Django built-in package. Here is a basic example how to use it
Use Django Sessions (see an example below) - it is working option despite of that they have another purpose. When customer is loaded Django will load full session record, so you'll have all data in request.session variable. But that is bad practice: you can get a lot of data duplication and increased database memory consumption.
Use API (e.g. using DjangoRestFramework) together with usual Django app. So you'll just get data you need, and when you need. These API requests can also be cached so it is fast solution.
Yes, you can use session to pass data across views. A session works like a temporary server storage and keeps the needed data in a dictionary form.
For instance, add the following lines to myview:
request.session['one'] = one
request.session['two'] = two
Then, retrieve the data in secondview by referring to the session:
one = request.session['one']
two = request.session['two']
you can use cookies. but if you want more secure your request i suggest to you using redis and the python client for redis
file settings.py
redis = redis.Redis(host='localhost', port=6379, db=0)
file views.py
def view1(request):
redis.set("foo", "boo")
def view2(request):
boo = redis.get("foo")
Why not just saving the results of the two queries as hidden fields in the form rendered by the first template ?
<form ...>
<input type="hidden" id="address" name="address" value="{{address}}">
<input type="hidden" id="numbers" name="numbers" value="{{numbers}}">
...
Then, you can either add 'address' and 'numbers' form fields to MyForm
address = forms.CharField(widget=forms.HiddenInput(), required=False)
...
or just retrieve the values from request.POST
I'm having big trouble understanding the whole forms business in django. As I understand it the cleaned form data is a dictionary. So all my defined form fields should be in the dictionary like so: {'definedform': userinput, ...}. Is this correct?
I want to create a form in which a user can input data. This data should then be send to a different view, in which the inputted data is rendered with a latex template (and subsequently rendered into a pdf). This works more or less fine if I define the context in the /create_pdf/ view and grab the user input manually. But I suppose there is a nicer way. What I think should work:
def index(request):
if request.method == "POST":
persoform = PersonalForm(request.POST, prefix='personal')
if persoform.is_valid():
content = persoform.cleaned_data()
content = Context(content)
return HttpResponseRedirect('/create_pdf/')
else:
persoform = PersonalForm()
return render(request, 'app/template.html', {'persoform': persoform})
And in my /create_pdf/ view:
def create_pdf(request):
template = get_template('app/latextemplate.tex')
rendered_tpl = template.render(content)
[...]
So, how can I make sure, to pass the data from my index view to my create_pdf view?
EDIT:
Forgot to mention: The error is "'content' not defined". So I understand that the /create_pdf/ view doesn't get content dictionary, but I have no idea how I would make sure that it does.
Put the data in to the session on submit, and pop it out in the second view.
if form.is_valid():
request.session['perso'] = form.cleaned_data
return HttpResponseRedirect('/create_pdf/')
...
def create_pdf(request):
data = request.session.pop('perso'], {})
Is there a way I can pass data from a form submission over to the 'thank you' page. The reason i'd like to do this is because I have a form on the website, where the user will select multiple fields which all contains different PDF's.
So once the user has submitted the form the idea is to re-direct them to a thankyou page, where they can view the list of pdf/files they have selected on the form.
I hope this is enough info to go on. Here are my views / models.
def document_request(request, *args):
# template = kwargs['name'] + ".html"
if request.method == 'POST':
form = forms.ReportEnquiryForm(request.POST)
print(request.POST)
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
else:
form = forms.ReportEnquiryForm()
return render_to_response('test.html',{'form':form})
def thank_you(request):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
My initial idea was to pass the data to a new view called thank_you. But not this is possible.
class DocumentUpload(models.Model):
name = models.CharField(max_length="200")
document_upload = models.FileField(upload_to="uploads/documents")
def __unicode__(self):
return "%s" % self.name
class DocumentRequest(models.Model):
name = models.CharField(max_length="200")
company = models.CharField(max_length="200")
job_title = models.CharField(max_length="200")
email = models.EmailField(max_length="200")
report = models.ManyToManyField(DocumentUpload)
def __unicode__(self):
return "%s" % self.name
form.py
class ReportEnquiryForm(forms.ModelForm):
class Meta:
model = models.DocumentRequest
fields = ('name', 'company', 'job_title', 'email', 'report')
If you need anymore info, please ask :)
You've saved the user's submission in a DocumentRequest object. So you can pass the ID of that object in the URL when you redirect, and in the thank_you view you can get the DocumentRequest and render the list.
Edit The idea is to make the thank_you page like any other view that accepts a parameter from the URL:
url(r'thanks/(?P<id>\d+)/$, 'thank_you', name='thank_you')
and so the POST part of the form view becomes:
if form.is_valid():
docrequest = form.save()
return HttpResponseRedirect(reverse('thank_you', kwargs={'id': docrequest.id}))
and thank_you is:
def thank_you(request, id):
docrequest = DocumentRequest.objects.get(pk=id)
return render_to_response('thankyou.html',
{'docrequest' : docrequest },
context_instance=RequestContext(request))
Second edit
As others have suggested, this makes it possible for anyone to see the request. So a better solution is to put it in the session:
docrequest = form.save()
request.session['docrequest_id'] = docrequest.id
and in thank_you:
def thank_you(request):
if not 'docrequest_id' in request.session:
return HttpResponseForbidden
docrequest = DocumentRequest.objects.get(request.session['docrequest_id'])
You can do as Daniel Roseman said but in this case the thank you pages can be accessed by anyone with the Ids.
Some ways to pass data between views are the following(the list is not mine.):
GET request - First request hits view1->send data to browser -> browser redirects to view2
POST request - (as you suggested) Same flow as above but is suitable when more data is involved
Using django session variables - This is the simplest to implement
Using client-side cookies - Can be used but there is limitations of how much data can be stored.
Maybe using some shared memory at web server level- Tricky but can be done.
Write data into a file & then the next view can read from that file.
If you can have a stand-alone server, then that server can REST API's to invoke views.
Again if a stand-alone server is possible maybe even message queues would work.
Maybe a cache like memcached can act as mediator. But then if one is going this route, its better to use Django sessions as it hides a whole lot of implementation details.
Lastly, as an extension to point 6, instead of files store data in some persistent storage mechanism like mysql.
The simplest way is to use sessions. Just add the id to the session and redirect to the thank you view, you read the id value and query the db with that id.
I'm brand new to django and fairly new to programming in general. I've done the django tutorial and searched the web for an answer to this question, but to no avail, so now I'm here. I am confused how post works with django. All of the tutorials I've looked at how have a return function in views that displays the webpage. I get that. But then how does a user update data if the page is being rendered from that return statement? After the return there can't be any more updates because the function stops, right? What am I missing here? Any help would be greatly appreciated, I'm getting fairly desperate here.
One pattern for Django views (by no means the only pattern) is to check the request method (GET or POST) at the beginning of the view. If it is POST, then handle the incoming data (before the view returns), and then return either a rendered template, or a redirect.
def view_function(request):
if request.method == 'POST':
if data_is_valid(request.POST):
save_data(request.POST)
return HttpResponseRedirect('/somewhere/good')
else:
return render('template', {'errors': what_went_wrong}
else:
return render('template')
The user updates data in the logic of the view function. That is to say, if the user wishes to update something, you place the update logic in the view function before the return. For example, you would do this:
def update(request):
item = <some model>.objects.get(<something>)
<more code>
return <something>
Usually an edit view function contains two parts -- one for updating data, and the other for displaying the update form. For example,
def user_edit(request):
if request.method == 'POST': # is this a save action?
# save the user data
user_id = request.POST.get('user_id')
username = request.POST.get('username')
description = request.POST.get('description')
user = User.objects.get(id=user_id)
user.username = username
user.description = description
user.save()
return HttpResponseRedirect('/user/') # redirect to index
else:
# show the edit form
user_id = request.GET.get('user_id')
user = User.object.get(id=user_id)
return render_to_response('/user/edit.html', { 'user': user })
There are many different choices for the if request.method == 'POST' line. You can also use if request.POST.get('user_id') to check if specified field is set, to determine if this is a save action.
When both the GET and POST methods are in the same handler class, and I want to populate form fields with user input after failed form validation, I do this...
Class CommentHandler(BaseHandler):
def get(self, form=None):
if form is None: # create new form unless populated form is passed in
form = CommentForm()
# query DB and create template context
self.render('page.html', **context)
def post(self):
form = CommentForm(self.request.POST)
if form.validate():
# populate entity with form data and save to DB
return self.redirect_to('page')
self.get(form=form) # pass populated form back to user for editing
I don't know if this is the best way to get form data back to the user, but it seems to work. My question is: how do I pass that data back into the form if the GET and POST methods are in different handler classes?
class PageHandler(BaseHandler):
def get(self):
# displays form to user
class CommentHandler(BaseHandler):
def post(self):
# processes POSTed form data...
# but if form.validate() fails,
# how can I pass the form data back to the user
# so they can edit their form input?
When you post and call validate() WTForms binds the form encoded data to the Form instance. An http POST can return a response just like a GET this is why you sometimes get those funny messages in your browser when a server application has failed validation and you try to refresh. Its because the refresh action is going to invoke the GET processing pipeline and you will lose your POST data.
What you need to do instead of delegating back to the get implementation you just need to render a response from your post implementation that passes Form instance with the data bound to it back in the response. If you have set your template up in the recommended way the data will automatically show up in the appropriate fields. Below is a snippet of what your Handler might look like.
def post(self):
form = CommentForm(self.request.POST)
if form.validate():
# populate entity with form data and save to DB
return self.redirect_to('page')
# If we are here it means we failed validation
# We need to send back the data the use supplied
# with error messages so we can re-render the form
# with their data and error messages indicating why
# it was rejected.
self.render('page.html', form=form) # pass populated form back to user for editing
This of course assumes that your page.html knows what to do with the Form instance.
Here's one workflow that may help to solve your problem. I chose to provide a workflow instead of a specific code snippet in order to avoid being too prescriptive in the technology you use to solve the problem.
GET
if session contains form data:
add form data from session to template context
display form
POST
if form validates:
clear this form data from session
save to db
else:
save form data to session
redirect to GET handler