The error occurs when I request via post method.
views.py
class ConceptForkView(ConceptBaseView, mixins.CreateModelMixin):
print 'oclapi ConceptForkView111'
def dispatch(self, request, *args, **kwargs):
print 'oclapi ConceptForkView dispatch'
return super(ConceptForkView, self).dispatch(request, *args, **kwargs)
def post(self, request):
print 'oclapi ConceptForkView post'
urls.py
url(r'^forking/$', ConceptForkView.as_view(), name='concept-forking'),
ConceptBaseView
class ConceptBaseView(ChildResourceMixin):
lookup_field = 'concept'
pk_field = 'mnemonic'
model = Concept
permission_classes = (CanViewParentDictionary,)
child_list_attribute = 'concepts'
The command print 'oclapi ConceptForkView111' can run, but the method dispatch and post don't run. What is the reason?
I have searched many solutions, but they don't work to me. How can I solve this problem? Thank you.
Note that mixin is not a view. You probably have to inherit from View also. Mixins are usually classes that extend functionality and are not standalone. Class inheriting only from mixins probably won't work properly unless one of these mixins is not mixin in fact.
See: rest framework documentation. There is CreateAPIView which inherits not only from CreateModelMixin but also from GenericAPIView (and you probably should inherit it too). As we can read about GenericAPIView:
This class extends REST framework's APIView class, adding commonly required behavior for standard list and detail views.
So this "commonly required behavior" is important for your class to behave like view.
try using
def create(request, *args, **kwargs)
...
method instead of
def post(self, request):
...
CreateModelMixin uses create method rather than post method
Related
I am writing a class-based view to allow users to edit their profile. Since I want the users to access this view with a URL of the type my_profile/edit/ rather than something like profile/<int:pk>/edit/, using a view based on UpdateView is quite cumbersome since getting the user profile object requires to access the request object, and get_object does not directly have access to it.
My two questions:
Shall I use UpdateView in this case?
If so, what would be the best way to override get_object(self, queryset=None)? My best attempt so far is the following:
class EditProfileView(UpdateView):
model = UserProfile
_request = None
def get_object(self, queryset=None):
return get_user_profile(self._request)
def dispatch(self, request, *args, **kwargs): # can also override setup() in newer Django versions
self._request = request
return super().dispatch(request, *args, **kwargs)
This looks clean enough to me except that if one day the Django framework decides that get_object should be called early in the workflow of the view generation, this piece of code could break.
You don't need to do this. All class-based views make the request available as self.request.
Your entire code should just be:
class EditProfileView(UpdateView):
model = UserProfile
def get_object(self, queryset=None):
return get_user_profile(self.request)
Note, even if it didn't, you still wouldn't need to define _request at class level. That's just not necessary in Python.
Also, I don't know what your get_user_profile function does but it can probably be replaced with just self.request.user.profile.
My url looks like:
/api/v1/files/
/api/v1/files/100
Is it a good practice to use the same function to match them? just like the following:
class FileView(APIView):
parser_classes = (MultiPartParser,)
permission_classes = (IsAuthenticated,)
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return super(FileView, self).dispatch(request, *args, **kwargs)
def post(self, request, pk = None):
if pk is not None:
Do something
else:
do something
How to use different functions in Class-based views?
Thanks
I think having separate methods is cleaner than a single method that branches based on the pk. It's easier to understand which logic goes where by just looking at the view's methods rather than having to follow the (albeit simple) control flow.
My first recommendation would be to check out the viewsets that Django Rest Framework provides and look at performing your logic within the given methods it provides. This seems like it would best fit your use case.
Another option would be to look DRF's generic views which are based off these mixins. These allow more control and customization than the viewsets and are sometimes a better option if you don't need all of the functionality provided by the viewsets.
How can I add a ListCreateAPIView to the router urls?
Normally I do like:
router = routers.DefaultRouter()
router.register(r'busses', BusViewSet)
but now I have:
class CarList(generics.ListCreateAPIView): ...
I added it to the urlpatterns for now:
urlpatterns = patterns('',
url(r'^carts/', CarList.as_view(model=Car), name='cars'),
and I would like to add this Cars-view (which is working as intended if I call the url manually!) to the router, so it's in the overview page!
So: It works as it is, but I have to manually enter the url, it's not in the overview-page of the API.
The reason is why a ViewSet classes work with a router is a GenericViewSet which has a ViewSetMixin in a bases.
ViewSetMixin override as_view() method so that it takes an actions keyword that performs the binding of HTTP methods to actions on the Resource and router can build a map for action-method.
You can solve it by simple adding that mixin in a class bases:
from rest_framework.viewsets import ViewSetMixin
class CarList(ViewSetMixin, generics.ListCreateAPIView)
....
But it is not clear solution because ListCreateAPIView and ModelViewSet it is just an empty classes with a bunch of mixins in a bases. So you always can build you own ViewSet with a methods you need.
For example, here the code of ListCreateAPIView:
class ListCreateAPIView(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericAPIView):
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
And here the ModelViewSet:
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
pass
Notice a same mixins ListModelMixin and CreateModelMixin there is only difference in GenericViewSet and GenericAPIView.
GenericAPIView use method names and call an actions inside them. GenericViewSet instead use actions and map them to methods.
Here the ViewSet with methods you need:
class ListCreateViewSet(mixins.ListModelMixin,
mixins.CreateModelMixin,
GenericViewSet):
queryset_class = ..
serializer_class = ..
Now it will work with a router and you can override list and create methods if you need a special behavior.
I have a CB DeleteView that I am trying to decorate with Guardian's permission_required. The permission should be for the logged in user and for the object of the DeleteView. The Guardian docs aren't too clear about this, so I'm wondering if anyone could clarify.
I encountered almost the same problem and here is my solution (adapted to your case):
views.py
class MyModelDeleteView(DeleteView):
model=MyModel
#method_decorator(permission_required_or_403('myapp.delete_mymodel',
(MyModel, 'slug', 'slug'), accept_global_perms=True))
def dispatch(self, *args, **kwargs):
return super(MyModelDeleteView, self).dispatch(*args, **kwargs)
Note that you can pass accept_global_perms parameter, that is False by default. It allows users with 'myapp.delete_mymodel' permission to delete any object of MyModel class. This can be useful for moderators, for example.
Guardian Decorators documentation.
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.For xample,
class ExampleView(TemplateView):
template_name = 'Example.html'
#method_decorator(login_required)
def dispatch(self, *args, **kwargs):
return super(ExampleView, self).dispatch(*args, **kwargs)
The situation is pretty simple:
I'm writing a multi-user blog system. The system should prevent non-owner to edit or delete a blog post. In my view I use generic view.
class BlogUpdateView(UpdateView):
...
I know I should use #method_decorator to decorate dispatch method. However, most example is just #method_decorator(login_required) or model level permission. How can apply object level permission to check whether request.user is the author of this blog post?
For example, I tried to use django-authority apps, and I have a BlogPermission class in this file. and I tried to define a method in this class e.g.
def blog_edit(self, ??, ??)
what should I put into this method?
And then call this like:
#method_decorator(permission_required('blog_permission.blog_edit(???)'))
What should I pass in here?
Update: After read method_decorator code, I find it can only accept function without argument. I think that's why permission_required doesn't work here. But what's the work around about this?
Update solution:
In dispatch method, I check the user permission and then return HttpResponseForbidden() if the user does not meet the permission.
You can do it using class-based-views:
class BlogEdit(UpdateView):
model = Blog
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('blog_permission.blog_edit'):
return HttpResponseForbidden()
return super(BlogEdit, self).dispatch(request, *args, **kwargs)
# OR (for object-level perms)
def get_object(self, *args, **kwargs):
obj = super(BlogEdit, self).get_object(*args, **kwargs)
if not obj.user == self.request.user:
raise Http404 # maybe you'll need to write a middleware to catch 403's same way
return obj
Another option is to use UserPassesTestMixin (or user_passes_test for function-based).
class UserPassesTestMixin
When using class-based views, you can use the
UserPassesTestMixin to do this.
test_func()
You have to override the test_func() method of the class to
provide the test that is performed. Furthermore, you can set any of
the parameters of AccessMixin to customize the handling of
unauthorized users:
from django.contrib.auth.mixins import UserPassesTestMixin
class MyView(UserPassesTestMixin, View):
def test_func(self):
return self.request.user.email.endswith('#example.com')
We can now check if the self.request.user is allowed to process the details passed into the self.request.GET or self.request.POST.
class MyView(UserPassesTestMixin, View):
raise_exception = True # To not redirect to the login url and just return 403. For the other settings, see https://docs.djangoproject.com/en/3.2/topics/auth/default/#django.contrib.auth.mixins.AccessMixin
def test_func(self):
return (
self.request.user.is_staff
or self.request.user.has_perm('app.change_blog')
or self.request.user.email.endswith('#company.staff.com')
or is_requested_object_accessible(self.request.user, self.request.GET, self.request.POST) # If you have a custom checker
)
...