how to protect user from editing others blog posts in django? - 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

Related

How can I add if statement to a deleteview? CBV django

Hey I want to add if statement and according to it decide if to delete the object or not.
I could not find it online.
In general how can I add if statements to any CBV including Update for example..
This is my DeleteView func:
class PostDeleteView(LoginRequiredMixin, DeleteView):
model = Post
success_url = reverse_lazy('TheApp:post_list')
EDIT! THE SOLUTION THAT WORKED FOR ME:(Thanks to AKX)
def delete(self, request, *args, **kwargs):
if (Post.author == request.user.username):
return super().delete(request, *args, **kwargs)
else:
return HttpResponse('You are not the owner of this Post! You can not delete it!')
Well, as you know, CBVs' methods map to HTTP methods, so just override delete() and add your condition:
class SomeView(..., DeleteView, ...):
def delete(self, request, *args, **kwargs):
if request.GET.get('really') != 'true':
return HttpResponse('I knew you were just kidding!')
return super().delete(request, *args, **kwargs)

Decorator for CBV

I have this CBV:
class GetStuff(View):
def get(self, request, company_id):
...
I want to decorate the get function with a custom function which takes the request and company_id arguments and check some permissions.
Any idea on how to achieve this? Most information I've found about decorators focus on FBV.
This is what I have so far:
def custom_decorator(func_view):
def wrapper(request, company_id):
if not request.user.is_staff:
# do_something()
return func_view(request, company_id)
return wrapper
You have to decorate the dispatch method
To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class. django doc
from django.utils.decorators import method_decorator
class GetStuff(View):
#method_decorator(custom_decorator)
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
or
#method_decorator(custom_decorator, name='dispatch')
class GetStuff(View):
def dispatch(self, *args, **kwargs):
return super(GetStuff, self).dispatch(*args, **kwargs)
you can find other decorating techniques in the docs

Django mixins for class-based-generic views

I am trying to implement staff_member_required mixins:
Here are the two ways I found on how to do so:
First:
class StaffRequiredMixin(object):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
Second:
class StaffRequiredMixin(object):
#classmethod
def as_view(self, *args, **kwargs):
view = super(StaffRequiredMixin, self).as_view(*args, **kwargs)
return staff_member_required(view)
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
What I want to know is:
Why the second way is overriding the as_view() method and wrapping it with staff_member_required ?
Do we get any 'additional' advantages by doing so ?
I am new to these mixins. Please help.
TL; DR: they're close to the same, the difference is in checking is_active as well as is_staff and the error messages. You probably don't need both because the as_view override negates the need for the dispatch override anyway.
These are really just two ways of doing close to the same thing.
This code:
class StaffRequiredMixin(object):
#classmethod
def as_view(self, *args, **kwargs):
view = super(StaffRequiredMixin, self).as_view(*args, **kwargs)
return staff_member_required(view)
...could actually be used alone to implement the staff_member_required decorator. In this case the staff_member_required functionality gets called in the view's as_view() function (i.e., from as_view() in your URLConf).
This code:
class StaffRequiredMixin(object):
#method_decorator(login_required)
def dispatch(self, request, *args, **kwargs):
if not request.user.is_staff:
messages.error(
request,
'You do not have the permission required to perform the '
'requested operation.')
return redirect(settings.LOGIN_URL)
return super(StaffRequiredMixin, self).dispatch(request,
*args, **kwargs)
...filters users in the dispatch method. You can see in the Django codebase that as_view actually calls dispatch. This means that if you use both together you won't actually ever trigger the if not request.user.is_staff code in the dispatch method because any user who doesn't pass would have been filtered out in the as_view method.
The second difference is that staff_member_required is slightly different from what the first code does. If you check out the code, you'll notice that staff_member_required also checks whether the user's is_active flag passes (not just is_staff like in your dispatch decorator). It also doesn't pass the messages.error like in your code.

Exclude a field in django admin for users not super admin

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'))

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)