I can not send a post to django rest framework - python

I created a serializer to post a results, but when I try to use postman it says that the value 'enrollment_id' is null:
views.py
from rest_framework import generics
from .serializers import ResponseSerializer
class ResponseCreate(generics.CreateAPIView):
serializer_class = ResponseSerializer
serializers.py
class ResponseSerializer(serializers.ModelSerializer):
class Meta:
model = Response
fields = (
'enrollment_id',
'evaluation_id',
'question_id',
'question_component_id',
'user_id',
)
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.ResponseCreate.as_view()),
]
In the postman post body I send the following json:
{
"enrollment_id": 1,
"user_id": 2,
"question_component_id": 2,
"question_id": 1,
"evaluation_id": 1
}
Error postman:
IntegrityError at /response/
null value in column "enrollment_id" violates not-null constraint
DETAIL: Failing row contains (9, null, null, null, null, null).
view
Edit:
Model Response:
class Response(models.Model):
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
)
enrollment = models.ForeignKey(
Enrollment,
on_delete=models.CASCADE,
)
evaluation = models.ForeignKey(
Evaluation,
on_delete=models.CASCADE,
)
question = models.ForeignKey(
Question,
on_delete=models.CASCADE,
)
question_component = models.ForeignKey(
Question_Component,
on_delete=models.CASCADE,
)

Edit:
Your serializer need not add '_id' after the Model fields.
It should be like this:
class ResponseSerializer(serializers.ModelSerializer):
class Meta:
model = Response
fields = (
'enrollment',
'evaluation',
'question',
'question_component',
'user',
)
Now try seding the modified JSON via Postmen:
{
"enrollment": 1,
"user": 2,
"question_component": 2,
"question": 1,
"evaluation": 1
}
You need to define a queryset in your view:
class ResponseCreate(generics.CreateAPIView):
queryset = Response.objects.all()
serializer_class = ResponseSerializer

You have to indicate in your url patter the methods that are going to be allowed:
urlpatterns = [
path('', views.ResponseCreate.as_view({'get': 'list'})),
]
I can see in the error you get that `GET method is not allowed, that's because you didn't indicate Django to allow it.
Try this view:
from rest_framework import viewsets
from .serializers import ResponseSerializer
class ResponseCreate(viewsets.ModelViewSet):
queryset = models.Response.objects.all()
serializer_class = serializers.ResponseSerializer
ModelViewSet has already the proper response for al methods, but you have to indicate in your url pattern which ones are allowed.

Related

Getting an empty list when using filter - Django REST

I have my API in Django REST Framework:
Here is my models.py:
class myModel(models.Model):
user_email = models.CharField(max_length= 200, null= False)
Here is my views.py:
class GetItemsByEmail(generics.ListAPIView):
def get_queryset(self):
email_items = self.request.query_params.get("user_email")
if(email_items is not None):
itemsReturned = myModel.objects.all().filter(user_email = email_items)
return Response(data= itemsReturned)
Here is my urls.py:
url_patterns = [
path('users/account=<str:id>/shipments', GetItemsByEmail.as_view()),
]
My Question:
I am getting an empty list, getting nothing from making an API call to the above endpoint.
I want to get all the items in the database associated with a particular email?
In your views.py:
from rest_framework import generics
from .models import * # noqa
from .serializers import *
class GetItemsByEmail(generics.ListAPIView):
queryset = MyModel.objects.all() # noqa
serializer_class = MyModelSerializer
def get_queryset(self):
if self.kwargs.get('user_email_pk'):
return self.queryset.filter(id=self.kwargs.get('user_email_pk'))
return self.queryset.all()
In models.py I had to create another model to have the result that you want (get all database by a specific user_email!):
from django.db import models
class MyModel(models.Model):
user_email = models.CharField(max_length=200, null=False)
def __str__(self):
return self.user_email
class ServicesModel(models.Model):
# Just an example to emulate the expected result, do not worry about it!
name = models.CharField('Name', max_length=200)
user_email_service = models.ForeignKey(MyModel, related_name='services', on_delete=models.CASCADE) # Just an example to emulate the expected result, do not worry about it!
def __str__(self):
return self.name
In serializers.py:
from rest_framework import serializers
from .models import MyModel, ServicesModel
class ServiceModelSerializer(serializers.ModelSerializer):
class Meta:
model = ServicesModel
fields = (
'name',
)
class MyModelSerializer(serializers.ModelSerializer):
services = ServiceModelSerializer(many=True, read_only=True)
class Meta:
model = MyModel
fields = (
'id',
'user_email',
'services',
)
In urls.py:
from django.urls import path
from core.views import GetItemsByEmail
urlpatterns = [
path('users/', GetItemsByEmail.as_view(), name='users'), # Ignore!
path('users/account=<str:user_email_pk>/shipments/', GetItemsByEmail.as_view(), name='user_email'),
]
In the test that I made localy I created two 'user_email' and each one have diferent 'services' so you are able to get all the data by the id, images of the result:
You obviously only need to get attention in 'views.py' and 'serializers.py', I just created all this code to get in the expected result!
If you want your query to be case insensitive, you can try the following:
myModel.objects.filter(user_email__iexact=email_items)

How to display only specific data from the entire list storage on python django server (rest framework)

I have to display (as in topic title) only specific data from the list which is storage on server (PYTHON/DJANGO/REST_FRAMEWORK).
F.e. I want to choose data which has planted on the server with 'id=1', but I always get back all items. Can you explain how to correct this?
'urls' file (project level) :
# URL routes - known as endpoints API
urlpatterns = [
path('admin/', admin.site.urls),
path('devices/', include('efota.api.urls')),
]
'urls' file (subordinate folder) :
urlpatterns = [
url('', views.DeviceList.as_view()),
url('<int:pk>/', views.DeviceDetail.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
'views' file :
class DeviceList(generics.ListAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
class DeviceDetail(generics.RetrieveAPIView):
queryset = Device.objects.all()
serializer_class = DeviceSerializer
'serializers' file:
class DeviceSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
# the 'fields' controls which database attributes are available
class Meta:
model = Device
fields = (
'id',
'user',
'id_token',
'current_firmware',
'carrier_code',
'model_name',
'owner',
)
read_only_fields = ['id']
def get_url(self, obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
'models' file:
class Device(models.Model):
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey('auth.User', related_name='owner',
on_delete=models.CASCADE)
user = models.CharField(max_length=50, verbose_name='user')
id_token = models.CharField(max_length=1000,
verbose_name='id_token')
current_firmware = models.CharField(max_length=41,
verbose_name='current_firmware')
carrier_code = models.CharField(max_length=5,
verbose_name='carrier_code')
model_name = models.CharField(max_length=10,
verbose_name='model_name')
class Meta:
ordering = ('created', )
def __str__(self):
return self.model_name
'admin' file:
from django.contrib import admin
from .models import Device
# Register your models here.
admin.site.register(Device)
You are confusing old-style URL and new-style path syntax in your urls.py. The url() function takes a regex, and r'' matches every URL, while <int:pk>/ would only match that literal string. Use path instead.
urlpatterns = [
path('', views.DeviceList.as_view()),
path('<int:pk>/', views.DeviceDetail.as_view()),
]
To filter result returned by ListAPIView you can modify its get_queryset method as follows.
class DeviceDetail(generics.ListAPIView):
serializer_class = DeviceSerializer
def get_queryset(self):
return Device.objects.filter(id=self.kwargs['pk'])

Django Rest Framework: How to implement a nested logic?

Let's say I have three models as:
class User(AppModel):
name = models.CharField(max_length=255)
class Business(AppModel):
owner = models.ForeignKey("User", related_name="businesses", on_delete=models.CASCADE)
legal_name = models.CharField(max_length=255)
class Invoice(AppModel):
business = models.ForeignKey("Business", related_name="invoices", on_delete=models.CASCADE)
amount = models.integerField()
As you can see, a user can have multiple businesses and a business can have multiple invoices.
My serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields= ('name')
class BusinessSerializer(serializers.ModelSerializer):
owner = UserSerializer(many=False)
class Meta:
model = Business
fields= ('owner','legal_name')
class InvoiceSerializer(serializers.ModelSerializer):
business= BusinessSerializer(many=False)
class Meta:
model = Invoice
fields= ('business','amount')
views.py:
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
class BusinessViewSet(viewsets.ModelViewSet):
queryset = Business.objects.all()
serializer_class = BusinessSerializer
class InvoiceViewSet(viewsets.ModelViewSet):
queryset = Invoice.objects.all()
serializer_class = InvoiceSerializer
urls.py:
router = DefaultRouter()
router.register('user', UserViewSet, base_name='users')
router.register('business', BusinessViewSet, base_name='businesses')
router.register('invoice', InvoiceViewSet, base_name='invoices')
urlpatterns = router.urls
http://example.com/api/user returns all users. Not a problem.
But the functionality I'm looking for is:
http://example.com/api/business/ returns
[
{
"legal_name": "1business",
"owner": 1,
},
{
"legal_name": "2business",
"owner": 1,
},]
http://example.com/api/business/1/ returns
{
"legal_name": "1business",
"owner": 1,
}
The above is ok. But I also need:
http://example.com/api/business/1/invoices/ should return
[
{
"business": 1,
"amount": 100,
},
{
"business": 1,
"amount": 999,
},]
As well I should be able to create update delete those invoices there.
Any Help? I'm new to django rest framework. The above classes are just a sample. Ignore errors.
You should use django decorators which are #list_route and #detail_route for your viewset. But be careful with your DRF version. Because those decorators merged together as #action in DRF 3.8+. Here is the announcement.
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
class BusinessViewSet(viewsets.ModelViewSet):
queryset = Business.objects.all()
serializer_class = BusinessSerializer
#action(detail=True, methods=["GET"], url_path="invoices")
def invoices(self, request, pk=None):
"""
Your codes comes here to return related result.
pk variable contains the param value from url.
if you do not specify the url_path properties then action will accept the function's name as url path.
"""
entity = Invoice.objects.filter(business=pk)
serializer = self.get_serializer(entity, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
Then, you will be able to call this endpoints from;
http://example.com/api/business/{{PK}}/invoices/
http://example.com/api/business/1/invoices/
http://example.com/api/business/3/invoices/
http://example.com/api/business/23/invoices/
Here you can find more details about #actions from documentation.
PS: Don't forget to control empty entity results in your codes. You should return correct response with correct status codes.

Django nested serializer not serializing inner model

im trying to add a custom action to my ViewSet in Django2, using django-rest-framework. Problem is that my serializer is not serializing nested model and thus giving me error:
{
"labels": [
{
"non_field_errors": [
"Invalid data. Expected a dictionary, but got Label."
]
},
{
"non_field_errors": [
"Invalid data. Expected a dictionary, but got Label."
]
}
]
}
I have two models which have M:N relationship.
Label model:
class Label(models.Model):
name = models.CharField(max_length=30, help_text='Name of Label')
desc = models.CharField(max_length=200, help_text='Description of Label')
def __str__(self):
return self.name
LabelSet model:
class LabelSet(models.Model):
labels = models.ManyToManyField(Label, blank=True, help_text='ManyToMany field of corresponding labels')
name = models.CharField(max_length=30, help_text='Name of Label Set')
desc = models.CharField(max_length=200, help_text='Description of Label Set')
def __str__(self):
return self.name
Machine Model:
class Machine(models.Model):
name = models.CharField(max_length=30, help_text='Name of machine')
desc = models.CharField(max_length=200, help_text='Description of machine')
location = models.ForeignKey(Location, null=True, blank=True, on_delete=models.CASCADE, help_text='ID of machine location')
labelset = models.ForeignKey(LabelSet, null=True, blank=True, on_delete=models.DO_NOTHING, help_text='ID of set of labels relevant for this machine')
def __str__(self):
return self.name
Serializers:
class LabelSerializer(serializers.ModelSerializer):
class Meta:
model = Label
fields = '__all__'
class LabelSetSerializer(serializers.ModelSerializer):
qs = Label.objects.all().values()
labels = LabelSerializer(qs, many=True)
class Meta:
depth = 1
model = LabelSet
fields = ('name', 'desc', 'labels')
Custom action in viewsets.py (I want to retrieve available labels by machine, so path is /machines/{id}/labels
class MachineViewSet(viewsets.ModelViewSet):
'''
A viewset used for retrieving and editing Machine instances.
'''
#permission_classes = (DRYPermissions,)
serializer_class = MachineSerializer
queryset = Machine.objects.all()
# /api/v1/machines/{machine_id}/labels
#action(detail=True)
def labels(self, request, pk=None):
# Get labelset id
ls = Machine.objects.get(pk=pk).labelset
# Get LabelSet instance
serializer = LabelSetSerializer(data=model_to_dict(ls))
if serializer.is_valid():
return Response(serializer.data)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
The endpoint works fine, but when querying /machines/1/labels i got the response which is the first snippet:
"Invalid data. Expected a dictionary, but got Label."
Im literally out of ideas, tried even making dict from qs = Label.objects.all().values() in Serializer, no luck.
Thanks to #Jerin Peter George, output is now:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"name": "TestSet",
"desc": "asd",
"labels": [
{
"id": 1,
"name": "OK",
"desc": "desc"
},
{
"id": 2,
"name": "Broken",
"desc": "asd"
}
]
}
So /api/v1/machines/1/labels works, but suddenly /api/v1/machines/ does not. (502 Bad Gateway with error TypeError: 'LabelSet' object is not iterable)
APP level urls:
from django.conf.urls import url
from devices.viewsets import *
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'devices', DeviceViewSet, base_name='device')
router.register(r'projects', ProjectViewSet, base_name='project')
router.register(r'locations', LocationViewSet, base_name='location')
router.register(r'industries', IndustryViewSet, base_name='industry')
router.register(r'companies', CompanyViewSet, base_name='companies')
router.register(r'project_types', ProjectTypeViewSet, base_name='project_type')
router.register(r'device_types', DeviceTypeViewSet, base_name='device_type')
router.register(r'machines', MachineViewSet, base_name='machine')
router.register(r'records', RecordViewSet, base_name='record')
router.register(r'labels', LabelViewSet, base_name='label')
router.register(r'labelsets', LabelSetViewSet, base_name='label_set')
urlpatterns = router.urls
App level urls.py
from django.contrib import admin
from django.conf.urls import url
from django.urls import include, path
from rest_framework.documentation import include_docs_urls
from rest_framework_expiring_authtoken import views
from devices.views import AudioUploadView
API_PREFIX = 'api/v1/'
urlpatterns = [
url(API_PREFIX + 'docs/', include_docs_urls(title='API Docs')),
url(API_PREFIX + 'admin/', admin.site.urls),
url(API_PREFIX + 'api-token-auth/', views.obtain_expiring_auth_token),
path(API_PREFIX, include('devices.urls'))
]
EDIT: SOLVED
Apparently i added one more nested serializer to MachineSerializer
class MachineSerializer(serializers.ModelSerializer):
labelsets = LabelSetSerializer(many=True)
class Meta:
model = Machine
fields = '__all__'
So removing the line labelsets = LabelSetSerializer(many=True) did the trick.
And that is where the error came from, now is everything working as expected, thanks:)
Replace your labels() with below snippet,
#action(detail=True)
def labels(self, request, pk=None):
# Get labelset id
ls = Machine.objects.get(pk=pk).labelset
# Get LabelSet instance
serializer = LabelSetSerializer(ls)
return Response(serializer.data)

django-rest-framework many-to-many relations. Create if not exists

I try to send the following data to my django application:
{
"hashtags": ["hashtag"],
"title": "title",
"message": "message"
}
and i get this response:
{
"hashtags": [
{
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
}
]
}
I have the following view defined in views.py
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = IsAuthorizedOwnerOrReadOnly,
the models are defined like this:
class Post(models.Model):
ambassador = models.OneToOneField("User")
publish_date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
hashtags = models.ManyToManyField("PostHashtags", related_query_name="posts")
title = models.CharField(max_length=TEXT_SHORT, null=True, blank=True)
message = models.CharField(max_length=TEXT_MIDDLE, null=True, blank=True)
class PostHashtags(models.Model):
hashtag = models.CharField(max_length=TEXT_SHORT, null=False)
def __unicode__(self):
return self.hashtag
and i define the serializers like this:
class PostHashtagSerializer(serializers.ModelSerializer):
class Meta:
model = PostHashtags
fields = ("hashtag",)
class PostSerializer(serializers.ModelSerializer):
hashtags = PostHashtagSerializer(many=True)
class Meta:
model = Post
fields = ("id", "hashtags", "title", "message",)
read_only_fields = ("id", 'account_name',)
It seems like the hashtags are not created automatically using my current serialisation config. Is there a way to have my hashtags created if they do not yet exist, and if they do exist, have the Post use that same hashtag? In that case, how should my serialisers be defined?
EDIT 1:
After GwynBleidD's suggestions I now get the following error:
The `.create()` method does not support writable nestedfields by default.
Write an explicit `.create()` method for serializer PostSerializer , or set `read_only=True` on nested serializer fields.
Does anyone have a suggestion for such a create method?
EDIT 2: solved it using the following serialisers
class PostHashtagSerializer(serializers.ModelSerializer):
hashtag = serializers.CharField()
class Meta:
model = PostHashtags
fields = ("hashtag",)
class PostSerializer(serializers.ModelSerializer):
hashtags = PostHashtagSerializer(many=True,)
class Meta:
model = Post
fields = ("ambassador", "hashtags", "title", "message",)
def create(self, validated_data):
hashtags_data = validated_data.pop('hashtags')
post = Post.objects.create(**validated_data)
for hashtag in hashtags_data:
ht = PostHashtags.objects.create()
ht.hashtag = hashtag.get("hashtag")
ht.save()
post.hashtags.add(ht)
post.save()
return post
Hashtags are not string, but dict in that example. You have to submit:
{
"hashtags": [{"hashtag": "hashtag"}],
"title": "title",
"message": "message"
}

Categories