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
Related
I have a page in Django that I don't want to be accessed by anyone except when they clicked the specific link that I made for that page.
I'm aware about #login_required but the problem is I want the page to be restricted to EVERYONE.
I haven't tried any code yet since I absolutely have no idea how to do it. Even google did not give me answer. Please help
I have the same problem a few months back and way I solved it by making a POST request.
Whenever any user clicks on the link present on-page, I make a POST request at the Django application with some verification token sent in the POST request body.
You can generate any simple token mechanism and check for token validity in Django view and if success allows users to access that page.
The most common way to achieve this is to use randomized links.
In pseudo code
Page 1
<a href="/router/some-random-string">
# view serves '/secret-page'
class SecretView:
def _get(request):
# display real page here
def get(request):
return HttpNotFound()
# view serves '/router/<hash:str>'
class AccessorView(SecretView):
def get(request):
# get and validate hash
# if valid, display secret page
return super()._get(request)
I am using Boostrap modal fade window which renders Django form to update my database records. And what I fail to do is not to reload the page if the user has opened the Update window and did not change anything. It will be easier to get my idea if you look at the code below:
def updateTask(request, task_id):
#cur_usr_sale_point = PersonUnique.objects.filter(employees__employeeuser__auth_user = request.user.id).values_list('agreementemployees__agreement_unique__sale_point_id',flat=True)
selected_task = Tasks.objects.get(id=task_id)
task_form = TaskForm(instance=selected_task )
taskTable = Tasks.objects.all()
if request.method == 'POST':
task_form = TaskForm(request.POST,instance=selected_task)
if task_form.has_changed():
if task_form.is_valid():
# inside your model instance add each field with the wanted value for it
task_form.save();
return HttpResponseRedirect('/task_list/')
else: # The user did not change any data but I still tell Django to
#reload my page, thus wasting my time.
return HttpResponseRedirect('/task_list/')
return render_to_response('task_management/task_list.html',{'createTask_form':task_form, 'task_id': task_id, 'taskTable': taskTable},context_instance=RequestContext(request))
The question is, is there any way to tell Django to change the url (like it happens after redirecting) but not to load the same page with same data for the second time?
It's not trivial, but the basic steps you need are:
Write some javascript to usurp the form submit button click
Call your ajax function which sends data to "checking" view
Write a "checking" view that will check if form data has changed
If data have changed, submit the form
If not, just stay on page
This blog post is a nice walkthrough of the entire process (though targeted towards a different end result, you'll need to modify the view).
And here are some SO answers that will help with the steps above:
Basically:
$('#your-form-id').on('submit', function(event){
event.preventDefault();
your_ajax_function();
});
Call ajax function on form submit
Gotta do yourself!
Submit form after checking
When a user requests a password reset, he will be redirected to /password/reset/done/ which tells the user that 'We have sent you an e-mail...', but if you directly go to that page, you get the same information, which is a little bit confusing since no email's sent to anyone.
Is there a variable which can be used in password_reset_done.html template like {{ messages }} so that with if conditions other content can show up for direct visitors? If not, is there a way to do this?
EDIT:
Please don't be hasty on labeling this as a duplicate question of How to use the built-in 'password_reset' view in Django?.
I do know how to show up that page /password/reset/done/, which is basically a stage specifically used for telling a user that "we've sent you an email so that you can follow the link to reset your password...", while the question you are refering to is about how to correctly redirect a user when password reset is successful.
What I'm looking for is a way to either rediret it to another page or show some different stuff when a user is trying to directly surf that page(well people do that sometimes) which is supposed to be viewed after he successfully submitted a password reset request. I need some kind of hook to make it work.
After checking into the codes in /account/views.py, I realized a message need to be added in previous /password/reset/ page so that it can be used in the next page which is /password/reset/done/.
Here's an example:
Add this in the form_valid method of class PasswordResetView:
get_adapter().add_message(self.request,
messages.INFO,
'account/messages/'
'password_reset_mail_sent.txt',
{'email': form.cleaned_data["email"]})
Now I can use {{ email }} in 'account/messages/password_reset_mail_sent.txt', and with {% if messages %} in password_reset_done.html I can serve direct visitors a different content.
You can use Django Messages Framework to notify the user with success or fail:
from django.contrib import messages
def my_view(request):
# your view logic goes here
if sucess_condition:
messages.success(request, 'Action accomplished with success.')
else:
messages.error(request, 'Action Failed.')
Check Messages Framework documentation for more details and customization.
I'm aware that there is a context variable called "next" which allows you to specify the URL to redirect to after a page, which is commonly used when you want to redirect a user to a login page, then redirect them back to the page they were at before the login page once they've logged in.
In my views I've resorted to manually setting it via the URL such as redirecting to /login/?next=/someurl, but this isn't a clean way. I've tried googling and such, but there is surprisingly little information about it online.
How exactly do you set the context variable "next"? My site has a form that anyone can see, but only logged in users can submit. Right now if the user isn't logged in, they will get redirected to the login page with the "?next=/someurl/" attached to it so they get sent back to the original form once they log in.
However, from the login page there is a link to the sign up page for users who don't have an account, and I want to set "next" to be the original form page so that after they sign up they are redirected back to the original form. Any ideas or advice?
It sounds like you want to not simply use next for one redirect, but persist it across two redirects:
Some form page -> login -> signup -> Get back to some form
For the login page by itself, Django provides some automatic help for this (if you use the built-in auth views). But the second hop to the signup page requires some custom code explained below.
If you are using Django's built-in login view, and have created your own template for it, then Django will pass the current value of next to the template as a context variable called (appropriately) next. You can use that in the link to your sign-up page like this:
Sign me up!
Consequently, in the view you create to handle your user signup page, the value of next will be accessible as a GET param:
def signup(request):
if request.method == 'GET':
next = request.GET.get('next', None)
if next:
# Add it as a hidden input in your signup form
# Or re-append it as a GET param
# Or stick it in the user's session, which is
# what this example does:
request.session['next'] = next
Finally, in the same signup view, when you respond to the POSTed signup form, retrieve the next value in whichever way you chose to propogate it in the GET request, and redirect to it's value.
def signup(request):
....
# POST handling section
if signup_form.is_valid():
next = request.session.get('next', None)
if next:
# See caution note below!
return redirect(next)
Caution:
Be aware that you should check and sanitize the next value before you redirect to it after processing the signup form, to prevent browser-side tampering. For example, it's common to validate that the URL belongs to your own domain (if that's appropriate) and/or that Django's resolve function is able to successfully resolve it.
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.