Including user statistics for multiple views - python

I have a navigation bar that displays "login" and "register" when the user is not signed in. When the user is signed in, the navigation bar will display the user's name and the number of messages he has in his inbox.
The problem is that the navigation page is present on around 50 pages, so there are around 50 view functions that have to get the user information and send it to the template. If I want to change this later, it will be a pain!
For example, here is an example view:
def index(request):
user = request.user
...
return render_to_response("page.html", {'user': user})
I have to send the info about the user each time to any page with the navigation bar because my navigation bar contains the code:
{% if user %}
...
{% else %}
....
{% endif %}
Is there a cleaner way to do this?
Edit: Also, I have a UserProfile model which I want to send through to the template. Is there a way to do this, too?

The simplest way is to include django.contrib.auth.context_processors.auth to the TEMPLATE_CONTEXT_PROCESSORS configuration in your settings.py. As described in the docs it wil add a user and perms variable in your template context which gives you direct access to the current user.
Not that the default configuration for TEMPLATE_CONTEXT_PROCESSORS is this (in Django 1.3):
("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.contrib.messages.context_processors.messages")
So the context processor should already be active und you should be able to access the user variable in you templates without returning it in the view.
In your views, you can simply use the render shortcut which will take care of creating the RequestContext instance that is needed:
from django.shortcuts import render
def my_view(request):
return render(request, 'template.html' )

You could create a custom template tag, as DrTyrsa suggested, or you could create context processor.

You need a custom template tag for that.

Why you need to send user to every view, HttpRequest contains the user, you can easily access it in template when you are logged in.
and another solution is, save the user in sessions, and access it in any view or template.

Related

How can i get form data from django layouts (like 'base.html') without sending forms in all page through views?

many templates extend from base.html. base.html have one newsletter form that get email from user. Is there any easy way to get the form data from 'base.html' to view.
(Sending forms in all page through views is possible But I think there is A easy good Looking idea)
Use a context processor to do so:
Add a new .py file in your root project, name it context_processors.py.
context_processors.py
from app.forms import GlobalForm
def global_variables(request):
form = GlobalForm()
context = {'global_form':form}
return context
Then in settings.TEMPLATES add the context processors as the last line.
# stuff
'django.contrib.messages.context_processors.messages'
'project_name.context_processors.global_variables'
So, {{global_form}} is available in all templates

How to prevent users from seeing data that does not belong to them in Django DetailView?

I have a web app where a user signs in and begins entering items for a ToDoList. The base.html is wrapped in an is_authenticated check so users cannot see anything in the app until they have logged in. I was doing some testing with:
Logging in as User2
Adding a new ToDoListItem
Redirecting to the DetailView
In this case, the URL = http://localhost:8000/to_do_list/to_do_item/72
At this point I realized that the DetailView would allow User2 to see the details for any ToDoListItem for User1 just by entering in an existing pk into: http://localhost:8000/to_do_list/to_do_item/<int:pk>.
urls.py includes
path('to_do_item/<int:pk>', views.ToDoListItemDetail.as_view(), name='todo-item-detail'),
views.py
class ToDoListItemDetail(DetailView):
model = ToDoListItem
todolistitem_detail.html
{% extends 'base.html' %}
{% block content %}
Home
<h1>DetailView for 'ToDoListItem' model</h1>
<p>TaskTitle: '{{ object.title }}'</p>
<p>Complete: '{{ object.is_complete }}'</p>
<p>User: '{{ object.user}}'</p>
{% endblock %}
What is the recommended way to prevent this from happening? I am considering the following:
I could completely remove the DetailView and direct to a different URL that only returns the user's data (using something like ToDoListItem.objects.filter(user=request.user))
I could check that the name of the user logged in matches the name of the user that owns the ToDoListItem.
I could override get_context_data() for the DetailView and check user ownership there (similar to no. 1, but within the DetailView)
??? (Something else better than the above I do not know about yet)
Is there a way to limit a user to only see their own data throughout an application without implementing this logic every time it is needed?
You can filter in the DetailView as well, by overriding the get_queryset method [Django-doc]:
from django.contrib.auth.mixins import LoginRequiredMixin
class ToDoListItemDetail(LoginRequiredMixin, DetailView):
model = ToDoListItem
def get_queryset(self, *args, **kwargs):
return super(ToDoListItemDetail, self).get_queryset(
*args, **kwargs
).filter(user=self.request.user)
Django will, behind the curtains, always call get_queryset(..). By default this function returns the queryset of the model you specified with all objects. But you thus can filter it further down.
Django's get_object method [Django-doc] will then further filter it down with the id and/or slug, but if you already filter out the elements that do not belong to the self.request.user, then this can thus only result in an query returning no results.
It also makes sense to here add the LoginRequiredMixin [Django-doc] to your class, since in case the user did not log in, you probably want to redirect hem/her to the login screen.
There is a permission named: ¨can read¨ that allow you to handle the access. For example:
class FruitEdit(PermissionRequiredMixin,DetailView):
permission_required = 'app.read_fruit'
...
I hope you can solve it

How to have specific users see different webpages in Django?

I was wondering how one would handle a website that, once a user logged in, would be able to show a completely different page to each user?
Think of a master login/registration page.
Once a user registers, depending on their permissions, they would be redirected to the appropriate page for their user type (whilst not being able to access those of any other type).
So would this be an individual user/group permissions thing (using Django auth to do so)? Or would this be able to be implemented using models (i.e. have models of each type of user account and just hold instances in the database)?
This is my first week or so with Django (and web dev), so I am not at all familiar with this stuff.
Redirecting users based on permissions
On the After a user logs in, let's say they land on a URL set by settings.LOGIN_REDIRECT_URL that can you can change to e.g./enter/ URL , and you can map that /enter/ URL to be backed by below view called enter_view()
from django.shortcuts import redirect
def enter_view(request):
if request.user.has_perm('app_label.permission_codename'):
return redirect('/page-A')
elif request.user.has_perm('app_label.another_permission'):
return redirect('/page-B')
else:
return redirect('/page-C')
To change your LOGIN_REDIRECT_URL simply put in settings.py
LOGIN_REDIRECT_URL = '/enter/' # or any URL you want
References
https://docs.djangoproject.com/en/1.8/ref/settings/#login-redirect-url
https://docs.djangoproject.com/en/1.8/ref/contrib/auth/#django.contrib.auth.models.User.has_perm
https://docs.djangoproject.com/en/1.8/topics/http/shortcuts/#redirect
Checking permissions in your template
Further more if you need to customize small parts of a template, you can also check user permissions in similar manner using the {{ perms }} object
{% if perms.foo.can_vote %}
<p>You can vote!</p>
{% endif %}
https://docs.djangoproject.com/en/1.8/topics/auth/default/#permissions

How to add button next to Add User button in Django Admin Site

I am working on Django Project where I need to extract the list of user to excel from the Django Admin's Users Screen. I added actions variable to my Sample Class for getting the CheckBox before each user's id.
class SampleClass(admin.ModelAdmin):
actions =[make_published]
Action make_published is already defined. Now I want to append another button next to Add user button as shown in fig. . But I dont know how can I achieve this this with out using new template. I want to use that button for printing selected user data to excel. Thanks, please guide me.
Create a template in you template folder: admin/YOUR_APP/YOUR_MODEL/change_list.html
Put this into that template
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
{{ block.super }}
<li>
Export
</li>
{% endblock %}
Create a view function in YOUR_APP/admin.py and secure it with annotation
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def export(self, request):
... do your stuff ...
return HttpResponseRedirect(request.META["HTTP_REFERER"])
Add new url into YOUR_APP/admin.py to url config for admin model
from django.conf.urls import patterns, include, url
class YOUR_MODELAdmin(admin.ModelAdmin):
... list def stuff ...
def get_urls(self):
urls = super(MenuOrderAdmin, self).get_urls()
my_urls = patterns("",
url(r"^export/$", export)
)
return my_urls + urls
Enjoy ;)
The easy and accepted way is to override the template.
If you don't want to mess with the Django templates, you could add a Media class to your admin and add some javascript to create the button although I think creating elements with javascript is a bit nasty and should be avoided.
Though other answers are entirely valid, I think it is important to note that it is absolutely not necessary to add a button to get such behavior. You can use admin actions, as you did for the make_published action.
This as the advantage of not requiring to override any template, and thus prevent from potential troubles when upgrading django version (as admin templates may change, and changes might not be "compatible" with the way you overrode it).
import csv
from django.http import HttpResponse
from django.utils import timezone
def export_as_csv(modeladmin, request, queryset):
opts = modeladmin.model._meta
filename = format(timezone.now(), "{app}_{model}-%Y%m%d_%H%M.csv").format(
app=opts.app_label, model=opts.model_name)
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
writer = csv.writer(response)
field_names = [f.get_attname() for f in opts.concrete_fields]
writer.writerow(field_names)
for obj in queryset.only(*field_names):
writer.writerow([str(getattr(obj, f)) for f in field_names])
return response
Admin actions are made for this, adding a custom button is one step closer to "over-customization", which means it's probably time to write your own views.
The admin has many hooks for customization, but beware of trying to use those hooks exclusively. If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.
Quote from the introduction paragraph of Django Admin's documentation

Add to the django request.user variable

In django, I use the authenticate() function to log users in. Using request.user, I can access the logged in user's information which is pulled from the users table. If I do request.user.id, I can get the user's id. In any template, on any request, I can do user.username to get the username.
Is there a way by which I can add values to the request.user variable so that I can use them throughout my application and in templates?
For example, I would like to have this: request.user.timezone set when the user logs in to the site; I should be able to access this variable in templates and in my views.
You can write a simple middleware:
class UserTZMiddleware(object):
def process_view(self, request, view_func, view_args, view_kwargs):
if request.user.is_authenticated():
request.user.timezone = evaluate_tz()
and then append this into MIDDLEWARE_CLASSES in your settings.py file:
MIDDLEWARE_CLASSES = (
....,
'myapp.UserTZMiddleware',
)
Would you like to let the user change it time-zone and force a specific time-zone ? I assume you would, if so you would like to keep the timezone configurable in your database. Therfore I would suggest you to make a UserProfile model which has one to one relations with the User Object.
You can do :
class UserProfile(models.Model):
user = models.OneToOneField(User)
time_zone = models.whateverField()
... add anything else you would like to extend your profile ...
On your templete you will be able to get the timezone by doing that :
{{ request.user.userprofile.time_zone }}
Simple and very clean.
If you would like to have an automatic creation of your userprofile whenever you save a User object, you can use signals for that.
Add the request context processor to your TEMPLATE_CONTEXT_PROCESSORS. This processor adds the request to the context, in your template you can do {{request}} or {{ request.user }}
see: https://docs.djangoproject.com/en/dev/ref/templates/api/#django-core-context-processors-request
Use sessions, which are designed to keep variables in the context.
You should not change request.user as this is something that other applications will rely on and one that is populated by django's own auth machinery.
If you need to keep track of the timezone specifically, you should read up on the timezone support in django 1.4. In the documentation it provides examples for setting and managing timezones for users.

Categories

Resources