django rest framework POST request fails with nested serialization - django

My POST request to url http://127.0.0.1:8000/airlines/ fails
Here are models and corresponding serializers in my project. Initially I want to create Airlines information and then add flights later
Can someone please let me know where am I going wrong
models.py
class AirlineFirm(models.Model):
operator_name = models.CharField(max_length=30)
def __unicode__(self):
return "%s" % (self.operator_name)
class Flight(models.Model):
flight_num = models.CharField(max_length=7, primary_key=True)
src = models.CharField(max_length=20)
dest = models.CharField(max_length=20)
outbound_time = models.DateTimeField()
inbound_time = models.DateTimeField()
num_of_seats = models.IntegerField()
ticketPrice = models.FloatField()
delayed = models.BooleanField()
airlinefirm = models.ForeignKey(AirlineFirm)
serializers.py
class FlightSerializer(serializers.Serializer):
flight_num = serializers.CharField(read_only=True)
src = serializers.CharField()
dest = serializers.CharField()
outbound_time = serializers.DateTimeField()
inbound_time = serializers.DateTimeField()
num_of_seats = serializers.IntegerField()
ticketPrice = serializers.FloatField()
delayed = serializers.BooleanField()
airlinefirm = serializers.RelatedField(read_only='True')
#passengers = models.ManyToManyField(Passenger)
def create(self, validated_data):
return Flight.objects.create(**validated_data)
def update(self, instance, validated_data):
pass
class AirlineSerializer(serializers.ModelSerializer):
flights = FlightSerializer(many=True)
class Meta:
model = AirlineFirm
fields = ('operator_name','flights')
def create(self, validated_data):
flights_data = validated_data.pop('flights')
airlinefirm = AirlineFirm.objects.create(**validated_data)
for flight_data in flights_data:
Flight.objects.create(airlinefirm=airlinefirm, **flight_data)
return airlinefirm
views.py
#api_view(['GET','POST'])
def airline(request, format=None):
if request.method == 'GET':
airlines = AirlineFirm.objects.all()
serializer = AirlineSerializer(airlines, many=True)
return Response(serializer.data)
if request.method == 'POST':
#data = JSONParser().parse(request)
serializer = AirlineSerializer(data=request.data)
#import pdb;pdb.set_trace()
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
else:
import pdb;pdb.set_trace()
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
When I send a POST request http://127.0.0.1:8000/airlines/ to my airlines view class I get 404 response
http request
import json
import requests
payload = {'operator_name':'AmericanAirlines','flights':[]}
headers = {'Content-type':'application/json'}
r = requests.post('http://127.0.0.1:8000/',data=json.dumps(payload),headers=headers)
Here is the error message:
AttributeError: Got AttributeError when attempting to get a value for field flights on serializer AirlineSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the AirlineFirm instance.
Original exception text was: 'AirlineFirm' object has no attribute 'flights'.
[18/Feb/2016 13:43:58] "POST /airlines/ HTTP/1.1" 500 112039

You need to and an endpoint to the api in your urls.py if it's not there, then point to it in your request, like:
r = requests.post('http://127.0.0.1:8000/airlines',data=json.dumps(payload),headers=headers)

Related

'Response' object has no attribute 'user'

I am getting error AttributeError: 'Response' object has no attribute 'user' for the below code I have written
I am trying to get the user info from the context and create a notification model. I am getting the above error while returning the statement. I don't understand why I am getting this error
Model
class CourseNotification(models.Model):
uid = models.UUIDField(
primary_key=True,
default=uuid.uuid4,
editable=False,
unique=True)
course = models.ForeignKey('Course.Course', on_delete=models.SET_NULL, null=True)
user = models.ManyToManyField('Profile.myUser',null=True)
def get_user(self):
return [i for i in self.user.all()]
def __str__(self):
return self.course.course_title
View
class CourseNotificationView(ModelViewSet):
queryset = CourseNotification.objects.all()
serializer_class = CourseNotificationSerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
if self.request.user.email is not None:
profile = myUser.objects.get(email=self.request.user.email)
if profile is not None:
notification = CourseNotification.objects.filter(user=profile)
return notification
else:
return Response(data={"User": "Unauthorized User"}, status=HTTP_401_UNAUTHORIZED)
def retrieve(self, request, *args, **kwargs):
serializer = self.get_serializer(self.get_queryset(), many=True)
return Response(data=serializer.data)
Serializer
class CourseNotificationSerializer(serializers.ModelSerializer):
class Meta:
model = CourseNotification
fields = '__all__'
def create(self, validated_data):
users = self.context['request'].user
subject = validated_data['course']
if users is None and subject is None or subject == "":
raise serializers.ValidationError({"Invalid": "Subject could not be Invalid"})
checkNotification = self.checkNotification(users, subject)
if checkNotification is not None and checkNotification.status_code == 200:
return checkNotification
validate_subject = self.validateSubject(users, subject)
if validate_subject.status_code == 200:
return validate_subject
get_data = CourseNotification.objects.create(course=subject)
get_data.user.add(users)
get_data.save()
return Response(data=get_data, status=HTTP_201_CREATED, content_type="application/json")
#staticmethod
def checkNotification(users, subject):
get_data = CourseNotification.objects.filter(user=users, course=subject)
if get_data:
for data in get_data:
data.user.remove(users)
data.save()
return Response(data=get_data, status=HTTP_200_OK, content_type="application/json")
#staticmethod
def validateSubject(users, subject):
get_data = CourseNotification.objects.filter(course=subject).exclude(user=users)
if get_data:
subject = CourseNotification.objects.get(course=subject)
subject.user.add(users)
subject.save()
return Response(data=get_data, status=HTTP_200_OK, content_type="application/json")
I am trying to add data to the model through API. I am facing the problem

Django Rest Framework : Nested Serializer not working

I have almost tried everything but am not able to reach at the point
model.py file
class RecievingImages(models.Model):
"""Original and Masked Images"""
....
name = models.CharField(max_length = 100, unique = True, primary_key=True)
area = models.IntegerField()
number = models.IntegerField()
agency_name = models.CharField(max_length=100, default='general')
rawImage = models.ImageField(upload_to=imageNameForRawImage,)
...
class UpdationImages(models.Model):
""" Update Image wrt name"""
....
name = models.ForeignKey(RecievingImages, on_delete=models.PROTECT, related_name='updated')
updated_image = models.ImageField(upload_to=UpdatedImageFolder,)
updated_image_url = models.TextField(default='None')
....
serializer.py
class UpdatedImageSerializer(serializers.ModelSerializer):
class Meta:
model = UpdationImages
fields = ('name', 'updated_image', 'updated_image_url')
class RecievingImagesSerializer(serializers.ModelSerializer):
updated = UpdatedImageSerializer(many= True, read_only=True)
class Meta:
model = RecievingImages
fields = ('updated','name','area','number','agency_name', rawImage)
I have used related_name in the model and also following the documentation along with that with many = True
But still in serializer.data updated does not show
views.py
class MappingSinglePhoto(APIView):
""" Mapping Single Image """
def post(self, request):
try:
data = request.data
# import pdb; pdb.set_trace()
name_of_image = data['name']
mapped_images_qs = UpdationImages.objects.select_related('name').filter(name = name_of_image)
for image in mapped_images_qs:
serializer = RecievingImagesSerializer(instance = image)
pdb.set_trace()
serializer.data
# return Response(serializer.data)
except Exception as e:
print(e)
NOTE
if I use depth=1 then it's working fine, but I am not looking for all the fields that depth displays.
Thanks & Regards
Thanks to #ArakkalAbu
There was a problem in query views.py
class MappingSinglePhoto(APIView):
""" Mapping Single Image """
def post(self, request):
try:
data = request.data
# import pdb; pdb.set_trace()
name_of_image = data['name']
mapped_images_qs = RecievingImages.objects.filter(name = name_of_image)
for image in mapped_images_qs:
serializer = RecievingImagesSerializer(instance = image)
pdb.set_trace()
serializer.data
# return Response(serializer.data)
except Exception as e:
print(e)

Object of type Logo is not JSON serializable

I'm trying to build django rest api which will get images from front part and also serve it via REST APi, the problem is that eventho I'm able to post data to django via PostMan, still I get error Object of type 'Logo' is not JSON serializable. Data is already in database but I would like to fix this issue but dont know where to start, tried to change Response to JsonResponse but it brought no effect
Model
class Logo(models.Model):
name = models.CharField(max_length=60)
preview = models.ImageField()
thumb = models.ImageField()
thumbL = models.ImageField()
dataL = models.TextField(blank=False)
thumbS = models.ImageField()
dataS = models.TextField()
posL = models.CharField(max_length=60)
posS = models.CharField(max_length=60)
def __str__(self):
return str(self.name)
Serialization:
class LogoSerializer(serializers.ModelSerializer):
posL = serializers.CharField(required=False)
posS = serializers.CharField(required=False)
dataL = serializers.CharField(required=False)
dataS = serializers.CharField(required=False)
class Meta:
model = Logo
fields = ('__all__')
def create(self, validated_data):
return Logo.objects.create(**validated_data)
and View:
class LogoViewSet(viewsets.ViewSet):
parser_classes = (MultiPartParser, FormParser)
def list(self, request, *args, **kwargs):
queryset = Logo.objects.all()
serializer = LogoSerializer(queryset, many=True)
return Response(serializer.data)
def post(self, request, *args, **kwargs):
serializer = LogoSerializer(data=request.data)
if serializer.is_valid():
serializer.validated_data['posL'] = positionL
serializer.validated_data['posS'] = positionS
bg = serializer.save()
return Response(bg, status=status.HTTP_201_CREATED)
else:
bg = serializer.errors
return Response(bg, status=status.HTTP_400_BAD_REQUEST)
According to the docs you should use serializer.data instead of bg in your response.
So return Response(serializer.data, status=status.HTTP_201_CREATED)
The reason for this is because you are passing a Django model instance, which doesn't really have the fields in raw JSON serializable format, but rather a more complex object with field ins the format of CharFields, ImageFields, as well as a meta property that defines some of the model's properties like database table, etc.

DRF check if an object already exist in DB when receiving a request

I have products defined by their Name and Location. Each product has a unique pair of Name/Location.
I'm writing a view to be able to create a product and I would like to first check if it exists in DB.
If yes then keep the ID somewhere to return it to my front app.
If no then create it and get the ID also.
From my research, overriding the perform_create method should be the solution but I can't figure out how to.
Any help would be appreciated.
urls.py
from django.conf.urls import url
from main.views import product_view
urlpatterns = [
url(r'^products/$', product_view.ProductCreate.as_view()),
url(r'^products/(?P<pk>[0-9]+)/$', product_view.ProductDetail.as_view()),
]
product_view.py
from rest_framework import generics
from rest_framework import permissions
from main.models import Product
from main.serializers import ProductSerializer
class ProductCreate(generics.CreateAPIView):
"""
Create a new product.
"""
permission_classes = (permissions.IsAuthenticated,)
serializer_class = ProductSerializer
queryset = Product.objects.all()
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = create_product(product_name, product_location)
else:
product = product_list[0]
serializer = ProductSerializer(product)
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class ProductDetail(generics.RetrieveUpdateAPIView):
"""
Retrieve, update or delete a product.
"""
permission_classes = (permissions.IsAuthenticated,)
serializer_class = ProductSerializer
queryset = Product.objects.all()
serializer.py
from django.contrib.auth.models import User
from rest_framework import serializers
from main.models import Product
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ('id',
'product_name',
'product_shop',
'product_url',
'product_img',
'product_location')
EDIT
product model :
class Product(models.Model):
product_name = models.CharField(max_length=200, blank=True)
product_shop = models.CharField(max_length=200, blank=True)
product_url = models.CharField(max_length=400, blank=False)
product_img = models.CharField(max_length=400, blank=True)
product_location = models.CharField(max_length=200, blank=False)
product_creation_date = models.DateTimeField(default=datetime.now, blank=True)
productgroup = models.ForeignKey(ProductGroup, blank=True, null=True, on_delete=models.CASCADE)
def __str__(self):
return '#' + str(self.pk) + ' ' + self.product_name + ' (' + self.product_shop + ')'
A product is automatically created depending on its name and location. A specific function is handling the creation and fulfill the data.
The result I get in my front app is missing some data using this code.
Here is an example using httpie :
Request : http POST http://127.0.0.1:8000/products/ product_name="Product test" product_location="Loc1" product_img="www.myimg.com"
Result :
HTTP/1.0 201 Created
Allow: POST, OPTIONS
Content-Length: 247
Content-Type: application/json
Date: Thu, 08 Mar 2018 13:58:18 GMT
Server: WSGIServer/0.2 CPython/3.5.3
Vary: Accept
X-Frame-Options: SAMEORIGIN
{
"product_location": "Loc1",
"product_name": "Product test",
"product_img": "www.myimg.com"
}
In DB the product exists and has values for product_shop and product_url, and of course has an ID.
EDIT 2
I did some more test and logged as many things as possible.
Here is my perform_create function and the results from the logger :
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = create_product(product_name, product_location)
else:
product = product_list[0]
logger.info('product id : ' + str(product.id)) # Generated automatically
logger.info('product name : ' + product.product_name) # From the request
logger.info('product url : ' + product.product_url) # Generated by my create_product function
serializer = ProductSerializer(product)
logger.info('serializer.data['id'] : ' + str(serializer.data['id']))
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'}, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Here are the results and they are good :
product id : 3713
product name : Product 1
product url : /products/Product1/...
serializer.data['id'] : 3713
In the result of the request I now have only the product_url and the product_location....
Is there any other way to achieve what I want?
You need to check is serializer is valid at first. Then you can call serializer.save() to create new object, or just create new serializer object and pass to it already existing product:
def perform_create(self, serializer):
if serializer.is_valid():
product_name = serializer.validated_data['product_name']
product_location = serializer.validated_data['product_location']
if product_name != '':
product_list = Product.objects.filter(
product_name=product_name, product_location=product_location)
if not product_list:
product = serializer.save()
else:
product = product_list[0]
serializer = ProductSerializer(product)
return Response(serializer.data)
else:
return Response(data={'message': 'Empty product_name'},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
If serializer's data is not valid you have to return serializer.errors.
Ok so I solved my problem NOT using perform_create. I came back to an easy APIView and defined the POST method as wanted. Works perfectly now.
class ProductCreate(APIView):
"""
Create a new product.
"""
permission_classes = (permissions.IsAuthenticated,)
def post(cls, request, format=None):
serializer = ProductSerializer(data=request.data)
if serializer.is_valid():
............

How do I add a field to a django-rest-framework serializer that isn't on my model?

I have a serializer that works fine for the GET, POST, DELETE actions. It exposes the model fields that I want. However for the PUT action, the user will send back values that aren't built into my models and the server will deal with how to perform the update on the model. I can send the data back using Postman or Curl and it works but the browseable API still looks like this:
For the PUT method I want "is_winner", "num_hands_won", and "score" to show up instead of the actual model fields. How do I do this? (Let me know in the comments if you need more info)
StatisticsSerializer:
class StatisticsSerializer(serializers.ModelSerializer):
# pk = serializers.IntegerField(required=False)
class Meta:
model = Statistics
fields = [
'url',
'games_won',
'hands_won',
'games_played',
'high_score',
'low_score',
]
Statistics Model:
class Statistics(models.Model):
# Define model fields:
user = models.OneToOneField(User, primary_key=True)
games_won = models.IntegerField(null=True, blank=True)
hands_won = models.IntegerField(null=True, blank=True)
games_played = models.IntegerField(null=True, blank=True)
high_score = models.IntegerField(null=True, blank=True)
low_score = models.IntegerField(null=True, blank=True)
def __str__(self):
return str(self.pk)
def increment_games_won(self, is_winner):
if is_winner is True:
self.games_won = self.games_won + 1
return self.games_won
def add_to_hands_won(self, num_hands_won):
if num_hands_won > 0 and num_hands_won < 8:
self.hands_won = self.hands_won + num_hands_won
return self.hands_won
def increment_games_played(self):
self.games_played = self.games_played + 1
return self.games_played
def new_high_score(self, score):
if score > self.high_score:
self.high_score = score
return self.high_score
def new_low_score(self, score):
if score < self.low_score:
self.low_score = score
return self.low_score
Statistics ViewSet:
class StatisticsViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Statistics.objects.all()
serializer_class = StatisticsSerializer
filter_class = StatisticsFilter
search_fields = ('pk', 'user')
ordering_fields = ('games_won', 'hands_won', 'games_played', 'high_score', 'low_score')
def update(self, request, pk=None):
stats = self.get_object()
stats.increment_games_won(request.data['is_winner'])
stats.add_to_hands_won(request.data['num_hands_won'])
stats.increment_games_played()
stats.new_low_score(request.data['score'])
stats.new_high_score(request.data['score'])
stats.save()
serialized_stats = StatisticsSerializer(stats, context={'request': request}).data
return Response(serialized_stats)
You could probably use another Serializer and use it for you PUT API
StatisticsUpdateSerializer:
class StatisticsUpdateSerializer:
is_winner = ...
num_hands_won = ...
score = ...
And use this serializer in the PUT API or create a new route as shown in the example mentioned in the DRF documentation here
#detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
// Use your serializer below
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)