Using Wagtails Modeladmin:
Is there any way to disable edit & delete options leaving only the inspect view?
A possible approach that I can think of, is extending the template, removing the edit & delete buttons and then somehow disable the edit and delete view.
Is there any cleaner approach?
EDIT: Thanks to Loic answer I could figure out.
The PermissionHelper source code was also very helpful to figure out the correct method to override.
Complete answer for only showing inspect view
class ValidationPermissionHelper(PermissionHelper):
def user_can_list(self, user):
return True
def user_can_create(self, user):
return False
def user_can_edit_obj(self, user, obj):
return False
def user_can_delete_obj(self, user, obj):
return False
class ValidationAdmin(ModelAdmin):
model = Validation
permission_helper_class = ValidationPermissionHelper
inspect_view_enabled = True
[...]
Sadly, you need at least one of the add, change or delete permission on that model (set within the roles) for it to show up.
The way around that is to provide a custom permission helper class to your ModelAdmin and always allow listing (and still allow add/change/delete to be set within the roles):
class MyPermissionHelper(wagtail.contrib.modeladmin.helpers.PermissionHelper):
def user_can_list(self, user):
return True # Or any logic related to the user.
class MyModelAdmin(wagtail.contrib.modeladmin.options.ModelAdmin):
model = MyModel
permission_helper_class = MyPermissionHelper
modeladmin_register(wagtail.contrib.modeladmin.options.MyModelAdmin)
Related
I want to hide some models from admin index page and app page. For example, these that I have visible in inlines as related objects.
One more point, I want to keep the ability to work with change_view and add_view, but not list_view of that model.
Tried to find any hints in admin/templates/admin/*.html files, but haven't found anything helpful.
Is it possible without "hacks", monkey-patching and external libraries?
You can try this
this will hide the model from the index
class YourModel(admin.ModelAdmin):
def get_model_perms(self, request):
return {} # return empty
admin.site.register(YourModel, YourModelAdmin)
more about Django admin
https://docs.djangoproject.com/en/3.1/ref/contrib/admin/
As Django documentation tells:
ModelAdmin.has_module_permission(request)
Should return True if displaying the module on the admin index page and accessing the module’s index page is permitted, False otherwise. Uses User.has_module_perms() by default. Overriding it does not restrict access to the view, add, change, or delete views.
To avoid removal all permissions (like with get_model_perms()) you can redefine has_module_permission() method to always return False.
#admin.register(SomeModel)
class SomeModelAdmin(admin.ModelAdmin):
def has_module_permission(self, request):
return False
I have a DRF ViewSet to which I am adding the CanViewAndEditStaff permission. I want only certain users (user.access_level < 2) to be able to view the list of staff. In my Permissions class, how can I differentiate between a call to the list view and to the get item view. Here is my permissions class:
class CanViewAndEditStaff(permissions.BasePermission):
def has_permission(self, request, view):
# IF THIS IS A LIST VIEW, CHECK ACCESS LEVEL
if ( request.user.access_level < 3 ):
return True
# ELSE, CONTINUE ON TO OBJECT PERMISSIONS
def has_object_permission(self,request,view,account):
# admin can do anything
if ( request.user.access_level == 1 ):
return True
# view/edit/delete
else:
# users can view their own account
if account == request.user:
return True
elif account.access_level >= request.user.access_level:
return True
return False
class CanViewAndEditStaff(permissions.BasePermission):
def has_permission(self, request, view):
# IF THIS IS A LIST VIEW, CHECK ACCESS LEVEL
if (view.action == 'list' and request.user.access_level < 3 ):
return True
# ELSE, CONTINUE ON TO OBJECT PERMISSIONS
you can use view.action to know if this is list or something else.
This doesn't exactly address the question, but this technique is applicable.
I used a variation on Ykh's answer that allows the same permission class to be used broadly across many views which display a variety of different models.
In my view class I added an attribute to distinguish the originating view, thus allowing the appropriate object comparison to determine permissions
# views.py
class SomeView(ListAPIView):
permission_classes = (IsPermd, )
is_some_view = True
class SomeOtherView(RetrieveAPIView
permission_classes = (IsPermd, )
is_some_other_view = True
# permissions.py
class IsPermd(BasePermission):
def has_object_permissions(self, request, view, obj):
if hasattr(view, 'is_some_view'):
# whatever special considerations
if hasattr(view, 'is_some_other_view'):
# whatever other special considerations
This feels a little clunky, but until I find a better way I'll stick with it.
I am currently creating a lot of views and at each one of them only the User has permissions to access that view and create or edit the model.
Depending on the view I either put a small permission IF statement in the get_context_data or get_object.
While the following works I would like to make it more DRY since I have more than 20 views that need it:
def get_context_data(self, **kwargs):
context = super(SpaceCreate, self).get_context_data(**kwargs)
house = House.objects.get(pk=self.kwargs['pk'])
# CHECK IF USER HAS PERMISSION TO MAKE CHANGES TO THE MODEL
if house.owner != self.request.user:
raise PermissionDenied
or in the get_object() , and with more permission checks:
def get_object(self, *args, **kwargs):
obj = super(SpaceManage, self).get_object()
house = House.objects.get(pk=self.kwargs['pk'])
space = Space.objects.get(pk=self.kwargs['space_pk'])
if space.house.owner != self.request.user and house.pk != space.house.pk:
raise PermissionDenied
return space
While I have read a lot on creating permissions ( https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Authentication ), they seem to be applicable in cases where we want many users to have that permission instead of checking if the user has the permission to edit the models they have created.
Another possibility (in my opinion) is the use of mixins, but I don't seem to understand how they work and how to implement them.
What I'm trying to achieve is:
The user is only able to create, update and delete the models they have created, without re using the same code over and over in views.
EDIT 1: Based on #Lemayzeur's comments, creating a Mixin is the best approach for this. Any examples or explanations would be very helpful.
I am using flask-admin with ModelViews
class MyModel(ModelView):
can_create = False
can_edit = True
column_list = ['column']
This allows me to edit the data on each row. However I want to perform some custom function in addition to the editing. I tried to add a route for the edit but it overrides the existing functionality.
#app.route('/admin/mymodelview/edit/', methods=['POST'])
def do_something_in_addition():
...
Is there any way to extend the existing edit functionality?
Override either the after_model_change method or the on_model_change methods in your view class.
For example :
class MyModel(ModelView):
can_create = False
can_edit = True
column_list = ['column']
def after_model_change(self, form, model, is_created):
# model has already been commited here
# do custom work
pass
def on_model_change(self, form, model, is_created)
# model has not been commited yet so can be changed
# do custom work that can affect the model
pass
I currently have a bunch of inlines that all inherit from a base Inline Class set up like this:
class BaseInlineAdmin(admin.TabularInline):
extra = 0
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request):
return False
I now want to change my admin so that depending on the user, the inlines change like so:
class PartyAdmin(admin.ModelAdmin):
inline1 = [Inline1, Inline2]
inline2 = [Inline1, Inline2, Inline3]
def get_inline_instances(self, request, obj=None):
if request.user.is_staff:
return [inline(self.model, self.admin_site) for inline in self.inline1]
else:
return [inline(self.model, self.admin_site) for inline in self.inline2]
However, when I do this, the has_add_permission no longer works. When I don't use get_inline_instances, the user has no ability to add another item and I would like this functionality to stay consistent. Is there any reason this is not carrying over when I use this method?
For the record, has_delete_permission remains False which makes the situation even weirder.
get_inline_instances is the method that checks the permission. If you want to check for permissions, you either have to call super().get_inline_instances(), or you need to replicate the code from the original function.