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

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

Related

implementation difficulties in some part of views.py file in django cms

My views.py code:
from django.template import Context, loader, RequestContext
from django.http import HttpResponse
from skey import find_root_tags, count, sorting_list
from search.models import Keywords
def front_page(request):
if request.method == 'get' :
str1 = request.getvalue['word']
fo = open("xml.txt","r")
for i in range(count.__len__()):
file = fo.readline()
file = file.rstrip('\n')
find_root_tags(file,str1,i)
list.append((file,count[i]))
sorting_list(list)
for name, count in list:
s = Keywords(file_name=name,frequency_count=count)
s.save()
fo.close()
return HttpResponseRedirect('/results/')
else :
str1 = ''
list = []
template = loader.get_template('search/front_page.html')
c = RequestContext(request)
response = template.render(c)
return HttpResponse(response)
def results(request):
list1 = Keywords.objects.all()
t = loader.get_template('search/results.html')
c = Context({'list1':list1,
})
return HttpResponse(t.render(c))
#this for everyone.
the flow is this:
1) I run my app on the server .
2)It shows me the search page due to the else part of the view "def front_page(request)", now I want to execute the if part of the view "def front_page(request)" because I want to execute my python code written there and the redirected to the view "def results(request)", how can I do that ?
3) what should I mention in "action" of the front_page.html and in urls.py so that I can get back to the same view again. because I could'nt get back to the same view that I want it is repetitively showing me the same search page.Please help.
To enlarge upon the answer posted by #Barnaby....by using action='#' your form will be posted to the same url as the url used in the get request for the form.
Then in your view code, you have logic that says - if the request for this url is a GET request then do the work to configure the form, otherwise, you assume it is a POST and then you can handle the response.
Additionally I would advise that the your view explicitly checks that the request is a POST and if not make the assumption that it is a GET, rather than the other way around (as you have it), this is safer, as GET and POST are not the only request types, and you definitely need to know that you are dealing with a POST request if you want to deal with variables submitted in the POST request.
Hope that helps
Short answer: action="#". This is a HTML trick to post back to the current URL.
The general answer to how to reference a view in a template is to use the url tag. You may also want to consider using Django's forms functionality.

Persistent "CSRF token missing or incorrect.". Jinja and django-registration setup

I receive this message:
CSRF token missing or incorrect.
In most forums, the tell you to get the {% csrf_token %} in the form, and i do have it.
Also i have in my settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.csrf",
"django.contrib.auth.context_processors.auth",
)
I am using jinja, which didn't seem to use CSRF, but then i installed django registration and i got lost, since, it seems to be using some other views, that i don't have access to so to say, they are not written by me, and i can't figure out where they are. "standard auth views" as they call them. So i am unable to add "RequestContext".
Any ideas what's going on and how I can get it going? thanx
You might have to rewrite the django-registration view manually. Looks like there's an issue with how Jinja likes to do things and how Django wants to configure template loaders..
To look at the standard auth views, just look under "site-packages" in your python installation.
You could try wrapping the standard auth views like this:
from django.contrib.auth.views import login, logout
from django.views.decorators.csrf import csrf_protect
#csrf_protect
def my_wrapped_login_view(request):
return login(request)
#csrf_protect
def my_wrapped_logout_view(request):
return logout(request)
I basically imported Django's standard auth views and called them with my own, which have the csrf_protect decoration. It's worth a shot.
Have you also got the standard Django Templating system installed? That will be required for most apps that are distributed with templates.
For CSRF, a context processor inserts the variable 'csrf_token' into the response context that it retrieves from the middleware if enabled. Now all you have to do, is make sure that it's apart of your form.
This is straight out of django.core, and is subject to change at any time.
if csrf_token:
if csrf_token == 'NOTPROVIDED':
return mark_safe(u"")
else:
return mark_safe(u"<div style='display:none'><input type='hidden' name='csrfmiddlewaretoken' value='%s' /></div>" % csrf_token)
However, seeing that, all you really need to know is that you have to have an input type named csrfmiddlewaretoken with the value of context.get('csrf_token','') within your form and that's all she wrote.
This answer isn't specific to django-registration, but just using Django with Jinja2 in general.
Django's CsrfViewMiddleware sets the csrf_token cookie if it determines that you have accessed the csrf_token context member. Unfortunately, Jinja2 rendering doesn't occur until after Django's middleware executes. As a result, the cookie doesn't get set, and therefore does not match the form, and you will get the 403 error.
To get around this issue, you need to access context['csrf_token'] at some point before you finish processing the response.
If you're using class-based views, you can create a CsrfProtectMixin:
class CsrfProtectMixin(object):
def render_to_response(self, context, **response_kwargs):
# Csrf processing happens when using a RequestContext.
# Be sure to use one.
if not isinstance(context, RequestContext):
context = RequestContext(self.request, context)
# Force access to csrf_token due to the way jinja2 renders the template
# after middleware has finished processing. Otherwise, the csrf cookie
# will not be set.
str(context.get('csrf_token'))
return super(CsrfProtectMixin, self).render_to_response(context, **response_kwargs)
And then in your view class:
class MyView(CsrfProtectMixin, TemplateView):
def get(self, request, *args, **kwargs):
context = {}
return self.render_to_response(context)
If you're not using class-based views, you can do something like:
def my_view(request):
context = RequestContext(request)
str(context['csrf_token']) #force access to the csrf_token
return render_to_response('template.html', context)
Or perhaps monkey patch render_to_reponse with the logic in the class, above.
The most straightforward answer to this one is to just put {% csrf_token %} within the form tag in your template/html.
I just switched off the csrf middleware in the settings like so, and now it works:
#'django.middleware.csrf.CsrfViewMiddleware',

Django: Template context processor request variable

I am trying to implement django-facebookconnect, for I need to check if a user logged in via Facebook or a regular user.
At the template, I can check if user logged in via facebook by checking request.facebook.uid
such as:
{% if is_facebook %}
{% show_facebook_photo user %}
{% endif %}
For this, I need to pass is_facebook': request.facebook.uid to the template and I will be using this in everywhere thus I want tried to apply it to an existing template context processor and call the snipplet above at the base.html, and it works fine for Foo objects:
def global_variables(request):
from django.conf import settings
from myproject.myapp.models import Foo
return {'is_facebook': request.facebook.uid,'foo_list': Foo.objects.all()}
I can list Foo objects at any view without any issue however it fails for this new is_facebook, it simply returns nothing.
If I pass 'is_facebook': request.facebook.uid in every single view , it works but I need this globally for any view rendering.
If you have access via the request object, why do you need to add a special is_facebook boolean at all? Just enable the built-in django.core.context_processors.request and this will ensure that request is present in all templates, then you can do this:
{% if request.facebook.uid %}
It could be a timing issue. Make sure that the Common middleware comes before the facebook middleware in your settings file. You can probably debug and see when the facebook middleware is modifying the request and when your context processor is invoked. That may give you some clue as to why this is happening. But, as Daniel said, you can always just use the request object in your templates.

Custom traversal and page templates

Using Marius Gedminas's excellent blog post, I have created a custom traverser for a folder in my site.
This allows me to show: http://foo.com/folder/random_id
Instead of: http://foo.com/folder/object.html?id=random_id
The configuration side works great, I can catch the random_ids and search through my messages for the correct one, ready to display.
My problem is that I'm unsure how to then display the data via my usual page templates - at the TODO point in his original code ;)
if name == 'mycalendar':
mycalendar = ... # TODO: do something to get the appropriate object
return mycalendar
Usually I'd use something similar to:
class Test(BrowserPage):
template = ViewPageTemplateFile('atest.pt')
def __call__(self):
return self.template()
But I can't work out how to do this correctly in the context of the custom traversal.
UPDATE: To be clear I want to avoid adding anything else to the url (No: http://foo.com/folder/random_id/read).
I don't need the view to be available via any other address (No: http://foo.com/folder/read)
The ZCML for the view I'd like to use is:
<browser:page
for="foo.interfaces.IFooFolderContainer"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
I'm guessing (on further advice), something along the lines of:
return getMultiAdapter((mycalendar, self.request), IPageTemplate, name=u'read')
Or even a default view for the object type (a dict in this case) that's being returned:
<browser:page
for="dict"
name="read"
template="read.pt"
permission="zope.ManageContent"
/>
It would be easier to answer your question if you showed what your custom traverser is doing.
Essentially, you want something like this:
def publishTraverse(self, request, name):
if name in self.context:
return MyMessageView(self.context[name], request)
# fall back to views such as index.html
view = queryMultiAdapter((self.context, request), name=name)
if view is not None:
return view
# give up and return a 404 Not Found error page
raise NotFound(self.context, name, request)
where MyMessageView can be something as simple as
class MyMessageView(BrowserPage):
__call__ = ViewPageTemplateFile('read.pt')
Disclaimer: I'm not sure if the view you instantiate directly will be protected by a security wrapper; make sure your functional tests ensure anonymous users can't view messages if that's what you want.
If you end up at a proper object with your custom traverser, you can just tack on the template name and user "context" in that template. So http://foo.com/folder/random_id/my_template and in the template do the normal <h1 tal:content="context/title" /> stuff.
IIUC, what you want is to render the 'read' view when somebody requests /folder/random_id. If that's the case, all you need to do is make your traversal return an object (IFolderContent, maybe) representing a random_id and specify the 'view' page as the defaultView for IFolderContent.
The defaultView is needed because there's no view specified for the random_id object in your URL.

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

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

Categories