Exclude a field in django admin for users not super admin - django

how i'll exclude a field in django admin if the users are not super admin?
thanks

I did it in this way:
admin.py
def add_view(self, request, form_url='', extra_context=None):
if not request.user.is_superuser:
self.exclude=('activa', )
return super(NoticiaAdmin, self).add_view(request, form_url='', extra_context=None)

Overriding the exclude property is a little dangerous unless you remember to set it back for other permissions, a better way might be to override the get_form method.
see: Django admin: exclude field on change form only

In the future, it looks like there will be a get_fields hook. But it's only in the master branch, not 1.5 or 1.6.
def get_fields(self, request, obj=None):
"""
Hook for specifying fields.
"""
return self.fields
https://github.com/django/django/blob/master/django/contrib/admin/options.py

If you have a lot of views, you can use this decorator :
def exclude(fields=(), permission=None):
"""
Exclude fields in django admin with decorator
"""
def _dec(func):
def view(self, request, *args, **kwargs):
if not request.user.has_perm(permission):
self.exclude=fields
return func(self, request, *args, **kwargs)
return view
return _dec
usage:
#exclude(fields=('fonction', 'fonction_ar'))

Related

Passing kwargs from CBV to Form in Django

I have a ModelForm which needs a user passed in so that the queryset can be updated. I am overriding the __init__ method of the ModelForm as such:
def __init__(self, *args, **kwargs):
# override init to get user's casino's EmployeeType queryset
self.user = kwargs.pop('user')
print(self.user)
super(MemoForm, self).__init__(*args, **kwargs)
self.fields['receiver'].queryset = EmployeeType.objects.filter(
casino=self.user.casino
)
In the View I have a get and a post method. I am trying to pass the **kwargs in as such:
class VideoUploadView(LoginRequiredMixin, View):
"""
Display a form for uploading videos.
"""
form_class = VideoUploadForm
success_url = '/videos'
template_name = 'videos/video_upload.html'
def get(self, request, *args, **kwargs):
form = self.form_class()
return render(
request,
self.template_name,
{'form': form, 'user': self.request.user}
)
In a CreateView you are able to use the get_form_kwargs method to pass in the **kwargs. How is it done in a normal View? Should we use the __init__ method? The way shown above does not seem to work as both *args and **kwargs seem to be empty.
These are the built-in methods of View.
I don't really understand why you're not using a FormView here as well, so that you can still override get_form_kwargs; you really shouldn't ever need to define get (or post) directly.
But nevertheless, the answer is simple: you just pass your kwargs directly to the form:
form = self.form_class(user=request.user)

Django Admin: Show only some objects in ManyToMany field?

I have relatively simple data model with User, Group and Task. Each group has its own tasks and users. Users can be only assigned to one group.
Tasks belong to groups and each task has manyToMany field for users, so multiple users can have the same task assigned.
In my admin when assigning users to task it shows all created users, I want it to only show users from the same group as the task.
What would be the best approach?
I have checked available customization options for admin.ModelAdmin but haven't found anything related to my problem.
you can use formfield_for_manytomany
the formfield_for_manytomany method can be overridden to change the default formfield for a many to many field
in your case change your admin.py to :
class TaskAdmin(admin.ModelAdmin):
def get_object(self, request, object_id, s):
# Hook obj for use in formfield_for_manytomany
self.obj = super(TaskAdmin, self).get_object(request, object_id)
# print ("Got object:", self.obj)
return self.obj
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "user":
kwargs["queryset"] = User.objects.filter(task=self.obj)
return super().formfield_for_manytomany(db_field, request, **kwargs)
admin.site.register(Task, TaskAdmin)
You can customize the model admin using a method: formfield_for_manytomany
class TaskAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name == "users":
# Filter your users below as per your condition
kwargs["queryset"] = Users.objects.filter()
return super(TaskAdmin, self).formfield_for_manytomany(db_field, request, **kwargs)

how to protect user from editing others blog posts in django?

I am making a blog with django. The people who are the owner of the blog post can only edit the blog post. how to protect the route? should I make a custom middleware or there is an easy way
Create mixin. It will be something like:
class IsOwnerMixin(object):
permission_denied_message = _("You are not the owner of this blog - you cannot edit it")
def dispatch (self, request, *args, **kwargs):
if self.get_object().owner != request.user:
raise PermissionDenied(self.get_permission_denied_message())
return super().dispatch(request, *args, **kwargs)
def get_permission_denied_message(self):
"""
Override this method to override the permission_denied_message attribute.
"""
return self.permission_denied_message
See docs for more info: https://docs.djangoproject.com/en/2.2/topics/class-based-views/mixins/
You can create a mixin for this purpose. If you are using functional views then function decorator can do the trick while you can use mixins for class-based views.
For understanding, here is an example:
mixin
class IsPermittedMixin(object):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
//check the permission of the user
return super(IsPermittedMixin, self).dispatch(request, *args, **kwargs)
raise PermissionDenied()
views
In your views you can use this mixin as:
class EditBlog(IsPermittedMixin, View):
//do something

In a django view, how do I redirect a user to the login view if that don't belong to the object set?

I feel like this is really simple and I'm just missing it.
I have a very simple generic class based view detail.
When I do teh get_object I want to make sure that the request.user is in the set that belongs to the object. If not, redirect them to the login.
Here's my view:
class AwesomeDetail(LoginRequiredMixin, DetailView):
"""
An awesome detail
"""
template_name = "awesomeness/detail.html"
def get_object(self):
awesomeness = ModelName.objects.get(id=self.kwargs['pk'])
if self.request.user in awesomeness.staff.all():
return awesomness
else:
return redirect('account_login')
Staff is a many to many to users. What am I missing? The redirect isn't happening. It renders the template, but of course, awesomeness is missing.
UserPassesTestMixin was introduced in Django 1.9.
You define a test_func method to return True/Fales depending on whether the test passes.
If the user fails the test they will be redirected to settings.LOGIN_URL with a redirect field.
UserPassesTestMixin supports any of the fields for AccessMixin.
from django.contrib.auth.mixins import UserPassesTestMixin
class AwesomeDetail(UserPassesTestMixin, LoginRequiredMixin, DetailView):
"""
An awesome detail
"""
template_name = "awesomeness/detail.html"
model = ModelName
def test_func(self):
if self.request.user in self.object.staff.all():
return True
else:
return False
I'm not very sure about this, but sounds like get method is the one you should work on:
class AwesomeDetail(LoginRequiredMixin, DetailView):
"""
An awesome detail
"""
template_name = "awesomeness/detail.html"
def get(self, request, *args, **kwargs):
self.object = self.get_object()
if not self.request.user in self.object.staff.all():
return redirect('account_login')
else:
return super(AwesomeDetail, self).get(request, *args, **kwargs)

Django: How to get current user in admin forms?

In Django's ModelAdmin, I need to display forms customized according to the permissions a user has. Is there a way of getting the current user object into the form class, so that i can customize the form in its __init__ method?
I think saving the current request in a thread local would be a possibility but this would be my last resort because I'm thinking it is a bad design approach.
Here is what i did recently for a Blog:
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
form.current_user = request.user
return form
I can now access the current user in my forms.ModelForm by accessing self.current_user
EDIT: This is an old answer, and looking at it recently I realized the get_form method should be amended to be:
def get_form(self, request, *args, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user
return form
(Note the addition of *args)
Joshmaker's answer doesn't work for me on Django 1.7. Here is what I had to do for Django 1.7:
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, obj=None, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, obj, **kwargs)
form.current_user = request.user
return form
For more details on this method, please see this relevant Django documentation
This use case is documented at ModelAdmin.get_form
[...] if you wanted to offer additional fields to superusers, you could swap in a different base form like so:
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if request.user.is_superuser:
kwargs['form'] = MySuperuserForm
return super().get_form(request, obj, **kwargs)
If you just need to save a field, then you could just override ModelAdmin.save_model
from django.contrib import admin
class ArticleAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super().save_model(request, obj, form, change)
I think I found a solution that works for me: To create a ModelForm Django uses the admin's formfield_for_db_field-method as a callback.
So I have overwritten this method in my admin and pass the current user object as an attribute with every field (which is probably not the most efficient but appears cleaner to me than using threadlocals:
def formfield_for_dbfield(self, db_field, **kwargs):
field = super(MyAdmin, self).formfield_for_dbfield(db_field, **kwargs)
field.user = kwargs.get('request', None).user
return field
Now I can access the current user object in the forms __init__ with something like:
current_user=self.fields['fieldname'].user
stumbled upon same thing and this was first google result on my page.Dint helped, bit more googling and worked!!
Here is how it works for me (django 1.7+) :
class SomeAdmin(admin.ModelAdmin):
# This is important to have because this provides the
# "request" object to "clean" method
def get_form(self, request, obj=None, **kwargs):
form = super(SomeAdmin, self).get_form(request, obj=obj, **kwargs)
form.request = request
return form
class SomeAdminForm(forms.ModelForm):
class Meta(object):
model = SomeModel
fields = ["A", "B"]
def clean(self):
cleaned_data = super(SomeAdminForm, self).clean()
logged_in_email = self.request.user.email #voila
if logged_in_email in ['abc#abc.com']:
raise ValidationError("Please behave, you are not authorised.....Thank you!!")
return cleaned_data
Another way you can solve this issue is by using Django currying which is a bit cleaner than just attaching the request object to the form model.
from django.utils.functional import curry
class BlogPostAdmin(admin.ModelAdmin):
form = BlogPostForm
def get_form(self, request, **kwargs):
form = super(BlogPostAdmin, self).get_form(request, **kwargs)
return curry(form, current_user=request.user)
This has the added benefit making your init method on your form a bit more clear as others will understand that it's being passed as a kwarg and not just randomly attached attribute to the class object before initialization.
class BlogPostForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.current_user = kwargs.pop('current_user')
super(BlogPostForm, self).__init__(*args, **kwargs)