how to pass data in body of API request - django

I need to update some info in my database using PUT request and Django rest framework
I need to update max_date parameter by passing the value in the body of the query (pass True or False)
but i have 0 ideas how to do it
i only know how to update something by passing a variable in the url, like api/config/max_date/<int:boolean_by_number>
my URLs:
urlpatterns_config = [
path('all/', api_config.APIListConfig.as_view(), name='api info config'),
path('forever/', api_config.APIForever.as_view(), name='put or get forever config'),
path('max_date/', api_config.APIMaxDate.as_view(), name='put or get max_date config')
]
my API file:
class APIMaxDate(APIView):
def get(self, request, *args, **kwargs) -> JsonResponse:
config = Configuration.objects.get(name='max_date')
data = {
f"{config.name}": config.value
}
return JsonResponse(data, status=status.HTTP_200_OK)
def put(self, request, *args, **kwargs) -> JsonResponse:
return JsonResponse({}, status=status.HTTP_201_CREATED)

If values are passed via PUT, you can access them in django like so:
max_date = request.PUT.get('max_date', False)
if max_date:
# continue to use max_date to update your db instance

Related

how to customize the response of a api from retrieve function in mixin

I'm a beginner to Django, i have written a class-based API view with mixin. the functionality is simple i.e fetch the data of the given id.Im pasting the code below.
class GenericAPi(generics.GenericAPIView,mixins.ListModelMixin,mixins.RetrieveModelMixin):
serializer_class=ArticleSerializer
queryset=Article.objects.all()
lookup_field="id"
def get(self,request,id):
if id:
data=self.retrieve(request)
return Response({"data":data.data,"status":data.status_code})
else:
return self.list(request)
this is the response I'm getting
{"id":5,"title":"loream","author":"me"}
then I navigate to the retrieve function in the mixin, to make some changes in the response.
def retrieve(self, request, *args, **kwargs):
print('Retrieving')
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response({"result":serializer.data})
and then I make a call to API, but still, I'm getting the same response.
How to customize the response in the retrieve function itself.
I need response like this.
{"result":{"id":5,"title":"loream","author":"ipsum"}}
I think you don't have to customize retrieve function in the RetrieveModelMixin.
class GenericAPi(generics.GenericAPIView,mixins.ListModelMixin,mixins.RetrieveModelMixin):
serializer_class=ArticleSerializer
queryset=Article.objects.all()
lookup_field="id"
def get(self, request, id):
if id:
try:
article = Article.objects.get(pk = id)
return Response({"result": ArticleSerializer(article).data})
except Article.DoesNotExist:
return Response(status = status.HTTP_404_NOT_FOUND)
return
return self.list(request, *args, **kwargs)

Django Rest - testing api delete

Currrently I am testing my django rest api, but I am stuck on delete method.
My url looks like this
path('books/shelfs/<int:shelf>/readers/<int:pk>/',
views.ReaderViewSet.as_view(
{'get': 'retrieve', 'delete': 'destroy', 'patch': 'partial_update'}),
And My ViewSets looks like this
def destroy(self, request, pk=None, *args, **kwargs):
if request.data.get("book_type") is None:
raise ParseError(detail="book_type is required, options are : 'global, non-global'")
try:
instance = self.get_object()
user = self.request.user
serializer = self.get_serializer(self.get_object())
.......
self.perform_destroy(instance)
except Http404:
pass
return Response(status=status.HTTP_204_NO_CONTENT)
And my test case is this
def test_book_delete(self):
# check if delete works
book_type = {'book_type': 'global'}
response = self.client.delete("/api/v1/books/shelfs/{}/"
"readers/{}/".format(
1, 2), data=book_type)
self.assertEqual(response.status_code, 204)
But its alway 415 error
The question is, how to pass this book_type in delete ?
HTTP 415 means that the server refused to accept the JSON payload.
Docs:
If you need to explicitly encode the request body, you can do so by setting the content_type flag.
So try to set the content_type as follows:
response = self.client.delete("/api/v1/books/shelfs/{}/"
"readers/{}/".format(
1, 2), data=book_type, content_type="application/json")

Can't get max value of a field in Django Rest framework

I am trying to build a web app with DRF. I am trying to pass the customer id and display the maximum transaction he has done.
This is my views.py
class max_val(APIView):
def post(self, request, *args, **kwargs):
cid = json.loads(request.body).get('cid')
queryset = model_name.objects.filter(customer_id=cid).aggregate(Max('transaction'))
serialize = serializer_name(queryset, many=True)
return Response(serialize.data, status=status.HTTP_200_OK)
When I run this I get -
HTTP 200 OK
Allow: POST, OPTIONS
Content-Type: application/json
Vary: Accept
[
{
"customer_id": null,
"transaction": null
}
]
This is my serialiser -
class serialser_name(serializers.ModelSerializer):
class Meta:
model = model_name
fields = ('customer_id', 'transaction')
It is not returning the maximum value. How can I do that ?
aggregate not returning object, its just returning dictionary of field_name__max and it's value .try this:
class max_val(APIView):
def post(self, request, *args, **kwargs):
cid = json.loads(request.body).get('cid')
max_transaction = model_name.objects.filter(customer_id=cid).aggregate(Max('transaction'))
return Response(max_transaction, status=status.HTTP_200_OK)
you can find more information about aggregate here .

Django ModelViewSet PATCH request return model fields updated

class MerchantStampCardViewSet(viewsets.ModelViewSet):
'''
A view set for listing/retrieving/updating/deleting stamp cards for the current
merchant
'''
permission_classes = (IsMerchantAndAuthenticated, )
def get_queryset(self):
if len(MerchantProfile.objects.filter(user=self.request.user)) > 0:
merchant_profile = MerchantProfile.objects.get(user=self.request.user)
if merchant_profile.merchant:
return StampCard.objects.filter(merchant=merchant_profile.merchant)
return None
def get_serializer_class(self):
if self.request.method == 'GET':
return StampCardSerializerWithRewards
else:
return StampCardSerializer
I'm trying to make this code return the fields changed in the response body. The model class has a couple fields like name, city, province, zip code and address and through the front-end the user can only change one at a time, but I want the body of the 200 response to contain the field name changed and the new value just to confirm that a change was successful and nothing went wrong.
So for example if the user changes the name to Billy. The response should be 200 and the body should say {name : 'Billy'}
How do I do this?
You can try like this:
class YourViewSet(...):
def update(self, request, *args, **kwargs):
instance = self.get_object()
current_data = self.get_serializer(instance).data # collect current data
# next few lines of the code is from default implementation
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
if getattr(instance, '_prefetched_objects_cache', None):
instance._prefetched_objects_cache = {}
updated_data = serializer.data # now we get the updated data
response_dict = dict()
for key, value in current_data:
# find the differences
if updated_data.get(key) != value:
response_dict[key] = updated_data.get(key)
return Response(response_dict) # send the difference through response
Here I have put a override on update method. Then I have collected the dictionary data from current object and updated object. Then compared them and sent differences in a dictionary as response. FYI its an untested code.

How do we call a function each time an api end-point is called in django

In my Django server, there is an rest api through which we are saving the values in the database. If the name exists in the database then I update the value or else will create a new value and name. The code for the function is given below:
def getIgnitionData():
name_list =[]
value_list =[]
cursor = connections['ignition'].cursor()
cursor.execute('SELECT * FROM MDA_table')
value = cursor.fetchall()
cursor.execute('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = \'MDA_table\'')
name = cursor.fetchall()
for i in name:
name_list.append(str(i[0]))
for row in value:
for j in row:
value_list.append(str(j))
cursor.close()
print name_list
print value
#Here we will check to see if the variable exists. If so, update the value. If not,
#then create a new variable.
for k in range(0,len(name_list)):
if (Ignition.objects.filter(name = name_list[k]).exists()):
Ignition.objects.filter(name=name_list[k]).update(value = value_list[k])
else:
Ignition.objects.create(name=name_list[k], value=value_list[k])
The view_api.py is as follows:
class IgnitionViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows to view variables from the ignition database.
"""
serializer_class = IgnitionSerializer
#queryset = ignition.objects.all()
permission_classes = [HasGroupPermission]
required_groups = {
'GET': ['Admin', 'Facility', 'Operator'],
'PUT': [],
'POST': [],
}
ignition.getIgnitionData() # This is where we are calling the function
def get_queryset(self):
return Ignition.objects.all()
The code works well when I run the get request for the first time from the browser, but then if I update the values in the database without restarting the server then it doesn't even print the name_list (which means it doesn't call the code). If I restart the server and access the end point, then it does fetch the updated values. This is not practical though.
I wanted that whenever I call the api endpoint it fetches the updated values from the database so that I don't have to restart the server every time. Thanks in advance.
You can override dispatch() method which is calling each time your view is using:
class IgnitionViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows to view variables from the ignition database.
"""
serializer_class = IgnitionSerializer
#queryset = ignition.objects.all()
permission_classes = [HasGroupPermission]
required_groups = {
'GET': ['Admin', 'Facility', 'Operator'],
'PUT': [],
'POST': [],
}
def dispatch(self, request, *args, **kwargs):
ignition.getIgnitionData() # This is where we are calling the function
return super(IgnitionViewSet, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
return Ignition.objects.all()