Is there any possibility to change permissions list in user edit page? I don't wan't to show all of permissions for example admin log entry or auth group etc.
How can I modify a main queryset to exclude some of it?
I got the idea from this topic, which also answer your question, but it's not that clear.
You have to overwrite the queryset of user permissions in the UserAdmin form used for visualization.
To do this, the easiest way is to create a subclass of UserAdmin and overwrite the get_form method:
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
class MyUserAdmin(UserAdmin):
def get_form(self, request, obj=None, **kwargs):
# Get form from original UserAdmin.
form = super(MyUserAdmin, self).get_form(request, obj, **kwargs)
if 'user_permissions' in form.base_fields:
permissions = form.base_fields['user_permissions']
permissions.queryset = permissions.queryset.filter(content_type__name='log entry')
return form
You can change the filter of your queryset for whatever you want:
Examples:
# Exclude admin and auth.
permissions.queryset = permissions.queryset.exclude(content_type__app_label__in=['admin', 'auth'])
# Only view permissions of desired models (Can be your models or Django's)
permissions.queryset = permissions.queryset.filter(content_type__model__in=['blog', 'post', 'user', 'group'])
After you create your class, you have to register your User model with your newly created Admin:
admin.site.unregister(User) # You must unregister first
admin.site.register(User, MyUserAdmin)
Edit:
I added comment from Maik Hoepfel, because this code made django crashed when creating new user.
You can do the same with the permission list in your Group edit page, but you have to create another Admin that extends from GroupAdmin, and change form.base_fields['user_permissions'] with form.base_fields['permissions']
Renato's answer is almost perfect. The Django Admin makes adding a user a two-step process with the same form, and his code fails with a KeyError for 'user_permissions' in the first step.
The fix is easy enough, just use the code below instead:
def get_form(self, request, obj=None, **kwargs):
form = super(MyUserAdmin, self).get_form(request, obj, **kwargs)
# adding a User via the Admin doesn't include the permissions at first
if 'user_permissions' in form.base_fields:
permissions = form.base_fields['user_permissions']
permissions.queryset = permissions.queryset.filter(content_type__name='log entry')
return form
Related
I have this url pattern:
path("user/<int:pk>", MyAccountView.as_view(), name='my_account'),
And this view:
class MyAccountView(DetailView):
model = CustomUser
When the user is logged Django redirect to that URL.
The problem is that any user can access other users.
For example, if the logged user has pk 25, he can access the view of user with pk 26 by writing in the browser url box:
localhost:8000/user/26
I want that each user can access to his user page only, so if user with pk 25 try to access the url with pk 26, the access should be denied.
Can you point me in some direction of how this is done? The Django documentation is very confusing in this respect.
Thanks.
You need to override the get method of DetailView
from django.core.exceptions import PermissionDenied
from django.contrib.auth.mixins import LoginRequiredMixin
class MyAccountView(LoginRequiredMixin, DetailView):
model = CustomUser
def get(self, request, pk):
if request.user.pk != pk:
raise PermissionDenied()
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
Easy !
First change the view path from user/<int:pk>/ to user/
Link the view to the current user, DetailView won't work because it heavily relies on either pk or slug and we won't be using none of them, so you'll have to write a new view. (Example using FBV because i do not use CBV)
# views.py
from django.contrib.auth.decorators import login_required
# redirects to login page if the user is not authenticated
#login_required(login_url='/example url you want redirect/')
def get_user_profile(request):
context = dict(user=request.user)
return render(request, "template.html", context)
And that's it, any user visiting /user/ will only see their account/profile.
I have a Django app with multiple levels of admin role and users defined by the django-role-permissions package. I want to apply permissions to users only through groups, defining the permissions for the group roles and assigning the user to the correct group.
I want to "hide" the higher level admin roles in Groups when lower level admins create users. A lower level admin should not be able assign a created user to a higher level admin role in Groups.
I can do this with Users (lower level admins cannot view / change higher level admins) by overriding get_queryset with code shown below. I want to be able to do something equivalent with the roles in Groups. I would prefer to do this in my admin.py rather than in templates. It would have been nice if the Groups members were also gathered by get_queryset, but that does not seem to be the case. I have searched around without finding anything definitive. I set up django-debug-toolbar to see the context passed into the forms, but I don't see the Groups elements passed in that way to change_form. It's tempting to try overriding FilteredSelectMultiple inside of django.contrib.admin.widgets, but I don't yet see exactly where the Groups elements are actually gathered, so that feels like too much guesswork to try that yet.
What would be the best way for me to proceed?
from django.contrib.auth.models import User
from django.contrib import admin
from django.utils.translation import ugettext, ugettext_lazy as _
from rolepermissions.checkers import has_role
from myapp.roles import SuperAdmin, Admin # can't import TrustedUser and User because already have User above LOL
import logging
logger = logging.getLogger(__name__)
class MyUserAdmin(UserAdmin):
def get_queryset(self, request):
qs = super(MyUserAdmin, self).get_queryset(request)
logger.debug(f'get_queryset logged in user is {request.user}, queryset is {qs}')
if request.user.is_superuser: # see all users
return qs
elif has_role(request.user, SuperAdmin): # see all but superusers
return qs.filter(is_superuser=False)
elif has_role(request.user, Admin): # see all but superusers & SuperAdmins
excludes = [x.username for x in qs if has_role(x, SuperAdmin)] # has_role will be true for superuser
return qs.exclude(username__in=excludes)
else:
logger.debug(f'get_queryset should never get here {request.user}, acting on qs {qs}')
return q.none()
#Unregister current Django Admin
admin.site.unregister(User)
#Register current Django Admin
admin.site.register(User, MyUserAdmin)
By looking at the admin feature with the web developer tools, the objects I care about a the RelatedFieldWidgetWrapper which contains the FilteredSelectMultiple class. Going through all the inheritance, eventually I got to forms/widget, and within forms/widget there is a method optgroups that appears to provide the group lookup that I am looking for.
So I do want to override FilteredSelectMultiple. How can I do that or how can I override django.contrib.admin.widgets?
I ended up doing something that looked a lot like this:
from django.contrib.auth.admin import UserAdmin, GroupAdmin
from django.contrib.auth.models import User, Group
from django.contrib import admin
from django.utils.translation import ugettext, ugettext_lazy as _
from rolepermissions.checkers import has_role
from myproj.roles import SuperAdmin, Admin
class MyUserAdmin(UserAdmin):
def get_form(self, request, obj=None, **kwargs): # filters groups in the change_form, prevents creation of higher privledge user when adding user
# Get form from original UserAdmin.
form = super(MyUserAdmin, self).get_form(request, obj, **kwargs)
if 'groups' in form.base_fields and has_role(request.user, Admin) and not request.user.is_superuser:
groups_filtered = form.base_fields['groups']
excludes = ["super_admin", "admin"] # Admin role cannot modify krakbot_admin or admin
groups_filtered.queryset = groups_filtered.queryset.exclude(name__in=excludes) # updates groups_filtered queryset
return form
with an unregister of the original User model, and registering the new User with the MyUserAdmin class. There was additional functionality in the MyUserAdmin class, which I have left off.
The key information was someone else on SO mentioning that the base_fields had the group data that I was looking for, and also that the data I wanted to filter was present the second time the from was presented, so I needed a conditional ('groups' in form.base_fields) to detect when we were going through the form for the second time.
In "wagtail_hooks.py" I have the code below. As wagtail admin I can see the StudentModelAdmin, but as a user with restricted access to the admin interface I can't.
I would like to allow users with wagtail admin access and the specific permission below to access the student model admin. How do I go about creating the "CourseRegisterPermission" class?
from wagtail.contrib.modeladmin.options import (ModelAdmin, modeladmin_register)
from wagtail.wagtailcore import hooks
from .models import Participant
#hooks.register('register_permissions')
def view_course_registrations():
return Permission.objects.filter(codename="view_course_registrations")
class CourseRegisterPermission(PermissionHelper):
# how do I allow users with the permission to view course registrations
# to see the 'StudentModelAdmin" below?
class StudentModelAdmin(ModelAdmin):
model = Participant
menu_label = "Student Registrations"
menu_icon = "group"
search_fields = ('name', 'supervisor_name')
list_display = ('name', 'email')
list_filter = ('course',)
permission_helper_class = CourseRegisterPermission
I tried to find some examples of wagtail PermissionHelper but wasn't able to find any.
Any hint would be appreciated!
You can use the wagtail.contrib.modeladmin.helpers.PermissionHelper or wagtail.contrib.modeladmin.helpers.PagePermissionHelper permission helper classes from Wagtail's sources as an example. See methods like user_can_list, user_can_create, etc.
But... Are you sure that you need to define your own permission helper class? It seems to me that you can just create a new (or edit existing) group in the Wagtail admin and give required object permissions to your Participant model.
On my screenshot Programme is the model that I manage using ModelAdmin.
You can override some functions inside CourseRegisterPermission
class CourseRegisterPermission(PermissionHelper):
def user_can_list(self, user):
"""
Return a boolean to indicate whether `user` is permitted to access the
list view for self.model
"""
# this is just an example
return user.role == "driver"
def user_can_delete_obj(self, user, obj):
"""
Return a boolean to indicate whether `user` is permitted to 'delete'
a specific `self.model` instance.
"""
perm_codename = self.get_perm_codename('delete')
if obj.status > 0:
return False
if not self.user_has_specific_permission(user, perm_codename):
return False
if user.id == obj.id:
# users may not delete themselves
return False
You can also override the following functions:
def user_can_list(self, user):
def user_can_create(self, user):
def user_can_inspect_obj(self, user, obj):
def user_can_edit_obj(self, user, obj):
def user_can_delete_obj(self, user, obj):
def user_can_unpublish_obj(self, user, obj):
def user_can_copy_obj(self, user, obj):
I have own Auth User model, which inherits from PermissionsMixin. While I'm visiting django-admin page for any instance of that model, I get a lot of db queries (as I have a lot of permissions). The problem lies here, django/contrib/auth/models.py:
class Permission(models.Model):
[...]
def __str__(self):
return "%s | %s | %s" % (
six.text_type(self.content_type.app_label),
six.text_type(self.content_type),
six.text_type(self.name))
Every time a permission is displayed on admin page, it makes a query for its content_type.
Question is: can I ensure that for every query involving my Auth User model, especially for that which doesn't come from my code (like django admin) will be run prefetch_related for permissions and their content_types?
If you need this only for django-admin, then you can create your own admin page using techniques described in official documentation.
For instance:
from django.contrib import admin
from django.contrib.auth.models import Permission
from foo.models import User
class UserAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request=None, **kwargs):
if db_field.name == 'user_permissions':
kwargs['queryset'] = Permission.objects.all().select_related('content_type')
return super(UserAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)
admin.site.register(User, UserAdmin)
I'm new to django.
I'm creating simple app in which I have users enter some data and view it later. I need to make django admin show to the user only the data she enter and non of the other users data.
Is it possible to change it to multiple admin pages?
Thank you
Store a reference to a user in your model.
models.py:
from django.db import models
from django.contrib.auth.models import User
class MyModel(models.Model):
user = models.ForeignKey(User)
... (your fields) ...
Force the current user to be stored in that field (when using admin)
Force any list of these objects to be (additionally) filtered by the current user (when using admin)
Prevent other users from editing (even though they can't see the object in the list they could access its change_form directly)
admin.py:
from django.contrib import admin
from models import MyModel
class FilterUserAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
def get_queryset(self, request):
# For Django < 1.6, override queryset instead of get_queryset
qs = super(FilterUserAdmin, self).get_queryset(request)
return qs.filter(created_by=request.user)
def has_change_permission(self, request, obj=None):
if not obj:
# the changelist itself
return True
return obj.user === request.user
class MyModelAdmin(FilterUserAdmin):
pass # (replace this with anything else you need)
admin.site.register(MyModel, MyModelAdmin)
If you have MyOtherModel with a foreign key "user" just subclass MyOtherModelAdmin from FilterUserAdmin in the same manner.
If you want certain superusers to be able to see anything, adjust queryset() and has_change_permission() accordingly with your own requirements (e.g. don't filter/forbid editing if request.user.username=='me').
In that case you should also adjust save_model() so that your editing doesn't set the user and thus "take away" the object from the previous user (e.g. only set user if self.user is None (a new instance)).
You'll have to save in the user to every item and query each item with that user as search criteria. You'll probably build a base model which all your other models will inherit from. To get you started take a look at row-level permissions in the admin.