Django filtering and deleting - python

I have this modelViewSet
class LikeViewSet(viewsets.ModelViewSet):
queryset = Likes.objects.all()
serializer_class = LikeSerializer
filter_fields = ('user','post')
def delete(self, request, pk, format=None):
post = Likes.objects.get(pk=pk)
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
I'm trying to filter using the url such as:
http://localhost:8000/likes/?user=anon&post=1
And then delete that specific result that I get from django but django keeps on giving me
delete() takes at least 3 arguments (2 given)
I can't really figure out why. Can anyone help please? Thanks! I'm using Django Rest Framework
EDIT: This is the model for the LikeViewSet:
class Likes(models.Model):
user = models.ForeignKey(Profile, related_name='liker')
post = models.ForeignKey(Post, related_name=' post' )
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
The idea is, it's a model table for a relationship between a user model and a post model so the filtering has to be done in the url that way

When you're using a ViewSet, you should use the destroy() method rather than delete().
See documentation here:
A ViewSet class is simply a type of class-based View, that does not
provide any method handlers such as .get() or .post(), and instead
provides actions such as .list() and .create().
Based on your code, it doesn't look like you're doing anything unique in the destroy/delete method. Are you fine with just using the default destroy function?

Related

The filters in django rest framework doesn't work with personalized generic view class

I am developing a hierarchical api for the models of my app, example:
https: //www.example.com/model/pk/submodel/pk2/subsubmodel/pk3 ...etc
so because of that I have to personalizate the generic view class adding the corresponding method for each action example:(def list(), def retrieve() ). The problem of that is things like pagination and filtering doesn't work, I have already solved the problem of pagination by adding a few lines of code in the list method but I have not found any way to solve the problem with the filters.
here's an example of my code:
the serializer:
class RegionSerializerList(serializers.ModelSerializer):
class Meta:
model = Region
fields = ('id', 'name')
The view:
class RegionList(generics.ListCreateAPIView):
queryset = Region.objects.all()
serializer_class = RegionSerializerList
pagination_class = PostPageNumberPagination
filter_backends = [SearchFilter, OrderingFilter]
search_fields = ['name', 'id']
def list(self, request, pk):
self.pagination_class = PostPageNumberPagination
queryset = Region.objects.filter(country=pk)
page = self.paginate_queryset(queryset)
#the following 3 lines fix the pagination problem
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = RegionSerializerList(queryset, many=True)
return Response(serializer.data)
the filters botom appears in the web but any function works, Neither the order nor the search works, It continues showing all the same data of the list, Using the URL filter (/?search=) doesn't, and neither using the get_queryset method.
I'm using filter_backends with SearchFilter, OrderingFilter and the search_fields that work perfectly without the personalised method list, but if i used that method doesn't works I don't know why and how to fix that. :(
I need the filter to work without having to change the personalized methods on which my api depends
Then I wonder if anyone here knows how to solve this problem and give me a hand, I would appreciate it a lot, thanks

Updating a model using Django Rest Framework and ViewSets

I'm new to DRF, and I'm trying to build a webhook that gives out lists of model objects and also allows these objects to be updated. I followed this tutorial http://www.django-rest-framework.org/tutorial/quickstart/, and have the following serializer and view:
class Task(serializers.ModelSerializer):
class Meta:
model = Task
fields = ('user', 'task', 'unixTime')
View:
class RequestViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows reqests to be viewed or edited.
"""
queryset = Task.objects.filter(done = False).order_by('-unixTime')
serializer_class = Task
paginate_by = None
def list(self, request, *args, **kwargs):
self.object_list = self.filter_queryset(self.get_queryset())
serializer = self.get_serializer(self.object_list, many=True)
return Response({'results': serializer.data})
I'm pretty sure I have to include a def update under def list, but the online resources I found were a bit unclear on how to implement them and what they do. Any help is appreciated.
#hackerman, Hmm..., if you followed the next step,
http://www.django-rest-framework.org/tutorial/quickstart/#urls
You will get an api address, it may looks like http://localhost:8000/task/1/, assume here is a task obj (id=1) in your db. Please open it in your browser and check that api works or not.
And then, you need a http client (requests is a good choice) to create a PUT request with json string data.
Hope those can help.
May be you just need to rename the serializer.
class TaskSerializer(serializers.ModelSerializer):
And don't forget replace in the viewset
serializer_class = TaskSerializer
After it you can remove your list method, because it is standard.

Is it complusory to pass instance to serializer in DRF?

Suppose I have a serializeDeviceGroup and a APIView class for posting devices into the group.
The serializer for DeviceGroup is
class DeviceGroupSerializer(serializers.ModelSerializer):
id = serializers.UUIDField(source='token', format='hex', read_only=True)
class Meta:
model = DeviceGroup
fields = ['id','name']
class DevicesGroupsAPIView(APIView):
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, token=None, format=None):
print('reqquest', request.data)
print('token', token)
device_group_instance = DeviceGroup.objects.get(token=token)
for device_token in request.data['devices']:
device = Device.objects.get(token=device_token, owner=request.user)
device.group = device_group_instance
device.save()
In above post function, is it compulsory to create a instance of serializer and check if serializer is valid then return the response.
The relation between Device and DeviceGroup is a device can be on only one group and a group can have multiple devices(list of device ids)
How should the post function be if i need to use DeviceGroupSerializer to post the list of devices? I did not understand this serializer and view part clearly.
Django REST framework is loosely coupled so you can bypass serializers.
However, depending on what you are doing, this may requires some work. Note that for POST you may perform some checks by yourself instead.
Tom Christies post on Django REST framework performances optimization illustrate how you can remove parts of the framework.

Set a custom queryset (select_related) for a list field in the admin change page?

I have an using the Django admin interface to manage a lot of objects, and one of the page is giving me issue, this page has a field to a related object (Foreign Key) that has a __str__ that also goes to its related objects, this make a lot of queries and is barely useable (Around 3000 queries to show the page as there are a LOT of objects).
I would like to know if there is a way to set a custom queryset ? I would like to add a select_related or prefetch_related to this element.
The part causing issue is this certificate requests list :
The page model (Certificate has the following attribute:
class Certificate(models.Model):
certificate_request = models.OneToOneField(
"CertificateRequest",
verbose_name=_("Certificate request"),
related_name="certificate",
blank=True,
null=True
)
And the related model has this :
class CertificateRequest(models.Model):
domain = models.ForeignKey(
"Domain",
verbose_name=_("Domain"),
related_name="certificate_requests"
)
def __str__(self):
return "{state} certificate request for {domain} from {creation_date}".format(
state=dict(self.STATUS).get(self.status),
domain=self.domain.fqdn,
creation_date=self.creation_date
)
What would be the way to fix this ? How can I set a queryset on this part ?
EDIT: I added more informations.
I tried using a custom form, but this didn't do any change :
class CertificateForm(forms.ModelForm):
certificate_request = forms.ModelChoiceField(queryset=CertificateRequest.objects.select_related("domain"))
class Meta:
model = Certificate
fields = "__all__"
#admin.register(Certificate)
class CertificateAdmin(CompareVersionAdmin):
model = Certificate
class Meta:
form = CertificateForm
You can create a custom ModelForm for your admin where you specify a ModelChoiceField for the ForeignKey. Here you can specify the queryset parameter:
# forms.py
class MyForm(forms.ModelForm):
certificate_request = forms.ModelChoiceField(queryset=CertReq.objects.foo().bar())
# select/prefetch-------^^^^^^^^^^^
class Meta:
model = Foo
# admin.py
class YourAdmin(ModelAdmin):
form = MyForm
The get_object method on the ModelAdmin class is what is responsible for retrieving the object to edit. You could certainly extend that method in your subclass to use select_related as necessary.

How Can I Use Two Different Model Serializers With the Same Model?

I'm using django-rest-framework. I have a model with a relation. I would like to just display the count of related items when a user hits the /modelname/ URL, but show the full related set when a user hits a specific model instance at /modelname/1/.
I can almost get what I want.
I have two serializers, like so:
class DataSetSerializer(serializers.ModelSerializer):
revisions = serializers.RelatedField(source='datasetrevision_set', many=True)
class Meta:
model = DataSet
fields = ('id', 'title', 'revisions')
class ShortDataSetSerializer(serializers.ModelSerializer):
class Meta:
model = DataSet
fields = ('id', 'title', 'revisions')
If I use the short version, I get the count of revisions (it's a calculated field). If I use the long version, I get the full list of related items as "revisions".
Short:
[{"id": 1, "title": "My Data Set", "revisions": 0}]
Long:
[{"id": 1, "title": "My Data Set", "revisions": ["Data Set v1", "Data Set v2"]}]
What I want to do is be able to switch between them based on query parameters (url). I tried to set the serializer_class to the ShortDataSetSerializer when the ID was not present, but it overrode all cases, not just the non-ID case.
class DataSetViewSet(viewsets.ModelViewSet):
serializer_class = DataSetSerializer
model = DataSet
def get_queryset(self):
try:
id = self.kwargs['id']
queryset = DataSet.objects.filter(id=id)
except KeyError:
queryset = DataSet.objects.all()
# We want to only list all of the revision data if we're viewing a
# specific set, but this overrides for all cases, not just the one
# we want.
self.serializer_class = ShortDataSetSerializer
return queryset
Is there a way I can make this work? I realize I may be approaching this in a totally ridiculous manner, but it seems like there should be an easy solution.
The data example I gave rather abbreviated compared to the real data I'm working with. The end goal is to show a subset of fields in list view, and every field in the GET for a specific ID. This is a read-only API, so I don't need to worry about POST/PUT/DELETE.
You could do it by overriding the get_serializer_class method:
class DataSetViewSet(viewsets.ModelViewSet):
model = DataSet
def get_queryset(self):
queryset = DataSet.objects.all()
if self.kwargs.get('id'):
queryset = queryset.filter(pk=self.kwargs.get('id'))
return queryset
def get_serializer_class(self):
return DataSetSerializer if 'id' in self.kwargs else ShortDataSetSerializer
I think one easy solution for this problem would be to use class based generic views instead of a viewset.
You can use a list create api view with serializer_class as ShortDataSetSerializer. So when you get the list of data it will have the count of revisions. Also if you want the post request to work on the same url you will then have to override the get_serializer_class method to set the serializer_class based on request type.
For the retrieve view you can use the serializer_class as DataSetSerializer. It will have a list of revisions instead of count.
Checkout generic views api guide on DRF docs website.
Also, you can override the list and retrieve methods on the viewset, but I would prefer to use class based views since DRF has a lot of additional functionalities attached to the request functions like get, put etc.(or list, detail) and it is better not to override them.
Thank you, Benjamin. That didn't do what quite I was looking for. Ultimately what I had to do was this (with the same serializers as above):
class DataSetViewSet(viewsets.ModelViewSet):
model = DataSet
def list(self, request):
queryset = DataSet.objects.all()
serializer = ShortDataSetSerializer(queryset, many=True)
return Response(serializer.data)
def detail(self, request, id=None):
queryset = DataSet.objects.get(id=id)
serializer = DataSetSerializer(queryset)
return Response(serializer.data)
And in the urls.py:
url(r'^sets/$', views.DataSetViewSet.as_view({'get': 'list'})),
url(r'^sets/(?P<id>\d+)/$', views.DataSetViewSet.as_view({'get': 'detail'})),

Categories