Episode has GenericRelation with Like model
class Episode(models.Model):
likes = GenericRelation(Like)
Viewset:
class EpisodeViewSet(viewsets.ModelViewSet, LikedMixin):
serializer_class = EpisodeSerializer
permission_classes = (IsOwnerOrReadOnly,)
def get_queryset(self):
queryset = Episode.objects.filter(story=self.kwargs.get('story_id'))
return queryset
urls:
router.register(r'(?P<story_id>\d+)/episodes', views.EpisodeViewSet, basename='episodes')
LikedMixin has function for performing like action:
#action(detail=True, methods=['POST'])
def like(self, request, pk=None):
obj = self.get_object()
services.add_like(obj, request.user)
return Response()
When I try post request with url 0.0.0.0:3000/stories/1/episodes/1/like/, i recieve error
like() got an unexpected keyword argument 'story_id'.
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
if isinstance(obj, (Story,)):
return obj.author == request.user
elif isinstance(obj, (Episode,)):
return obj.story.author == request.user
You can try like this..
#action(detail=True, methods=['POST'])
def like(self, request, pk=None, **kwargs):
obj = self.get_object()
services.add_like(obj, request.user)
return Response()
Related
In my detailView I have 2 methods get_context_data and post. In get_context_data I can get the detailView pk with self.object.pk but how can I get it in the post method?
[ updated ]
here is the view
class Class_detailView(LoginRequiredMixin, DetailView):
login_url = '/'
model = Class
template_name = "attendance/content/teacher/class_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['attendance_form'] = AttendanceForm(current_class_pk=self.object.pk) # pass data to form via kwargs
return context
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST)
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))
form
class AttendanceForm(forms.ModelForm):
class Meta:
model = Attendance
fields = ['student',]
def __init__(self, *args, **kwargs):
current_class_pk = kwargs.pop('current_class_pk')
super(AttendanceForm, self).__init__(*args, **kwargs)
current_student = Class.objects.get(id=current_class_pk)
self.fields['student'].queryset = current_student.student
I want to get the pk and pass it to the form when the post request is called.
How can I do it?
did you try this:
def post(self, request, *args, **kwargs):
if request.method == "POST":
attendance_form = AttendanceForm(request.POST, current_class_pk=self.kwargs.get('pk'))
if attendance_form.is_valid():
attendance_form.instance.teacher = self.request.user
attendance_form.save()
return redirect('class-detail', pk=self.kwargs.get('pk'))
I want to arrange permission like that each user can edit his own profile. Just super user can edit all profile. What I need to add permissions.py ? Thank you.
views.py
class UserViewSet(mixins.ListModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
generics.GenericAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
authentication_classes = (JSONWebTokenAuthentication, )
permissions.py
class IsOwnerOrReadOnly(BasePermission):
message = '!!'
my_safe_method = ['GET', 'PUT']
def has_permission(self, request, view):
if request.method in self.my_safe_method:
return True
return False
def has_object_permission(self, request, view, obj):
# member .0 Membership.objects.get(user=request.user)
# member.is_active
if request.method in SAFE_METHODS:
return True
return obj.user == request.user
Write your own permission
class IsObjectOwner(BasePermission):
message = "You must be the owner of this object."
my_safe_methods = ['GET', 'PUT', 'PATCH', 'DELETE']
def has_permission(self, request, view):
if request.method in self.my_safe_methods:
return True
return False
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return obj
else:
return obj == request.user
and then in the view add it in permission_classes
class UserDetailView(RetrieveUpdateDestroyAPIView):
permission_classes = [IsObjectOwner, permissions.IsAuthenticated]
I am trying to return custom json with get_queryset but always get 404 error in response.
class TestViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows groups to be viewed or edited.
"""
queryset = Test.objects.all()
serializer_class = TestSerializer
def get_queryset(self):
if self.request.method == "GET":
content = {'user_count': '2'}
return HttpResponse(json.dumps(content), content_type='application/json')
If I delete everything starting from def I'll got correct response with standard json data. What I am doing wrong?
If you don't need a ModelViewSet and just want custom JSON on a GET request
You can also use an APIView, which doesn't require a model
class MyOwnView(APIView):
def get(self, request):
return Response({'some': 'data'})
and
urlpatterns = [
url(r'^my-own-view/$', MyOwnView.as_view()),
]
With a ModelViewSet
You've put the custom JSON into get_queryset, that's wrong. If you want to use a ModelViewSet, this by itself should be enough:
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
This ModelViewSet comes with default implementations for .list(), .retrieve(), .create(), .update(), and .destroy(). Which are available for you to override (customize) as needed
Returning custom JSON from .retrieve() and/or .list() in ModelViewSet
E.g. to override .retrieve() to return custom view when retrieving a single object. We can have a look at the default implementation which looks like this:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
So as an example to return custom JSON:
class TestViewSet(viewsets.ModelViewSet):
queryset = Test.objects.all()
serializer_class = TestSerializer
def retrieve(self, request, *args, **kwargs):
return Response({'something': 'my custom JSON'})
def list(self, request, *args, **kwargs):
return Response({'something': 'my custom JSON'})
There are 2 ways to custom the response in Class-based views with ModelViewSet
Solution 1: custom in views.py
class StoryViewSet(viewsets.ModelViewSet):
permission_classes = (permissions.AllowAny,)
queryset = Story.objects.all()
serializer_class = StorySerializer
def retrieve(self, request, *args, **kwargs):
# ret = super(StoryViewSet, self).retrieve(request)
return Response({'key': 'single value'})
def list(self, request, *args, **kwargs):
# ret = super(StoryViewSet, self).list(request)
return Response({'key': 'list value'})
Solution 2: custom in serializers.py (I recommend this solution)
class StorySerializer(serializers.ModelSerializer):
class Meta:
model = Story
fields = "__all__"
def to_representation(self, instance):
ret = super(StorySerializer, self).to_representation(instance)
# check the request is list view or detail view
is_list_view = isinstance(self.instance, list)
extra_ret = {'key': 'list value'} if is_list_view else {'key': 'single value'}
ret.update(extra_ret)
return ret
My view:
class ModEmailDeleteView(DetailView):
model = EmailModel
template_name = "email_delete.html"
success_url = reverse_lazy('moderator_profile', request.user.id)
Here I want to check if the user of particular filed is request user then only he can delete. Like
if obj.user == request.user
then only he can delete or throw 404
Also what if I have multiple primary key in the url?? and want different validations using those primary keys
You can do it like this:
class ModEmailDeleteView(DeleteView):
model = EmailModel
template_name = "email_delete.html"
success_url = reverse_lazy('moderator_profile', request.user.id)
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
if self.object.user == request.user:
self.object.delete()
return HttpResponseRedirect(self.get_success_url())
else:
raise Http404 #or return HttpResponse('404_url')
There is mixin for django Deleview , you just override the delete
function in your view,
class DeletionMixin(object):
"""
A mixin providing the ability to delete objects
"""
success_url = None
def delete(self, request, *args, **kwargs):
"""
Calls the delete() method on the fetched object and then
redirects to the success URL.
"""
if self.object.user = request.user:
self.object.delete()
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
I've been trying for a while now to get 'DELETE' to work when reading a collection in Django Api View. I've been using 'ListCreateAPIView' and that only provides get and post method handlers. Does anyone know how to fix this?
My view:
class NotepadDetail(generics.ListCreateAPIView):
model = Session
serializer_class = SessionSerializer
def get_queryset(self):
user=self.request.user
notepad = self.kwargs['notepad_pk']
return Session.objects.filter(user=user, notepad=notepad)
def pre_save(self, obj):
obj.user = self.request.user
obj.notepad = get_object_or_404(Notepad, user=self.request.user, pk=self.kwargs['notepad_pk'])
Solved it by adding:
def get_object(self, notepad_pk):
try:
return Notepad.objects.get(user=self.request.user, pk=notepad_pk)
except Notepad.DoesNotExist:
raise Http404
def delete(self, request, notepad_pk, format=None):
object = self.get_object(notepad_pk)
object.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
You can use a DestroyModelMixin:
class NotepadDetail(generics.DestroyModelMixin,
generics.ListCreateAPIView):
model = Session
serializer_class = SessionSerializer
def get_queryset(self):
user=self.request.user
notepad = self.kwargs['notepad_pk']
return Session.objects.filter(user=user, notepad=notepad)
def pre_save(self, obj):
obj.user = self.request.user
obj.notepad = get_object_or_404(Notepad, user=self.request.user, pk=self.kwargs['notepad_pk'])
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
The self.destroy method provided by the DestroyModelMixin will handle the object deletion, will correctly raise the pre_delete and post_delete signals, and will return the 204 NO CONTENT status.
def delete(self, request, pk, format=None):
event = self.get_object(pk)
event.delete()
return Response("Object Deleted")