Django: custom Action with date parameter not working - python

I have the following code in Python Django:
#action(detail=True, methods=['get'], url_path=r'by_date')
def get_meal_by_date(self, request, meal_date=date.today):
print(meal_date)
meals_by_date = Meal.objects.filter(
owner=self.request.user,
mealTimestamp__day=meal_date.day,
mealTimestamp__month=meal_date.month,
mealTimestamp__year=meal_date.year
)
serializer = self.get_serializer(meals_by_date, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
That Code works on call it like this:
http://localhost:8000/meals/by_date/
My problem is how can I enrich the method to gets data back with date parameter.
For example with this call:
http://localhost:8000/meals/by_date/2022-03-16T21:30:45.290Z/

What is your problem exactly, are you passing in the date in the url? That should work.
path('adress/date:date/', something like this. In your view get the date.

Related

How to pass data from request body in unit test django

I have modified get_queryset() method that looks like this:
def get_queryset(self):
type_of_review = self.request.data['type_of_review']
queryset = Reviews.objects.filter(type_of_review=type_of_review).order_by('-id')[:3]
return queryset
It sorts my models according to the type_of_review field, orders them and retrieves 3 last.
When I was trying to write a unit test to it, I ran into a problem that I cannot find a reliable or working way to pass filter argument into my get_queryset() method.
I tried doing it like this:
def test_list_three_review(self):
self.data = [ReviewsFactory.create() for i in range(10)]
response = self.client.get(self.url2, {"type_of_review": "Team"})
self.assertEqual(response.status_code, status.HTTP_200_OK)
But got an error: test_list_three_review - KeyError: 'type_of_review'
Can somebody explain to me what am I doing wrong?
Will gladly provide any additional information.
Thanks anyway!
OK, so self.client.get takes data which should be a dictionary. I think you want to do the following...
def test_list_three_review(self):
[ReviewsFactory.create() for i in range(10)]
data = {'type_of_review':'Team'}
response = self.client.get(self.url2, data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
Not exactly sure why you want to include a list of objects in the request since your get_queryset method overrides it anyway.
You are passing data as query string parameter. In-order to parse it in view you could use request.GET.get("type_of_review")
your get_queryset() method will become
def get_queryset(self):
type_of_review = self.request.GET.get('type_of_review')
queryset = Reviews.objects.filter(type_of_review=type_of_review).order_by('-id')[:3]
return queryset
GET request is not for passing json data, if you need to pass data as json anyway you could use POST method
then you can pass data like
def test_list_three_review(self):
self.data = [ReviewsFactory.create() for i in range(10)]
data = {"type_of_review": "Team"}
response = self.client.post(self.url2, data)
self.assertEqual(response.status_code, status.HTTP_200_OK)
and your get_queryset method will work as it is.

Django: Model.objects.filter() returns an empty Queryset

I am using Django for the Backend in my webapplication. I have a some data in my database which I can query just fine with the objects.get() method.
volumes = Volume.objects.get(volumeEngTitle="If there is a volume Title")
This returns the following value in Postman:
{
"volumeNumber": 8.1
"chapterNumber": 12
"Translated": True
}
But I need to return multiple volumes that have the same title. This is why I tried to use filter() but the result of the Http.Get in Postman is always an empty set, e.g {}. filter() seems to never find any instance that matches the volumeEngTitle even though it exists.
Here is the whole function to return the volume.
from rest_framework.response import Response
from .serializers import VolumeSerializer
from ..volumeModel import Volume
#api_view(['GET'])
def getVolume(request):
volumes = Volume.objects.filter(volumeEngTitle="If there is a volume Title")
#This works like a charm but only for a single instance:
#volumes = Volume.objects.get(volumeEngTitle="If there is a volume Title")
serializer = VolumeSerializer(volumes)
return Response(serializer.data)
The result of this method is always {}. Even though it works fine with the get() method.
I understand that get() is for a single return value and filter() gives me a queryset with mulitple values. But I can't figure out why filter doesn't return anything at all.
Do I need to specify anything for filter()?
EDIT:
The VolumeSerializer looks like this:
from rest_framework import serializers
from ..volumeModel import Volume
class VolumeSerializer(serializers.ModelSerializer):
class Meta:
model = Volume
fields = ['mangaVolumeNumber', 'chapterNumber', 'Translated']
I think you need to add many=True to your Serializer:
serializer = VolumeSerializer(volumes, many=True)
The Serializer.__init__ method expects a model instance, not a queryset.

How to manually populate a relational model in Django REST

I'm still new to Django and DRF. I have 2 models (Policy and Rescue) and Rescue is related to Policy by the Foreign Key policy_id. I have no issue to POST a JSON message and get Policy populated with the request data by CreateView. However, the 2nd model Rescue needs be populated based on some calculations from the JSON data POSTed to the Policy. Rescue cannot be POSTed beforehand. I tried hard but had no clue to do so.
Is this something to do with nested serializer or something else?
I've tried to
Can I try this way: inside the class CreateView:
class CreateView(generics.CreateAPIView):
def create(self, request, *args, **kwargs):
my_serializer = self.get_serializer(data=request.data)
...
# get a policy object based on 'policy_id' against serializer
my_policy = Policy.objects.get(policy_id=my_serializer.data['policy_id'])
...
... # some calculations to work out a rescue id, and will be returned and saved.
Rescue.objects.create(rescue_id='QD1234', policy=my_policy)
you can use a generic CreateAPIView and override the perform_create method.
def perform_create(self, serializer):
my_policy = serializer.save()
# you custom calculation for rescue_id
rescue_obj = Rescue.objects.create(rescue_id='QD1234', policy=my_policy)
perform create method is documented here: https://www.django-rest-framework.org/api-guide/generic-views/#methods

Django rest framework - API view complains about NoneType response

I am using a ModelViewSet to create objects from parameters received in a POST request. The serialiser looks like this:
class FooSerializer(ModelSerializer):
class Meta:
model = Foo
fields = '__all__'
I want to intercept the request, and perform a check on it (against a method of the model, if it matters) before allowing the creation to continue. In vanilla django forms, I would override the form_valid method, do the check, and then call super().form_valid(...). I am trying to do the same here:
class BookingView(ModelViewSet):
queryset = DirectBooking.objects.all()
serializer_class = DirectBookingSerializer
def create(self, request):
print(request.data)
#Perform check here
super().create(request)
This works, in the sense that it creates an object in the DB, but the trace shows an error:
AssertionError: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'NoneType'>`
This seems strange, as I would expect the super().save to return the appropriate response.
I am aware that I will need to return a response myself if the check fails (probably a 400), but I would still like to understand why it is failing here.
A view should return a HttpResponse. In a ViewSet, you do not implement .get(..) and .post(..) directly, but these perform some processing, and redirect to other functions like .create(..), and .list(..).
These views thus should then return a HttpResponse (or "friends"), here you call the super().create(request), but you forget to return the response of this call as the result of your create(..) version.
You should thus add a return statement, like:
class BookingView(ModelViewSet):
queryset = DirectBooking.objects.all()
serializer_class = DirectBookingSerializer
def create(self, request):
print(request.data)
#Perform check here
return super().create(request)

How to properly get a serializer instance in Django view?

I'm trying to get an instance of a serializer in my overwritten list method and then pass it in through perform_create. Basically what this code does is it checks if the queryset is empty and if it is, we do a perform_create. The problem is that I'm trying to get an instance of the serializer so I can pass it in to the perform_create method. I don't believe the line serializer = self.get_serializer(data=request.data)
correctly grabs the serializer as it shows nothing when I try to log it. Any help is appreciated, thanks.
class ExampleViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
queryset = Example.objects.all()
serializer_class = ExampleSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwner)
def list(self, request):
queryset = self.get_queryset()
name = self.request.query_params.get('name', None)
# print(request.data)
if name is not None:
queryset = queryset.filter(name=name)
if (queryset.count() == 0):
serializer = self.get_serializer(data=request.data)
print(serializer)
return self.perform_create(serializer)
return HttpResponse(serializers.serialize('json', queryset))
elif name is None:
return HttpResponse(serializers.serialize('json', queryset))
As far as I can see, with
serializer = self.get_serializer(data=request.data)
you are trying to access POST data while responding to a GET request.
DRF ViewSets offer the methods:
list (called upon an HTTP GET request)
create (called upon an HTTP POST request)
retrieve (called upon an HTTP GET request)
update (called upon an HTTP PUT request)
partial_update (called upon an HTTP PATCH request)
destroy (called upon an HTTP DELETE request)
Also see this explicit example binding HTTP verbs to ViewSet methods
So if
you are POSTing data, the list method isn't called at all (as suggested by #Ivan in the very first comment you got above).
The solution is to move the code to the appropriate method, i.e create
Otherwise
your client is GETting, the list method is called, but request.data will be empty at best.
The solution is to make the client provide the parameters for the creation as GET parameters, along with name.
That way the view will find them in self.request.query_params
In case you have a form, simply change the way it sends its data by making it use HTTP GET. See here for further info

Categories

Resources