Get user group in a template - django

I want to display a menu that changes according to the user group of the currently logged in user, with this logic being inside of my view, and then set a variable to check in the template to determine which menu items to show....I'd asked this question before, but my logic was being done in the template. So now I want it in my view...The menu looks as below
<ul class="sidemenu">
<li>General List </li>
<li>Sales List </li>
<li>Add a New Record </li>
<li>Edit Existing Record </li>
<li>Filter Records </li>
<li>Logout </li>
</ul>
Suppossing the user is management, they'll see everything...But assuming the user is in the group sales, they'll only see the first two and the last two items...and so on. I also want a dynamic redirect after login based on the user's group. Any ideas?

The standard Django way of checking permissions is by the individual permission flags rather than testing for the group name.
If you must check group names, being aware that Users to Groups is a many-to-many relationship, you can get the first group in the list of groups in your template with something like this:
{{ user.groups.all.0 }}
or using it like this in a conditional (untested but should work):
{% ifequal user.groups.all.0 'Sales' %}
...
{% endif %}
If you go with the preferred permission model you would do something like the following.
...
{% if perms.vehicle.can_add_vehicle %}
<li>Add a New Record </li>
{% endif %}
{% if perms.vehicle.can_change_vehicle %}
<li>Edit Existing Record </li>
{% endif %}
...
These are the permissions automatically created for you by syncdb assuming your app is called vehicle and the model is called Vehicle.
If the user is a superuser they automatically have all permissions.
If the user is in a Sales group they won't have those vehicle permissions (unless you've added those to the group of course).
If the user is in a Management group they can have those permissions, but you need to add them to the group in the Django admin site.
For your other question, redirect on login based on user group: Users to Groups is a many-to-many relationship so it's not really a good idea to use it like a one-to-many.

user.groups.all.0.name == "groupname"

Create a user_tags.py in your app/templatetags follow above:
# -*- coding:utf-8 -*-
from __future__ import unicode_literals
# Stdlib imports
# Core Django imports
from django import template
# Third-party app imports
# Realative imports of the 'app-name' package
register = template.Library()
#register.filter('has_group')
def has_group(user, group_name):
"""
Verifica se este usuário pertence a um grupo
"""
groups = user.groups.all().values_list('name', flat=True)
return True if group_name in groups else False
And finally in template use it:
{% if request.user|has_group:"Administradores"%}
<div> Admins can see everything </div>
{% endif %}

If you are working with Custom User Model (best practice with Django), you can create a method:
CustomUser(AbstractUser):
# Your user stuff
def is_manager(self):
return self.groups.filter(name='Management').exists()
Then inside your template you just call it this way:
{% if user.is_manager %}
{# Do your thing #}
{% endif %}
That method will be also useful validating permission in other parts of your code (views, etc.)

Related

How to create a clean url for user profile in django

I am building an application . One of the functionalities of this application is to display objects created by users to other users when they login .Lets call these objects created by users (x).
Each of these objects displays the name of the user who created it . Bellow is my code of my view to display all objects created by users.
def CreatedObjects(request):
objects=Model-Class-Name.objects.all()
# to display all users .
users=User.objects.all()
template_name="blabla.html"
context={"objects":objects,"users":users}
return render(request,template_name,context)
my html file
<div class="objects>
<!--Display all available objects created by users-->
{% for obj in objects %}
<!--Display the name of the user who created object, with a link that will take you to user profile of this user -->
<div class="user'>
{% for user in users %}
<a href="{% url 'app_namespace:url_name' user.pk %}">{{obj.user.username}}
</div>
{% endfor %}
{% endfor %}
<div>
The userprofile link works ,but since i am using a for loop to loop through available users in the database ,the user name is displayed repeatedly . So my problem is how to avoid this .How can i create a userprfile link without for looping or how can i avoid the user name not to be displayed continuously on a single object .

How get current user in a template tag?

How can i get the current user in a django template tags? (request object is not accessible)
Or how can i access to request object?
If you want to access the current user in a template tag, you must pass it as a parameter in the templates, like so:
{% my_template_tag user %}
Then make sure your template tag accepts this extra parameter. Check out the documentation on this topic. You should also check out simple tags.
The user is always attached to the request, in your templates you can do the following:
{% if user.is_authenticated %}
{% endif %}
You don't have to specify "request" to access its content
UPDATE:
Be aware: is_authenticated() always return True for logged user (User objects), but returns False for AnonymousUser (guest users). Read here: https://docs.djangoproject.com/en/1.7/ref/contrib/auth/
This question was already answered here:
{% if user.is_authenticated %}
<p> Welcome '{{ user.username }}'</p>
{% else %}
Login
{% endif %}
and make sure you have the request template context processor installed in your settings.py:
TEMPLATE_CONTEXT_PROCESSORS = (
...
'django.core.context_processors.request',
...
)
Note:
Use request.user.get_username() in views & user.get_username in
templates. Preferred over referring username attribute directly.
Source
This template context variable is available if a RequestContext is used.
django.contrib.auth.context_processors.auth is enabled by default & contains the variable user
You do NOT need to enable django.core.context_processors.request template context processor.
Source : https://docs.djangoproject.com/en/dev/topics/auth/default/#authentication-data-in-templates
Suppose you have a profile page of every registered user, and you only want to show the edit link to the owner of the profile page (i.e., if the current user is accessing his/her profile page, the user can see the edit button, but the user can't see the edit button on other user's profile page.
In your html file:
<h2>Profile of {{ object.username }}</h2>
{% if object.username == user.username %}
Edit
{% endif %}
Then your urls.py file should contain:
from django.urls import path
from .views import ProfileUpdateView
urlpatterns = [
...
path('<int:pk>/profile/update', ProfileUpdateView.as_view(), name = 'profile_update'),
...
]
considering you have appropriate ProfileUpdateView and appropriate model

How to reset user password from the admin interface

In my website, I want to let the admins reset the password of any user.
With reset I mean exactly what the password_reset view does (under contrib.auth): Send a confirmation link to that user email.
How would be the best way of doing that? Is there an already app/snippet that does that?
Edit:
Let's suppose user john is an admin. What I want is to let john reset any user's password through the admin interface. For example, to reset max password, he will just go to the max user, and click on any link to reset his password.
What I finally did was to add a custom ModelAdmin:
from django.contrib.auth.forms import PasswordResetForm
from django.contrib.auth.admin import UserAdmin
class CustomUserAdmin(UserAdmin):
...
def reset_password(self, request, user_id):
if not self.has_change_permission(request):
raise PermissionDenied
user = get_object_or_404(self.model, pk=user_id)
form = PasswordResetForm(data={'email': user.email})
form.is_valid()
form.save(email_template_name='my_template.html')
return HttpResponseRedirect('..')
def get_urls(self):
urls = super(UserAdmin, self).get_urls()
my_urls = patterns('',
(r'^(\d+)/reset-password/$',
self.admin_site.admin_view(self.reset_password)
),
)
return my_urls + urls
and I also had to override the change_form.html template, like this:
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block object-tools %}
{% if change %}{% if not is_popup %}
<ul class="object-tools">
{# You can also give a name to that pattern and refer to it below using 'url' #}
<li>Reset password</li>
<li>{% trans "History" %}</li>
{% if has_absolute_url %}
<li><a href="../../../r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">
{% trans "View on site" %}</a>
</li>
{% endif%}
</ul>
{% endif %}{% endif %}
{% endblock %}
The result looks like this:
If you want a more detailed explanation, I blogged about it.
The passreset app just exposes the django views via urls.py, and adjusts the login template to show a "Forgot my password" link.
The built-in django password reset views and templates are meant for self-reset. I guess the reset form could be prepopulated with a different user's email address (in the query string) but you'd still need to make adjustments such as changing the email template - "You're receiving this e-mail because you requested a password reset for your user account" is probably not what you want:
https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/templates/registration/password_reset_email.html
Therefore you should expose the views at different URLs if you want to include self-reset as well.
Hook the django views into urls.py like so:
urlpatterns += patterns('django.contrib.auth.views',
url(r'^accounts/password/reset/$',
'password_reset',
name='password-reset'),
url(r'^accounts/password/reset/done/$',
'password_reset_done',
name='password-reset-done'),
url(r'^accounts/password/reset/confirm/(?P<uidb36>[-\w]+)/(?P<token>[-\w]+)/$',
'password_reset_confirm',
name='password-reset-confirm'),
url(r'^accounts/password/reset/complete/$',
'views.password_reset_complete',
name='password-reset-complete')
)
and where you want to make adjustments, pass in e.g. your own email template:
url(r'^/accounts/password/reset/$',
'password_reset',
{'email_template_name': 'my_templates/password_reset_email.html'}
name='password-reset'),
The "password_reset" view has more parameters you can tweak:
https://docs.djangoproject.com/en/dev/topics/auth/#module-django.contrib.auth.views
("post_reset_redirect" comes to mind as another one for your purposes)
To show a corresponding link you'd either change the User admin (careful, already registered - unregister then register your own, subclassed plus additional link field) or the change_form template itself.
I'm unaware of an app that provides this out-of-the-box, so I upvoted the question :-).
Yep, there is an app for that. Check here:
https://github.com/bendavis78/django-passreset

Check permission inside a template in Django

Can I use the Auth application's permission checking inside a template in Django? (I want to display a simple form at the end of the template for privileged users)
And more importantly, should I do it at all or is this no the "Django way"?
If you are looking to check for permissions in templates, the following code would suffice:
{% if perms.app_label.can_do_something %}
<form here>
{% endif %}
Where model refers to the model that the user need permissions to see the form for.
Refer to https://docs.djangoproject.com/en/stable/topics/auth/default/#permissions for more examples.
The currently logged-in user's permissions are stored in the template variable {{ perms }}
(This requires the following context processor to be enabled: django.contrib.auth.context_processors.auth)
Tested on Django 2.0 +
If you want to see all the permissions the logged in user has, on your template (.html),
print :
{{ perms.app_name }}
Or
{{ perms }}
In order to check if user has permission , use:
{% if perms.app_name.change_model_name_lower_cased %}
E.g :
{% if perms.Utilization.change_invoice %}
Here: Utilization is my App name. Invoice is a model name.
Note that in general, there will be 4 kinds of permissions:
change [E.g Utilization.change_projectemail]
view [E.g Utilization.view_invoice]
delete [E.g Utilization.delete_invoicetype]
add [E.g Utilization.add_invoicetype]
Also , if you want to see all permissions a user has due to the groups he belongs to, launch Django shell...
user = User.objects.get(username='somename')
user.get_group_permissions()
Here, all permissions listed, are due to the groups he belongs to.
One more Unique way to do this is:
{% if 'app_label.permission' in perms %}
<form here>
{% endif %}
Example:
{% if 'auth.view_group' in perms %}
<p> Hello World! </p>
{% endif %}
This comes handy when you want to use your default/custom authentication permissions whether you've created an app for your model or not because this method don't need an app name. It just need the permission name from your permissions table.
You can put multiple checks also using and/or commands:
{% if 'auth.view_group' in perms and 'auth.add_group' in perms %}
<form here>
{% endif %}
If you need more granularity in checking perms (on a particular object for example), check out this extension: http://django-authority.readthedocs.org/en/latest/check_templates/
And for those using Jinja templates and struggling with this question, like I did for a few hours/days...
Use an extended Jinja Environment, and add the request object to it:
# in settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.jinja2.Jinja2",
'DIRS': ['jinja2'],
"APP_DIRS": True,
"OPTIONS": {
'environment': 'main.jinjaconfig.env.environment',
}
},
# Other template backends...
]
# main/jinjaconfig/env.py
from django.template.context_processors import request
from jinja2 import Environment
# ...
def environment(**options):
env = Environment(**options)
# Update globals with the functions and objects you need, here 'request'
env.globals.update({
'request': request,
# Other globals like 'static', 'url', ...
})
return env
Then you can access the request object in Jinja templates, and also request.user and request.user.has_perm() and all user related functions:
{% if request.user.has_perm('app_label.can_do_something') %}
{# Stuff .. #}
{% endif %}

Tying in to Django Admin's Model History

The Setup:
I'm working on a Django application which allows users to create an object in the database and then go back and edit it as much as they desire.
Django's admin site keeps a history of the changes made to objects through the admin site.
The Question:
How do I hook my application in to the admin site's change history so that I can see the history of changes users make to their "content"?
The admin history is just an app like any other Django app, with the exception being special placement on the admin site.
The model is in django.contrib.admin.models.LogEntry.
When a user makes a change, add to the log like this (stolen shamelessly from contrib/admin/options.py:
from django.utils.encoding import force_unicode
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.models import LogEntry, ADDITION
LogEntry.objects.log_action(
user_id = request.user.pk,
content_type_id = ContentType.objects.get_for_model(object).pk,
object_id = object.pk,
object_repr = force_unicode(object),
action_flag = ADDITION
)
where object is the object that was changed of course.
Now I see Daniel's answer and agree with him, it is pretty limited.
In my opinion a stronger approach is to use the code from Marty Alchin in his book Pro Django (see Keeping Historical Records starting at page 263). There is an application django-simple-history which implements and extends this approach (docs here).
The admin's change history log is defined in django.contrib.admin.models, and there's a history_view method in the standard ModelAdmin class.
They're not particularly clever though, and fairly tightly coupled to the admin, so you may be best just using these for ideas and creating your own version for your app.
I know this question is old, but as of today (Django 1.9), Django's history items are more robust than they were at the date of this question. In a current project, I needed to get the recent history items and put them into a dropdown from the navbar. This is how I did it and was very straight forward:
*views.py*
from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION
def main(request, template):
logs = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20]
logCount = LogEntry.objects.exclude(change_message="No fields changed.").order_by('-action_time')[:20].count()
return render(request, template, {"logs":logs, "logCount":logCount})
As seen in the above code snippet, I'm creating a basic queryset from the LogEntry model (django.contrib.admin.models.py is where it's located in django 1.9) and excluding the items where no changes are involved, ordering it by the action time and only showing the past 20 logs. I'm also getting another item with just the count. If you look at the LogEntry model, you can see the field names that Django has used in order to pull back the pieces of data that you need. For my specific case, here is what I used in my template:
Link to Image Of Final Product
*template.html*
<ul class="dropdown-menu">
<li class="external">
<h3><span class="bold">{{ logCount }}</span> Notification(s) </h3>
View All
</li>
{% if logs %}
<ul class="dropdown-menu-list scroller actionlist" data-handle-color="#637283" style="height: 250px;">
{% for log in logs %}
<li>
<a href="javascript:;">
<span class="time">{{ log.action_time|date:"m/d/Y - g:ia" }} </span>
<span class="details">
{% if log.action_flag == 1 %}
<span class="label label-sm label-icon label-success">
<i class="fa fa-plus"></i>
</span>
{% elif log.action_flag == 2 %}
<span class="label label-sm label-icon label-info">
<i class="fa fa-edit"></i>
</span>
{% elif log.action_flag == 3 %}
<span class="label label-sm label-icon label-danger">
<i class="fa fa-minus"></i>
</span>
{% endif %}
{{ log.content_type|capfirst }}: {{ log }}
</span>
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>{% trans "This object doesn't have a change history. It probably wasn't added via this admin site." %}</p>
{% endif %}
</li>
</ul>
To add to what's already been said, here are some other resources for you:
(1) I've been working with an app called django-reversion which 'hooks into' the admin history and actually adds to it. If you wanted some sample code that would be a good place to look.
(2) If you decided to roll your own history functionality django provides signals that you could subscribe to to have your app handle, for instance, post_save for each history object. Your code would run each time a history log entry was saved. Doc: Django signals
Example Code
Hello,
I recently hacked in some logging to an "update" view for our server inventory database. I figured I would share my "example" code. The function which follows takes one of our "Server" objects, a list of things which have been changed, and an action_flag of either ADDITION or CHANGE. It simplifies things a wee bit where ADDITION means "added a new server." A more flexible approach would allow for adding an attribute to a server. Of course, it was sufficiently challenging to audit our existing functions to determine if a changes had actually taken place, so I am happy enough to log new attributes as a "change".
from django.contrib.admin.models import LogEntry, User, ADDITION, CHANGE
from django.contrib.contenttypes.models import ContentType
def update_server_admin_log(server, updated_list, action_flag):
"""Log changes to Admin log."""
if updated_list or action_flag == ADDITION:
if action_flag == ADDITION:
change_message = "Added server %s with hostname %s." % (server.serial, server.name)
# http://dannyman.toldme.com/2010/06/30/python-list-comma-comma-and/
elif len(updated_list) > 1:
change_message = "Changed " + ", ".join(map(str, updated_list[:-1])) + " and " + updated_list[-1] + "."
else:
change_message = "Changed " + updated_list[0] + "."
# http://stackoverflow.com/questions/987669/tying-in-to-django-admins-model-history
try:
LogEntry.objects.log_action(
# The "update" user added just for this purpose -- you probably want request.user.id
user_id = User.objects.get(username='update').id,
content_type_id = ContentType.objects.get_for_model(server).id,
object_id = server.id,
# HW serial number of our local "Server" object -- definitely change when adapting ;)
object_repr = server.serial,
change_message = change_message,
action_flag = action_flag,
)
except:
print "Failed to log action."
Example code:
from django.contrib.contenttypes.models import ContentType
from django.contrib.admin.models import LogEntry, ADDITION
LogEntry.objects.log_action(
user_id=request.user.pk,
content_type_id=ContentType.objects.get_for_model(object).pk,
object_id=object.pk,
object_repr=str(object),
action_flag=ADDITION,
)
Object is the object you want to register in the admin site log.
You can try with str() class in the parameter object_repr.