How to implement a "back" link on Django Templates? - python

I'm tooling around with Django and I'm wondering if there is a simple way to create a "back" link to the previous page using the template system.
I figure that in the worst case I can get this information from the request object in the view function, and pass it along to the template rendering method, but I'm hoping I can avoid all this boilerplate code somehow.
I've checked the Django template docs and I haven't seen anything that mentions this explicitly.

Actually it's go(-1).
<input type=button value="Previous Page" onClick="javascript:history.go(-1);">

This solution worked out for me:
Go back
But that's previously adding 'django.core.context_processors.request', to TEMPLATE_CONTEXT_PROCESSORS in your project's settings.

Back
Here |escape is used to get out of the " "string.

Well you can enable:
'django.core.context_processors.request',
in your settings.TEMPLATE_CONTEXT_PROCESSORS block and hook out the referrer but that's a bit nauseating and could break all over the place.
Most places where you'd want this (eg the edit post page on SO) you have a real object to hook on to (in that example, the post) so you can easily work out what the proper previous page should be.

You can always use the client side option which is very simple:
Back

For RESTful links where "Back" usually means going one level higher:
<input type="button" value="Back" class="btn btn-primary" />

All Javascript solutions mentioned here as well as the request.META.HTTP_REFERER solution sometimes work, but both break in the same scenario (and maybe others, too).
I usually have a Cancel button under a form that creates or changes an object. If the user submits the form once and server side validation fails, the user is presented the form again, containing the wrong data. Guess what, request.META.HTTP_REFERER now points to the URL that displays the form. You can press Cancel a thousand times and will never get back to where the initial edit/create link was.
The only solid solution I can think of is a bit involved, but works for me. If someone knows of a simpler solution, I'd be happy to hear from it. :-)
The 'trick' is to pass the initial HTTP_REFERER into the form and use it from there. So when the form gets POSTed to, it passes the correct, initial referer along.
Here is how I do it:
I created a mixin class for forms that does most of the work:
from django import forms
from django.utils.http import url_has_allowed_host_and_scheme
class FormCancelLinkMixin(forms.Form):
""" Mixin class that provides a proper Cancel button link. """
cancel_link = forms.fields.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
"""
Override to pop 'request' from kwargs.
"""
self.request = kwargs.pop("request")
initial = kwargs.pop("initial", {})
# set initial value of 'cancel_link' to the referer
initial["cancel_link"] = self.request.META.get("HTTP_REFERER", "")
kwargs["initial"] = initial
super().__init__(*args, **kwargs)
def get_cancel_link(self):
"""
Return correct URL for cancelling the form.
If the form has been submitted, the HTTP_REFERER in request.meta points to
the view that handles the form, not the view the user initially came from.
In this case, we use the value of the 'cancel_link' field.
Returns:
A safe URL to go back to, should the user cancel the form.
"""
if self.is_bound:
url = self.cleaned_data["cancel_link"]
# prevent open redirects
if url_has_allowed_host_and_scheme(url, self.request.get_host()):
return url
# fallback to current referer, then root URL
return self.request.META.get("HTTP_REFERER", "/")
The form that is used to edit/create the object (usually a ModelForm subclass) might look like this:
class SomeModelForm(FormCancelLinkMixin, forms.ModelForm):
""" Form for creating some model instance. """
class Meta:
model = ModelClass
# ...
The view must pass the current request to the form. For class based views, you can override get_form_kwargs():
class SomeModelCreateView(CreateView):
model = SomeModelClass
form_class = SomeModelForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["request"] = self.request
return kwargs
In the template that displays the form:
<form method="post">
{% csrf token %}
{{ form }}
<input type="submit" value="Save">
Cancel
</form>

For a 'back' button in change forms for Django admin what I end up doing is a custom template filter to parse and decode the 'preserved_filters' variable in the template. I placed the following on a customized templates/admin/submit_line.html file:
<a href="../{% if original}../{% endif %}?{{ preserved_filters | decode_filter }}">
{% trans "Back" %}
</a>
And then created a custom template filter:
from urllib.parse import unquote
from django import template
def decode_filter(variable):
if variable.startswith('_changelist_filters='):
return unquote(variable[20:])
return variable
register = template.Library()
register.filter('decode_filter', decode_filter)

Using client side solution would be the proper solution.
Cancel

Related

Django: I want to track from which url a request was made? [duplicate]

I have a Post model that requires a certain category before being added to the database, and I want the category to be generated automatically. Clicking the addPost button takes you to a different page and so the category will be determined by taking a part of the previous page URL.
Is there a way to get the previous page URL as a string?
I have added my AddPost button here.
<aside class="addPost">
<article>
<form action="/Forum/addPost">
<input type="submit" name="submit" value="Add Post"/>
</form>
</article>
</aside>
You can do that by using request.META['HTTP_REFERER'], but it will exist if only your tab previous page was from your website, else there will be no HTTP_REFERER in META dict. So be careful and make sure that you are using .get() notation instead.
# Returns None if user came from another website
request.META.get('HTTP_REFERER')
Note: I gave this answer when Django 1.10 was an actual release. I'm not working with Django anymore, so I can't tell if this applies to Django 2
You can get the referring URL by using request.META.HTTP_REFERER
More info here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
I can't answer #tryingtolearn comment, but for future people, you can use request.META['HTTP_REFERER']
Instead of adding it to your context, then passing it to the template, you can place it in your template directly with:
Return
A much more reliable method would be to explicitly pass the category in the URL of the Add Post button.
You can get the previous url in "views.py" as shown below:
# "views.py"
from django.shortcuts import render
def test(request):
pre_url = request.META.get('HTTP_REFERER') # Here
return render(request, 'test/index.html')
You can also get the previous url in Django Template as shown below:
# "index.html"
{{ request.META.HTTP_REFERER }}

How to use html form 'placeholder' property with standard view?

I am new to Django and am using Django's user auth package (django.contrib.auth) for user login, password reset, etc.
Now, while everything works just fine, on the logon form, I'd like to use the html-placeholder property. How can I use / populate this? I did find some answers (e.g. this one) but I do not understand where to place this code, how to extend the view / form or even the model (e.g. adding new fields) as this gets delivered with the standard package.
Right now, I have added the following:
forms.py
from django import forms
from django.contrib.auth.forms import AuthenticationForm
class LoginForm(forms.Form):
username = forms.CharField(label='username')
password = forms.CharField(label='password')
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.fields['username'].widget.attrs['placeholder'] = 'Username'
self.fields['password '].widget.attrs['placeholder'] = 'Password'
I am not sure what I need to do in urls.py or models.py or anywhere else for the code to be executed.
I found the following solution:
installed bootstrap4
using the following tag in my html:
{% bootstrap_field form.password field_class="field" placeholder="Password" show_label=False %}
I believe by adding the widgets to your forms init, when you instantiate the form in your template the placeholder should appear like intended. Did you just try rendering your form such as {{form}} in the template and see if it rendered it? I dont think bootstrap4 is necessary but it is a great tool.

web.py rendering wrong page, path not correct

I'm working with web.py on a project with a user authentication component. This project has a /demo directory and a /register directory.
The problem is that for some reason, clicking the button meant to travel to the /register directory goes to /demo instead. Interestingly, if I type /register at the end of the URL, /register appears properly. The URL is different in this case, lacking the ?register=Register part that comes from clicking the button.
The specific error that appears when I click on the register button is this:
type 'exceptions.TypeError'> at /demo
__template__() takes exactly 1 argument (0 given)
The code that it points to as a problem area is this:
class demo:
def GET(self):
render = create_render(session.get('privilege', 0))
return '%s' % render.demo() # Specifically this line generates the error.
I don't think there is any weird fall through, as in the code there are two classes implemented between the register class and the demo class. They're actually implemented in the order they're displayed in the urls list, with reset and profile in between the two.
I don't think there is anything wrong with my urls list:
urls = (
'/', 'index',
'/register', 'register',
'/reset', 'reset',
'/profile', 'profile',
'/demo', 'demo',
'/entropy', 'entropy',
)
Here also is the HTML code for the buttons:
<form action="demo" method="GET">
<input type="submit" name="demo" value="Demo"/>
<form/>
<form action="register" method="GET">
<input type="submit" name="register" value="Register"/>
</form>
Other potentially relevant bits of code:
register.GET():
class register:
def GET(self):
reg_form = forms.registration_form()
render = create_render(session.get('privilege'))
return render.register()
create_privilege(privilege): (Explained here, at #4)
def create_render(privilege):
if logged():
render = web.template.render('templates/logged', base='base')
else:
render = web.template.render('templates/', base="base")
return render
So, I suppose, the final question is this: Why is the button rendering the wrong page? I can post more code if need be!
Thank you!
I fixed it! There was an issue in one of the HTML buttons, a form tag was not closed properly. What a silly error.

How to get the previous URL from a POST in Django

I have a Post model that requires a certain category before being added to the database, and I want the category to be generated automatically. Clicking the addPost button takes you to a different page and so the category will be determined by taking a part of the previous page URL.
Is there a way to get the previous page URL as a string?
I have added my AddPost button here.
<aside class="addPost">
<article>
<form action="/Forum/addPost">
<input type="submit" name="submit" value="Add Post"/>
</form>
</article>
</aside>
You can do that by using request.META['HTTP_REFERER'], but it will exist if only your tab previous page was from your website, else there will be no HTTP_REFERER in META dict. So be careful and make sure that you are using .get() notation instead.
# Returns None if user came from another website
request.META.get('HTTP_REFERER')
Note: I gave this answer when Django 1.10 was an actual release. I'm not working with Django anymore, so I can't tell if this applies to Django 2
You can get the referring URL by using request.META.HTTP_REFERER
More info here: https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
I can't answer #tryingtolearn comment, but for future people, you can use request.META['HTTP_REFERER']
Instead of adding it to your context, then passing it to the template, you can place it in your template directly with:
Return
A much more reliable method would be to explicitly pass the category in the URL of the Add Post button.
You can get the previous url in "views.py" as shown below:
# "views.py"
from django.shortcuts import render
def test(request):
pre_url = request.META.get('HTTP_REFERER') # Here
return render(request, 'test/index.html')
You can also get the previous url in Django Template as shown below:
# "index.html"
{{ request.META.HTTP_REFERER }}

Distinguish a user depending on which page he comes from. Django

I would like to know if there is a way to differentiate a user depending on which page he comes from.
In my template, I would like to display something only if the user comes from a specific view (I wan't to display the same page I display for the others users, but adding a popup telling him something).
Is there a way to do that?
Thank you for your help
If possible, use HTTP_REFERER request header. This works in most cases. If it doesn't, then you'll have to maintain it in the session.
To know what view function would a URL call, use django.core.urlresolvers.resolve. I think this is not documented but it's pretty straight forward, example:
In [1]: from django.core import urlresolvers
In [2]: urlresolvers.resolve('/admin/')
Out[2]: ResolverMatch(func=<function index at 0xadb1924>, args=(), kwargs={}, url_name='index', app_name='admin', namespace='admin')
In [3]: urlresolvers.resolve('/admin/').func
Out[3]: <function django.contrib.admin.sites.index>
Now, using that against the HTTP_REFERER in a custom template filter could look like this:
from django import template
from django.core import urlresolvers
from yourapp.views import specific_view
register = template.Library()
#register.filter
def comes_from_specific_view(request):
if not request.META.get('HTTP_REFERER', None):
return False
return urlresolvers.resolve(request.META['HTTP_REFERER']).func == specific_view
In template:
{% if request|comes_from_specific_view %}show popup{% endif %}
There are several ways to do that:
You can set a cookie in the first view, and check its value in the destination view. Don't forget to clean the cookie if the user goes through another page in between. Downside is that the user can disable cookies in his browser.
Depending on the way the user goes to the destination view (a link or a form), you can use a GET or a POST parameter.
Use sessions
The most straightforward general way I can think of is making a middleware class that saves the previous request path in the user session and then using {{ user.session.previous_page }} in the template. For example:
class ReferrerMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated():
request.user.session['previous_page'] = request.session['current_page']
request.user.session['current_page'] = request.path

Categories