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")
Related
get the form and process it in post. It is necessary to save the unique uuid of the record to the model, I do it like this: formOne.save(related_uuid=related_uuid)
but doesn't work, the error is - save() got an unexpected keyword argument 'related_uuid'
models
class Orders(models.Model):
device = models.CharField(max_length=150)
uuid = models.CharField(max_length=22, blank=True)
views
class OrderAddView(TemplateView):
template_name = 'orders/order_add.html'
def get(self, request, *args, **kwargs):
context = super().get_context_data(**kwargs)
... some work code
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
related_uuid = shortuuid.uuid()
formOne.save(related_uuid=related_uuid)
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)
def form_invalid(self, formOne,, **kwargs):
context = self.get_context_data()
... some work code
return self.render_to_response(context)
assuming that "shortuuid.uuid()" return correct uuid; try
def post(self, request, *args, **kwargs):
formOne = SimpleOrderAddForm(self.request.POST, prefix='one_form')
if formOne.is_valid():
a=formOne.save(commit=False)
a.related_uuid=shortuuid.uuid()
a.save()
return HttpResponseRedirect('orders_home')
else:
print('NotValid')
return self.form_invalid(formOne, **kwargs)
I work on the API in the Django REST Framework. And now there is such a problem: there is a ModelViewSet and in its functions the same request to the database, the same check in the if block. Is it possible to somehow move this matter into a separate function and how to do it?
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def retrieve(self, request, *args, **kwargs):
instance = Link.objects.filter(Q(user_id=self.request.user.id) & Q(id=kwargs["pk"])).first()
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return super().retrieve(request, *args, **kwargs)
def partial_update(self, request, *args, **kwargs):
instance = Link.objects.filter(Q(user_id=self.request.user.id) & Q(id=kwargs["pk"])).first()
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return super().partial_update(request, *args, **kwargs)
If I understand the code correctly, the intention is to limit queryset to allow access only to links owned by currently logged in user. For that, you can just override get_queryset method and that is it. DRF will take care of getting object from the queryset by id and throwing 404 if the object is not found.
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
def get_queryset(self):
return Link.objects.filter(user_id=self.request.user.id)
You could do some of the implementation in a private method
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id)
& Q(pk)).first()
Or, taking it a step further, have a common implementation that uses getattr to decide which base implementation to use.
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def _retrieve_op(self, method, request, *args, **kwargs):
instance = self._link_objects_filter(kwargs["pk"])
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return getattr(super(), method)(request, *args, **kwargs)
def retrieve(self, request, *args, **kwargs):
return self._retrieve_op("retrieve", request, *args, **kw)
def partial_update(self, request, *args, **kwargs):
return self._retrieve_op("partial_update", request, *args, **kw)
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id)
& Q(pk)).first()
That could be further reduced with partial methods
import functools
class LinkViewSet(ModelViewSet):
permission_classes = (IsAuthenticated,)
serializer_class = LinkSerializer
queryset = Link.objects.all()
def _retrieve_op(self, request, method, *args, **kwargs):
instance = self._link_objects_filter(kwargs["pk"])
if not instance:
return Response(data="Not found", status=status.HTTP_404_NOT_FOUND)
return getattr(super(), method)(request, *args, **kwargs)
retrieve = functools.partialmethod(_retrieve_op, "retrieve")
partial_update = functools.partialmethod(_retrieve_op, "partial_update")
def _link_objects_filter(self, pk):
return Link.objects.filter(Q(user_id=self.request.user.id) & Q(pk)).first()
I'm not sure whether django meta programming will mess this up.
Can't figure out why CreateView doesn't return HttpResponse. For now, I use this view just for posting (no GET). I thought that set self.success_url should be enough (as you can see in def post).
class TripCreationView(CreateView):
form_class = TripCreationForm
template_name = 'frontend/homepage.html'
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
super(TripCreationView, self).post(self, request, *args, **kwargs)
#
# def form_valid(self, form):
# trip = form.save(self.request)
# return HttpResponseRedirect(self.success_url)
def get_form_kwargs(self):
kwargs = super(TripCreationView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
Do you know what to do?
You forgot a return statement.
def post(self, request, *args, **kwargs):
self.success_url = request.POST.get('success_url') or reverse('frontend:homepage')
return super(TripCreationView, self).post(self, request, *args, **kwargs)
My problem:
UpdateView create new object instead of updating previous, i think its happens because in class definition of my view i override get_object method like this:
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
And so if current user is not owner of the object - this method return nothing - its what i wanted, but my form class instead create new object:
class ClientCreation(forms.ModelForm):
class Meta:
model = Client
fields = ('name', 'loyal')
I think this is happened because form doesn't receive a self.instance and create new instead - what should i do in this situation? I don't want new object to be created, in case when owner of object is not the current user i want nothing to happend then sending such a post request. How should i correctly implement this?
UPDATE views.py:
class Distinct(generic.UpdateView):
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
def get_form_kwargs(self):
kwargs = super(Distinct, self).get_form_kwargs()
if self.request.user.is_authenticated():
kwargs.update({'user': self.request.user})
return kwargs
def post(self, request, *args, **kwargs):
if request.POST.get('action', '') == 'Delete':
object_get = self.get_object()
request.session['deleted_data'] = str(object_get)
object_get.delete()
return redirect(reverse('crm:main'))
else:
return super(Distinct, self).post(request, *args, **kwargs)
def get_success_url(self):
return reverse('crm:{}'.format(self.distinct_template), kwargs={'pk': self.kwargs['pk']})
class DistinctClient(Distinct):
form_class = ClientCreation
model = Client
template_name = 'crm/client_detail.html'
all_template = 'clients'
distinct_template = 'client'
def get_form_kwargs(self):
return generic.UpdateView.get_form_kwargs(self)
In UpdateView, if get_object returns None django will create a new object.So instead of return None do whatever you want.
def get_object(self, queryset=None):
try:
object_get = self.model.objects.get(pk=self.kwargs['pk'])
except ObjectDoesNotExist:
raise Http404("No object found matching this query")
if self.request.user.is_authenticated():
if object_get.owner == self.request.user:
return object_get
raise My #do something here.
UPDATE
class My(Exception):
pass
class DistinctClient(Distinct):
form_class = ClientCreation
model = Client
template_name = 'crm/client_detail.html'
all_template = 'clients'
distinct_template = 'client'
def dispatch(self, *args, **kwargs):
try:
return super(DistinctClient, self).dispatch(*args, **kwargs)
except My:
return redirect #to do or (return render(self.request, 'mytemplate.html', {}))
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)