Joining Tables with Django Rest Framework API - python

I just want to know how to take two related database entities and send the desired joined data back as a single json response.
For instance, given the following files:
models.py
class Alpha(models.Model):
alpha_id = models.AutoField(primary_key = True)
data = models.CharField(max_length = 255, null = True)
class Beta(models.Model):
beta_id = models.AutoField(primary_key = True)
alpha_fk = models.ForeignKey(Alpha, null = True, on_delete = models.CASCADE)
data = models.CharField(max_length = 255, null = True)
serializers.py
class AlphaSerializer(serializers.ModelSerializer):
class Meta:
model = Alpha
fields = ('alpha_id','data')
class BetaSerializer(serializers.ModelSerializer):
class Meta:
model = Beta
fields = ('beta_id', 'alpha_fk', 'data')
views.py
#csrf_exempt
def AlphaDetailView(request, pk):
try:
modelObject = Alpha.objects.get(pk = pk)
except Alpha.DoesNotExist:
return HttpResponse(status = 404)
if request.method == 'GET':
serializer = AlphaSerializer(modelObject)
return JsonResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = AlphaSerializer(modelObject, data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status = 400)
elif request.method == 'PATCH':
data = JSONParser().parse(request)
serializer = AlphaSerializer(modelObject, data = data, partial = True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status = 400)
elif request.method == 'DELETE':
modelObject.delete()
return HttpResponse(status = 204)
#csrf_exempt
def BetaDetailView(request, pk):
try:
modelObject = Beta.objects.get(pk = pk)
except Beta.DoesNotExist:
return HttpResponse(status = 404)
if request.method == 'GET':
serializer = BetaSerializer(modelObject)
return JsonResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
serializer = BetaSerializer(modelObject, data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status = 400)
elif request.method == 'PATCH':
data = JSONParser().parse(request)
serializer = BetaSerializer(modelObject, data = data, partial = True)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status = 400)
elif request.method == 'DELETE':
modelObject.delete()
return HttpResponse(status = 204)
urls.py
urlpatterns = [
url(r'^alpha/(?P<pk>[0-9]+)/$', AlphaDetailView),
url(r'^beta/(?P<pk>[0-9]+)/$', BetaDetailView)
]
When I do a GET request on Alpha, I would like to be able to also get all of the fields in Beta which are related by the foreign key, effectively simulating an SQL join.
My coming here is in part a result of the fact that the DRF documentation I've found online seems very vague. Aside from digging through the source code, is there a place where I can go to see detailed information about the available objects and their built-in methods along with expected parameters and return types? All I've found is paragraph descriptions of what different objects do in a very general sense.

Related

PUT Request not Updating data-Django

I am using Postman to get my request. My GET and POST appear to be working fine. It's only when I go to update the data with PUT that its where I am running into the hiccup. Postman actually sends data back as if the object is being updated, but when I go to check via GET it's the same data as before. I have tried adding the hive data to the serializer.save, but it tells me I'm adding too many parameters.
models.py
class Inspection(models.Model):
hive = models.ForeignKey(Hive, on_delete=models.CASCADE)
user = models.ForeignKey(User,on_delete=models.CASCADE)
eggs = models.IntegerField()
larvae = models.IntegerField()
sealed_brood = models.IntegerField()
covered_bees = models.IntegerField()
nectar_honey = models.IntegerField()
pollen = models.IntegerField()
pest_spotted = models.CharField(max_length=200)
pest_action = models.CharField(max_length=200)
notes_concerns = models.CharField(max_length=300)
serializers.py
class InspectionSerializer(serializers.ModelSerializer):
class Meta:
model = Inspection
fields = ['id', 'eggs', 'larvae', 'sealed_brood', 'covered_bees', 'nectar_honey', 'nectar_honey', 'pollen', 'pest_spotted', 'pest_action', 'notes_concerns','user_id','hive','hive_id']
depth = 1
hive_id = serializers.IntegerField(write_only=True)
Views.py
#api_view(['GET', 'POST','PUT'])
#permission_classes([IsAuthenticated])
def inspection_details(request, pk):
hive = get_object_or_404(Hive, pk=pk)
inspection = Inspection.objects.filter(hive_id = hive.id, user=request.user)
if request.method == "GET":
serializer = InspectionSerializer(inspection, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
elif request.method == 'POST':
serializer = InspectionSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(user=request.user)
return Response(serializer.data,status.HTTP_200_OK)
elif request.method == 'PUT':
serializer = InspectionSerializer(hive, data=request.data)
if serializer.is_valid(raise_exception=True):
serializer.save(user=request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
I think the instance variable is not correct, it should be inspection not hive.
#api_view(['GET', 'POST', 'PUT'])
#permission_classes([IsAuthenticated])
def inspection_details(request, pk):
hive = get_object_or_404(Hive, pk=pk)
inspection = Inspection.objects.filter(hive_id = hive.id, user=request.user)
...
elif request.method == 'PUT':
serializer = InspectionSerializer(inspection, data=request.data) # here
if serializer.is_valid(raise_exception=True):
serializer.save(user=request.user)
return Response(serializer.data, status=status.HTTP_200_OK)
First of all don't use if clause for is_valid. it raise exception anyway so if is not needed.
Next you need to update your serializer not saving it.
elif request.method == 'PUT':
serializer = InspectionSerializer(hive, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.update(object_to_update,serializer.validated_data)
# if you want to return updated serialized data :
updated_serializer = InspectionSerializer(object_to_update)
return Response(updated_serializer.data, status=status.HTTP_200_OK)

Django form with dynamic queryset ModelMultipleChoiceField

I'm trying to pass a queryset to a forms ModelMultipleChoiceField as an initial value. I want to send a filtered queryset as all the choices and an initial selection. It seems to fail is_valid. Can anyone tell me what I'm doing wrong?
forms.py
class sendListForm(forms.Form):
recipients = forms.ModelMultipleChoiceField(queryset = CustomUser.objects.all())
title = forms.CharField(max_length=100,required=True)
description = forms.CharField(max_length=500,required=False,widget=forms.Textarea(attrs={'cols': 20, 'rows': 4}))
extraInfo = forms.CharField(max_length=500,required=False, help_text='Add a message to send',widget=forms.Textarea(attrs={"rows":4, "cols":20}),label='Extra Message')
startDate = forms.DateField(required=False,widget=forms.HiddenInput())
startTime = forms.TimeField(required=False,widget=forms.HiddenInput())
endDate = forms.DateField(required=False,widget=forms.HiddenInput())
endTime = forms.TimeField(required=False,widget=forms.HiddenInput())
yearName = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
recipients = kwargs.pop('recipients')
super(sendListForm, self).__init__(*args, **kwargs)
self.fields['recipients'] = forms.ModelMultipleChoiceField(queryset=recipients)
views.py
def eventSendList(request, modelPk=None):
event = get_object_or_404(Event, pk=modelPk)
if request.method == 'POST':
form = sendListForm(request.POST,recipients=CustomUser.objects.all())
if form.is_valid():
print('valid')
baseInfo = {
'recipients':recipients,
'title':event.title,
'description':event.description,
'startDate':event.startDate,
'startTime':event.startTime,
'endDate':event.endDate,
'endTime':event.endTime,
'yearName':event.yearName.name,
}
classParents = CustomUser.objects.all()
form = sendListForm(initial=baseInfo,recipients=classParents)
return render(request, 'page/sendListForm.html',{'form':form})
It never gets past the if form.is_valid():... in the view.
you can easily find your form errors by printing the form.errors field.

Saving FieldIn Django Via API Call

I'm trying to create a new CustomerDetail object whenever an api is called.But the issue is, Whenever I try to create something for the favorite meal multi-selectfield, I get this error:
MultiValueDictKeyError at /api/customer/favorite_meal/
"'favorite_meal'"
This is the api:
#csrf_exempt
def favorite_meal(request):
if request.method == "POST":
access_token = AccessToken.objects.get(token = request.GET.get("access_token"),
expires__gt = timezone.now())
customer = access_token.user.customer
details = CustomerDetailSerializer(
CustomerDetails.objects.create(
customer = customer,
favorite_mean = request.POST["favorite_meal"]
))
return JsonResponse({"status" : "success"})
Here are my models:
class CustomerDetails(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='customer')
TYPE = (
('Time', (
('Breakfast', 'Breakfast'),
('Lunch', 'Lunch'),
('Dinner', 'Dinner'),
)),
)
favorite_meal = MultiSelectField(choices=TYPE)
interested_in = MultiSelectField(choices=CATEGORIES)
likes = models.ManyToManyField('Product')
completed_orders = models.IntegerField(default = "0", blank=True)
shares = models.IntegerField(default = "0", blank=True)
average_order = models.FloatField(default = "0.0", blank=True)
def __str__(self):
return self.customer.user.get_full_name()
In the request, I'm trying to select breakfast and lunch via the api. How would I do this?
Sending Params:
The status says success but, there is no Customer data in details
The MultiValueDictKeyError error raises when you are trying to access favorite_meal from request.POST, but the favorite_meal doesn't exist in request.POST
Try this
#csrf_exempt
def favorite_meal(request):
if request.method == "POST":
if not 'access_token' in request.POST:
return JsonResponse({"status": "'access_token' missing in POST payload"})
access_token = AccessToken.objects.get(token=request.POST.get("access_token"),
expires__gt=timezone.now())
customer = access_token.user.customer
if not 'favorite_meal' in request.POST and not request.POST["favorite_meal"]:
return JsonResponse({"status": "'favorite_meal' missing in POST payload"})
details = CustomerDetailSerializer(
CustomerDetails.objects.create(
customer=customer,
favorite_mean=request.POST["favorite_meal"]
))
return JsonResponse({"status": "success"})
Screenshot
UPDATE
#csrf_exempt
def favorite_meal(request):
if request.method == "POST":
if not 'access_token' in request.POST:
return JsonResponse({"status": "'access_token' missing in POST payload"})
access_token = AccessToken.objects.get(token=request.POST.get("access_token"),
expires__gt=timezone.now())
customer = access_token.user.customer
if not 'favorite_meal' in request.POST and not request.POST["favorite_meal"]:
return JsonResponse({"status": "'favorite_meal' missing in POST payload"})
for favorite_meal in request.POST["favorite_meal"]:
details = CustomerDetailSerializer(
CustomerDetails.objects.create(
customer=customer,
favorite_mean=favorite_meal
))
return JsonResponse({"status": "success"})

how can this view be correctly saved in the model

Good day I'm trying to save this in my models, but it's not working.
def my_view(request)
data = data['my_data']
if request.method == 'POST':
data_a = request.POST.get(data["data_a"])
high_temp = request.POST.get(data["data_b"])
date = request.POST.get(data["data_c"])
wind_speed = request.POST.get(data["data_d"])
obj = WeatherData.objects.create(
data_a=data_a, data_b=data_b,
data_c=data_c, data_d=data_d,)
obj.save()
return render(request, 'data.html', obj)
Assuming that you have parameters in POST your code should look like this:
def my_view(request)
data = data['my_data']
if request.method == 'POST':
data_a = request.POST.get(data["data_a"])
data_b = request.POST.get(data["data_b"])
data_c = request.POST.get(data["data_c"])
data_d = request.POST.get(data["data_d"])
obj = WeatherData.objects.create(
data_a=data_a, data_b=data_b,
data_c=data_c, data_d=data_d,)
# obj.save() --> you don't need save because you already called `create`
return render(request, 'data.html', obj)
You can use ModelForm: https://docs.djangoproject.com/es/1.9/topics/forms/modelforms/#modelform
forms.py:
class WeatherForm(ModelForm):
class Meta:
model = Weather
fields = ['data_a', 'data_b', 'data_c', 'data_d']
views:
def my_view(request)
data = data['my_data']
form = WeatherForm(request.POST or None)
if form.is_valid():
obj = form.save()
else:
obj = None
return render(request, 'data.html', {'form': form, 'obj': obj})

Pagination based on filtered results in Django Rest Framework

I have a simple filtering in my get function which needs to be paginated. I tried different ways of solving this issue , but I am not able to figure out what exactly needs to be done. The following is my code
class UserInfoViewSets(viewsets.ViewSet):
def UserInfo_post(self,request, format=None):
if request.method == 'POST':
serializer = UserInfoSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def UserInfo_get(self,request,user_token):
if request.method == 'GET':
queryset = UserInfo.objects.filter(user_token=user_token)
serializer = UserInfoSerializer(queryset,many=True)
I need to paginate the get results. I dont want to write a customized pagination class, but just set the parameter to limit the no of results per page. I have tried setting the following in the setting.py file
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 2,
}
In my views file , I added ListAPIView
class UserInfoListView(generics.ListAPIView):
model = UserInfo
serializer_class = UserInfoSerializer
paginate_by_param = 'limit'
clearly I am overlooking some minor detail. I tried scouting thought the documentation. But it seems to be geared towards people who are well versed with django.
That is what I do for pagination, filtering, ordering etc... First you need to pip install django-filter (https://github.com/alex/django-filter)
settings.py
REST_FRAMEWORK = {
. . .
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'DEFAULT_FILTER_BACKENDS': (
'rest_framework.filters.DjangoFilterBackend',
),
'PAGE_SIZE': 1000
. . .
}
views.py
class UserViewSet(viewsets.ModelViewSet):
lookup_field = 'id'
queryset = Users.objects.order_by('id')
serializer_class = AccountSerializer
filter_backends = (SearchFilter, OrderingFilter)
search_fields = ('^id', '^first_name', '^last_name', '^email', ^username',)
I am using AngularJS,
function getUserList(limit, pageNumber, search, ordering) {
var def = $q.defer();
offset = (pageNumber -1) * limit;
$http.get(API_URL +
'?limit=' +limit +
'&offset=' + offset +
'&search=' + search +
'&ordering=' + ordering)
.success(function(data){
def.resolve(data);
})
.error(function(data){
def.resolve(data);
});
return def.promise;
}
Example query:
http://127.0.0.1:8000/user/?limit=10&offset=0&search=%20&ordering=id
Thanks #umut and #prashant. I am sharing my version plainly using DRF. Hopefully, others new to drf will find this easier.
def Users_get(self,request,user_token):
if request.method == 'GET':
queryset = Users.objects.filter(user_token=user_token)
paginator = PageNumberPagination()
result_page = paginator.paginate_queryset(queryset, request)
serializer = UsersSerializer(result_page,many=True)
return paginator.get_paginated_response(serializer.data)
This gave me response with next and previous url links.
In code below entire viewset is designed GET POST PUT DELETE also your query regarding pagination over filtered query is also handled.
Pagination is done using Paginator its a built-in in django
For docs cover most of the part go to django docs for that. DRF docs can be confusing sometimes but django docs are finely descriptive and easy to implement
views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
#api_view(['GET', 'POST'])
def product_list(request):
if request.method == 'GET':
products = Product.objects.all()
product_make_up = Product.objects.filter(item_category__exact='Make Up')[:3]
product_skincare = Product.objects.filter(item_category__exact='Skin Care')[:3]
product_fragrance = Product.objects.filter(item_category__exact='Fragrance')[:3]
product_personal_care = Product.objects.filter(item_category__exact='Personal Care')[:3]
product_hair_care = Product.objects.filter(item_category__exact='Hair Care')[:3]
product_item_category = QuerySetChain(product_make_up,
product_skincare,
product_fragrance,
product_personal_care,
product_hair_care)
item_cates = request.query_params.get('item_category',None)
if item_cates is not None:
product = products.filter(item_category=item_cates)
paginator = Paginator(product,5)
page = request.query_params.get('page')
product = paginator.page(page)
try:
product = paginator.page(page)
except PageNotAnInteger:
product = paginator.page(1)
except EmptyPage:
product = pagintor.page(paginator.num_pages)
serializer = ProductSerializer(product, many=True)
else:
paginator = Paginator(product_item_category,15)
page = request.query_params.get('page')
product_item_category = paginator.page(page)
try:
product_item_category = paginator.page(page)
except PageNotAnInteger:
product_item_category = paginator.page(1)
except EmptyPage:
product_item_category = pagintor.page(paginator.num_pages)
serializer = ProductSerializer(product_item_category, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = ProductSerializer( data=request.data)
# data.encode("base64")
if serializer.is_valid():
serializer.save()
res_msg = {'Success_Message' : 'Successful','Success_Code' : 200}
return Response(res_msg)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#api_view(['GET', 'PUT', 'DELETE'])
def product_detail(request, pk):
"""
Get, udpate, or delete a specific task
"""
try:
product = Product.objects.get(pk=pk)
except Product.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = ProductSerializer(product)
return Response(serializer.data , status=status.HTTP_201_CREATED)
elif request.method == 'PUT':
serializer = ProductSerializer(product, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(
serilizer.errors, status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
product.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Categories