Django forms, POST - django

Django beginner.
I need to get user's choices and show the information to the user based on the data entered by the user.
For this I am using a form to fetch the data from url1 and I am posting the data to another url, url2 whose view function will process the data and show the information. All working fine.
I don't want to save the form's data into the database, that's why I am not redirecting after validating the form in url1 views' post method.
POST data can't go with redirects.
If I validate as well as process the form in url1 then I need to display the information in url1 itself, I need the urlname changed to url2 when displaying the information.
Is there any standard way to do this thing?

You should use the session for that. Save the data to the session on submit in view1, then redirect to view2 and get the data from the session for display.

Related

Django post form fields to another page

I have a Django form that takes filter options for a report. The report page is a separate view that renders the report based on the form data.
My first pass at this, I simply set the action of the form to the report page and method to GET. The form data was then passed directly the report view via the querystring which I would use GET to retrieve. The problem with this was that this bypassed form validation since the the form did not post back to its own view.
My next pass, I removed the form action (so it would post back to itself) and used form_valid (I am using class based views) to encode the form data and redirect to the report view like so:
ReportOptionsView(FormView)
form_class = OptionsForm
template_name = 'my_report_options.html'
report = reverse_lazy('my_report')
def form_valid(self, form):
qstr = urlencode(form.cleaned_data)
return redirect(self.report+"?"+qstr)
The report page works the same -I just retrieve the information from the querystring to filter the models and display the report.
I would prefer the form data not appear on the querystring of the report page. When I tried to redirect to the report page using a POST method is where I starting having trouble. Even going back to my original flow setting the form action to the report page (thus losing validation) and setting the form method to POST, I got 405 errors. I realize that there may be ways to do this using Javascript, but would prefer to stick with Django/Python
My question is, what is the proper method in Django to take cleaned data from a validated form and POST that data to separate view so that the form data is not exposed in the URL?

How to get POST parameters in a Django view protected by #login_required?

Django's login_required decorator requires a login before proceeding to any functionality in a view function. If a user is not logged in, they will be redirected through a login screen.
If the HTTP request is a GET, Django sometimes puts the GET destination in a parameter ("next"). Or, Django also has a LOGIN_REDIRECT_URL setting that is a URL a user is forwarded to after login.
If the HTTP request is a POST, that POST data is lost during the login process.
What is the best way to preserve the original URL and POST data for the original POST request, and re-post that data after the user is logged in?
I imagine putting something in a session, but I'd have to intercept the POST data and URL on the way through login_required. The best practice is not obvious to me.

How do I get the existing value of the fields in a django form when going back in the browser?

I'm trying to understand how django populates the data in a form when I go back in a browser
In the debugger when I go back in the browser it generates a GET request, not a POST and so the form is not bound and has no self.data.
How do I get the existing values of the fields in a django form when going back in the browser?
(note I am using session with database backend)
While creating the form use initial data.
data = { 'field_1': 'value'}
my_form = MyForm(initial=data)
The error is in the question. The data is coming from the browser cache not the backend.

How to add fields to a request?

I have a login page. Upon submission if 'webmail' is selected, the request
should be redirected to the webmail server, with the credentials submitted but
under different keys. Here's what I'm trying now:
if form.validate_on_submit():
if form.destination.data == 'webmail':
form.rc_user.data = form.email.data
form.rc_password.data = form.password.data
return redirect('https://example.com/webmail/', code=307)
This almost works: the POST is redirected to webmail. However the values
submitted are the default values, not the assigned values.
I have some more issues though:
the keys should be _user and _pass, but Flask seems to blow up with
leading-underscore field names.
I do not want to add these fields to the original class. I want to subclass
upon submission, somewhat as follows:
if form.validate_on_submit():
if form.destination.data == 'webmail':
class WebmailLoginForm(LoginForm):
rc_user = EmailField('user', default=form.email.data)
form = WebmailLoginForm(request.form)
return redirect('https://example.com/webmail/', code=307)
When I do this, the added fields show up as UnboundField and are not
submitted.
When issuing a redirect, the browser is simply told to resubmit to another server. I.e. it's too late for the server to influence the request.
So either: start a new request, or use javascript to change the submit target.
Thanks to my colleague Johan for kickstarting my brain.

Django linking to page without putting data in URL

So I have a a HTML page with a table in it which contains details of a certain model. Each row contains details of a different object. I have a cell for a button as well.
Now, what I want is for the user to be able to click on the button and it should take them to the appropriate page for that particular user that they've clicked on. The way I can do this now is by creating a URL that takes a user_id argument along with a view to redirect it to a template. This url can then be added to the button. However, I don't want the user_id to be shown in the URL (being shown in Inspect Element is okay (as in the row ID)).
This rushed, so sorry. How can I do this?
Is there a way to do it without putting any information whatsoever in the URL?
Thank you!
One way to do this is to send user ids from a POST instead of a GET for getting the user info, when the user clicks the button, you submit a hidden form which contains user_id (which you will update accordingly) and pass it to Django. On this POST call you will wire a render of the page for the user according to the POST parameter you are expecting containing the user id.
You can read the post parameters on a request via:
request.POST.get('user_id')
The downside of this approach is that you won't be able to share the link for a certain user, because the link will only contain the get parameters.
Maybe you can refactor your application to use some kind of SPA framework on the front-end. In this way you can load any content on your current page and the URL never changes if you don't want. Take a look for example at AngularJS or Durandal. Both works well with Django.
You can also solve the problem by using POST instead of GET but in my opinion that's not a very elegant solution because POST requests should be used just when you send data to the server.
If your worried about security I don't think keeping the user_id secret will be effective but if for some other reason you have to do this put it in session and redirect to user page without any parameters.
Put your table inside a form and store the id in an attribute of the button on each row:
<button class="mybutton" data-id="{{ my_object.id }}">view</button>
Put a hidden field at the bottom of your form:
<input type="hidden" id="user_id" name="user_id" />
Javascript:
$("table .mybutton").click(function(e) {
e.preventDefault();
$("#user_id").val($(this).attr("data-id"));
$("#my_form").submit();
});
In your table view:
if request.method == "POST":
request.session["user_id"] = request.POST.get('user_id')
return redirect("user_page")
In your details view:
user_id = request.session["user_id"]
creating urls
If the url is relevant to the user; then use the user_id; e.g. http://example.com/mysite/users/<user_id>/userstuff.
obfuscation is not security.
obfuscation is not a permission scheme.
Other possibilities:
http://example.com/mysite/users/<uniqueusername>/userstuff.
http://example.com/mysite/users/<slug>/userstuff.
http://example.com/mysite/users/<encoded>/userstuff, where encoded is either 2-way encoding, or a field on the user model that is unique.
getting logged in user (request.user)
If the url has nothing to do with the user, but you need to get the authenticated user then read the docs: https://docs.djangoproject.com/en/1.7/topics/auth/default/#authentication-in-web-requests.
def my_view(request):
if request.user.is_authenticated():
# use request.user
else:
# something else