"This field is required" POSTing to Django rest framework - python

I am trying to POST to my django rest framework serializer using httpie and the following syntax:
http POST http://127.0.0.1:8000/traffic/geofences/
{
"id":13,
"radius":40,
"lat":0,
"long":0,
"name":"B"
}
The following error is returned:
{
"id":["This field is required."],
"name":["This field is required."],
"lat":["This field is required."],
"long":["This field is required."],
"radius":["This field is required."]
}
I don't understand why these fields are not accepted. What should the format look like? The server will be sent data from a mobile device and inserting it into the database via django rest framework.
Here is my Serializer and Model and View
Serializer:
class GeofenceSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(min_value=0)
name = serializers.CharField()
lat = serializers.DecimalField(max_digits=10, decimal_places=6, coerce_to_string=False)
long = serializers.DecimalField(max_digits=10, decimal_places=6, coerce_to_string=False)
radius = serializers.IntegerField(min_value=0)
class Meta:
model = Geofence
fields = ('id', 'name','lat', 'long', 'radius')
def create(self, valid_data):
return Geofence.objects.create(**valid_data)
Model:
class Geofence(models.Model):
id = models.IntegerField(default=0, primary_key=True)
name = models.CharField(max_length=200, default="Geofence", blank=False)
lat = models.DecimalField(default=0, decimal_places=6, max_digits=10, blank=False)
long = models.DecimalField(default=0, decimal_places=6, max_digits=10, blank=False)
radius = models.IntegerField(default=10, blank=False)
def __str__(self):
return "Geofence: " + str(self.name);
View:
class GeofencesView(APIView):
model = Geofence
serializer_class = GeofenceSerializer
def getId(self):
return uuid.uuid4().int & (1<<64)-1
def get(self, request):
fences = Geofence.objects.all()
serializer = GeofenceSerializer(fences, many=True)
return Response(serializer.data)
def post(self, request, format=None):
serializer = GeofenceSerializer(data= request.data)
serializer.id = -1
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors)
I have read the django rest framework documentation and the django docs. The get request works fine. If nothing else, I would appreciate someone pointing me to where this is discussed in either documentation.

I don't think you're using the correct json format for httpie.
Try this instead:
http POST http://127.0.0.1:8000/traffic/geofences/ \
id:=13 radius:=40 lat:=0 long:=0 name="B"

Related

How to serialize two nested django tables?

iI've updated the post and pasted the complete code. can any fix the code for me. actually i want to POST data with album name and multiple images adn serialize the both and at POST request I want the whole submitted data. means.
{
name : My favourite songs list
media: [
{
media : filename1,
media : filename2
}
]
}
#views.py
#api_view(['POST'])
def addAlbum(request):
if request.method == 'POST':
serializer = AlbumSerializer(data=request.data)
albums = request.data.pop('media')
if albums:
name = Album.objects.create(name=request.data['name'])
if serializer.is_valid():
for file in albums:
AlbumImages.objects.create(media = file, album = name)
serializer.save()
return Response(serializer.data, status.HTTP_201_CREATED)
return Response(serializer.errors, status.HTTP_400_BAD_REQUEST)
return Response("Please upload the files", status.HTTP_400_BAD_REQUEST)
return Response("Not found", status.HTTP_400_BAD_REQUEST)
#serializers.py
class AlbumImagesSerializer(serializers.ModelSerializer):
class Meta:
model = AlbumImages
fields = "__all__"
class AlbumSerializer(serializers.ModelSerializer):
albumimages = AlbumImagesSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = "__all__"
#models.py
class Album(models.Model):
name = models.CharField(max_length=30, null=False, blank=False)
def __str__(self):
return self.name
class AlbumImages(models.Model):
album = models.ForeignKey(Album, related_name='albumimages', on_delete=models.CASCADE)
media = models.ImageField(upload_to='album/images')
def __str__(self) -> str:
return f'Files of ' + self.album.name
Album is being generated, files are being associated with album table, but the question is how i'll serialize both tables to show response on post request. waiting for help.
I adjusted your code a little bit but it wasn´t tested.
If you don´t want to adjusted the album-ForeignKey field you must change the behavoiur of saving instead of using .objects.create(). It should work.
You wrote a lot of validations. Your serializer can do that. You can use extra_kwargs = {'media': {'required': True} at your AlbumImage-serialzer for example. If you need more explenation don´t hasitate to ask.
album = Album(**validated_data)`
album.albumimages = albumimages_set
album.save()
#views.py
class AlbumView(APIView):
def post(self, request, *args, **kwargs):
resp = Response()
serializer = AlbumSerializer(data=request.data)
if serializer.is_valid():
resp.data = serializer.save()
else:
resp.status_code = HTTP_400_BAD_REQUEST
resp.data = serializer.errors
return resp
#serializers.py
class AlbumImagesSerializer(serializers.ModelSerializer):
class Meta:
model = AlbumImages
fields = "__all__"
class AlbumSerializer(serializers.ModelSerializer):
albumimages = AlbumImagesSerializer(many=True, read_only=True)
class Meta:
model = Album
fields = "__all__"
def create(self, validated_data):
albumimages = validated_data.pop('albumimages')
albumimages_set = [AlbumImages.objects.create(**element) for element in albumimages ]
album = Album.objects.create(**validated_data)
album.albumimages.set(albumimages_set)
album.save()
return album
#models.py
class Album(models.Model):
name = models.CharField(max_length=30, null=False, blank=False)
def __str__(self):
return self.name
class AlbumImages(models.Model):
album = models.ForeignKey(Album, related_name='albumimages', blank=True, null=True, on_delete=models.CASCADE)
media = models.ImageField(upload_to='album/images')
def __str__(self) -> str:
return f'Files of ' + self.album.name

Better way to save data in database using django serializer

Hello I am working on a project in which I'm making serializers
and I send post requests using postman. This is working fine but this is a very small part of the application and the code will grow to be very large. If there is a better way of doing this in which I write smaller code then I would like to employ that.
Right now the way I'm saving the information is as follows
models.py
class Services(models.Model):
business = models.ForeignKey(Business, on_delete=models.CASCADE)
service = models.CharField(max_length=30, blank=True)
serializers.py
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Services
fields = ('id', 'business', 'service')
views.py
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = BusinessSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
urlpatterns = [
path('service/', ServiceAPIView.as_view()),
]
Edited*
Here is the business model
class Business(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
business_name = models.CharField(max_length=50, blank=True)
invoices_terms = models.TextField(max_length=100, blank=True)
desc = models.TextField(max_length=1000, blank=True)
website = models.URLField(max_length=200, blank=True)
tax_percentage = models.DecimalField(blank=True, max_digits=5, decimal_places=3)
labour_rate_per_hour = models.DecimalField(blank=True, max_digits=5, decimal_places=2)
tax_hst_number = models.IntegerField(blank=True)
Well the code looks ok and it's also ok to have deep modules.
A little update could be the following:
class ServiceAPIView(APIView):
permission_classes = [IsAuthenticated,]
serializer_class = ServiceSerializer
def post(self, request):
data = request.data
business = Business.objects.get(user=request.user)
data['business'] = business.pk
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_200_OK)
In this way you can write only one Response line for each view request method and avoid to use if/else statements

DRF: how to change the value of the model fields before saving to the database

If I need to change some field values before saving to the database as I think models method clear() is suitable. But I can't call him despite all my efforts.
For example fields email I need set to lowercase and fields nda I need set as null
models.py
class Vendors(models.Model):
nda = models.DateField(blank=True, null=True)
parent = models.OneToOneField('Vendors', models.DO_NOTHING, blank=True, null=True)
def clean(self):
if self.nda == "":
self.nda = None
class VendorContacts(models.Model):
....
vendor = models.ForeignKey('Vendors', related_name='contacts', on_delete=models.CASCADE)
email = models.CharField(max_length=80, blank=True, null=True, unique=True)
def clean(self):
if self.email:
self.email = self.email.lower()
serializer.py
class VendorContactSerializer(serializers.ModelSerializer):
class Meta:
model = VendorContacts
fields = (
...
'email',)
class VendorsSerializer(serializers.ModelSerializer):
contacts = VendorContactSerializer(many=True)
class Meta:
model = Vendors
fields = (...
'nda',
'contacts',
)
def create(self, validated_data):
contact_data = validated_data.pop('contacts')
vendor = Vendors.objects.create(**validated_data)
for data in contact_data:
VendorContacts.objects.create(vendor=vendor, **data)
return vendor
views.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
permission_classes = (permissions.AllowAny,)
serializer_class = VendorsSerializer
def post(self, request, *args, **kwargs):
serializer = VendorsSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
else:
return Response(request.data, status=status.HTTP_200_OK)
As I learned from the documentation
Django Rest Framework serializers do not call the Model.clean when
validating model serializers
In dealing with this problem, I found two ways to solve it.
1. using the custom method at serializer. For my case, it looks like
class VendorsSerializer(serializers.ModelSerializer):
contacts = VendorContactSerializer(many=True)
class Meta:
model = Vendors
fields = (...
'nda',
'contacts',
)
def create(self, validated_data):
contact_data = validated_data.pop('contacts')
vendor = Vendors.objects.create(**validated_data)
for data in contact_data:
VendorContacts.objects.create(vendor=vendor, **data)
return vendor
def validate(self, attrs):
instance = Vendors(**attrs)
instance.clean()
return attrs
Using full_clean() method. For me, it looks like
class VendorsSerializer(serializers.ModelSerializer):
contacts = VendorContactSerializer(many=True)
class Meta:
model = Vendors
fields = (...
'nda',
'contacts',
)
def create(self, validated_data):
contact_data = validated_data.pop('contacts')
vendor = Vendors(**validated_data)
vendor.full_clean()
vendor.save()
for data in contact_data:
VendorContacts.objects.create(vendor=vendor, **data)
return vendor
But in both cases, the clean() method is not called. I really don't understand what I'm doing wrong.
In my case I had the same problem but with validation feature
I used the way below and it works for me (not excludes the way found above):
class CustomViewClass(APIView):
def post(self, request, format=None):
prepared_data_variable = 'some data in needed format'
serializer = CustomSerializer(data=request.data)
if serializer.is_valid(self):
serializer.validated_data['field_name'] = prepared_data_variable
serializer.save()
return Response(data=serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
This string is key for my solution serializer.validated_data['field_name'] = prepared_data_variable
For DRF you can change your serializer before save as below...
First of all, you should check that serializer is valid or not, and if it is valid then change the required object of the serializer and then save that serializer.
if serializer.is_valid():
serializer.object.user_id = 15 # For example
serializer.save()
UPD!
views.py
class VendorsCreateView(APIView):
"""Create new vendor instances from form"""
permission_classes = (permissions.AllowAny,)
serializer_class = VendorsSerializer
def post(self, request, *args, **kwargs):
data = request.data
if data['nda'] == '':
data['nda'] = None
for contact in data['contacts']:
if contact['email']:
print(contact['email'])
contact['email'] = contact['email'].lower()
serializer = VendorsSerializer(data=request.data)
try:
serializer.is_valid(raise_exception=True)
serializer.save()
except ValidationError:
return Response({"errors": (serializer.errors,)},
status=status.HTTP_400_BAD_REQUEST)
To answer your question: just override save() method for your models as written in docs. There you can assign any values to your model instance directly before saving it in database.
Also, you should probably use models.EmailField for your email fields which will get rid of your lower() check.

JSONField serializes as json for POST, but string for GET

There is likely a very simple problem with my code, but I've been slamming my head against this problem for a couple days and can't make any headway.
Important Packages:
Django==1.11.3
django-cors-headers==2.1.0
djangorestframework==3.7.0
drf-nested-routers==0.90.0
psycopg2==2.7.3
pycparser==2.18
Here is what is happening:
I create a model via an AJAX call
My server correctly serializes the brainstorm_data field as a json object.
Now I navigate my user to the next page and fetch the current model
For some reason, brainstorm_data is now be returned as a string. Anytime I call a GET request on this resource I always get a string representation of the JSON object.
Here is the code associated:
models.py
from django.contrib.postgres.fields import JSONField
class Adventure(TimeStampedModel,
models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User)
world = models.ForeignKey(World)
theme = models.ForeignKey(Theme, default=1)
brainstorm_data = JSONField()
image_src = models.CharField(max_length=400, null=True, blank=True)
sentence_summary = models.TextField(null=True, blank=True)
paragraph_summary = models.TextField(null=True, blank=True)
page_summary = models.TextField(null=True, blank=True)
outline_complete = models.BooleanField(default=False)
brainstorm_complete = models.BooleanField(default=False)
private = models.BooleanField(default=False)
def __str__(self):
return self.name
views.py
class MyAdventuresViewSet(viewsets.ModelViewSet):
queryset = Adventure.objects.all()
serializer_class = AdventureSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
return Adventure.objects.filter(user=self.request.user)
def create(self, request, *args, **kwargs):
user = self.request.user
world = World.objects.filter(user=user).first()
if not world:
world = World.objects.create(name='My World', user=user,
description="This is a default world we created for your adventures",
image_src=static('worlds/images/world_placeholder.png'))
data = request.data.copy()
data['user'] = user.pk
data['world'] = world.pk
data['theme'] = 1 # default theme
data['brainstorm_data'] = default_brainstorm
serializer = self.get_serializer(data=data)
serializer.is_valid(raise_exception=True)
adventure = serializer.save()
Storyboard.objects.create(adventure=adventure, raw=default_storyboard['raw'], html=default_storyboard['html'])
return JsonResponse(serializer.data)
#detail_route(methods=['post'])
def complete_outline(self, request, pk):
adventure = Adventure.objects.get(pk=self.kwargs['pk'])
complete_adventure_outline(adventure)
serializer = self.get_serializer(data=adventure)
serializer.is_valid(raise_exception=True)
return JsonResponse(serializer.data, status=200)
#detail_route(methods=['post'])
def genres(self, request, pk):
genre_names = request.data
genre_models = Genre.objects.filter(name__in=genre_names)
adventure = self.get_object()
adventure.genre_set.set(genre_models)
adventure.save()
serializer = AdventureSerializer(adventure)
return JsonResponse(serializer.data)
serializers.py
class AdventureSerializer(serializers.ModelSerializer):
genre_set = serializers.StringRelatedField(many=True, read_only=True)
character_set = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
location_set = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
storyboard = serializers.PrimaryKeyRelatedField(read_only=True)
theme = serializers.PrimaryKeyRelatedField(queryset=Theme.objects.all())
class Meta:
model = Adventure
fields = '__all__'
mixins
# this is a dictionary used to default brainstorm data each time an adventure is created
default_brainstorm = {
"nodes": [...],
"edges": [...]
}
You can override the to_internal_value and to_representation in a new serializer field to handle the return data for JSON field.
class JSONSerializerField(serializers.Field):
"""Serializer for JSONField -- required to make field writable"""
def to_internal_value(self, data):
return data
def to_representation(self, value):
return value
And in turn, you would use this Field in a serializer:
class SomeSerializer(serializers.ModelSerializer):
json_field = JSONSerializerField()
class Meta:
model = SomeModelClass
fields = ('json_field', )
This should solve your problem :)
When I originally created the columns I did it with a different json field package. The base DB columns was actually text instead of json or jsonb. Creating new columns (django json fields), migrating the data, and then shifting the data back got my database back in a consistent order.

Django rest framework if serializer returns empty

I have am implementing a follower and followers system in my drf api.
My relationship model and serializer:
models.py
class Relationship(models.Model):
user = models.ForeignKey(User, related_name="primary_user", null=True, blank=True)
related_user = models.ForeignKey(User, related_name="related_user", null=True, blank=True)
incoming_status = models.CharField(max_length=40, choices=RELATIONSHIP_CHOICE, default=RELATIONSHIP_CHOICE[0])
def __str__(self):
return self.user.username + " " + self.incoming_status + " " + self.related_user.username
serializers.py
class RelationshipSerializer(serializers.ModelSerializer):
outgoing_status = serializers.SerializerMethodField()
class Meta:
model = models.Relationship
fields = (
'user',
'related_user',
'incoming_status',
'outgoing_status',
)
def get_outgoing_status(self, obj):
related_user = obj.related_user
user = obj.user
try:
query = models.Relationship.objects.get(user=related_user, related_user=user)
user_id = query.incoming_status
except models.Relationship.DoesNotExist:
user_id = "none"
return user_id
views.py
class UserViewSet(viewsets.ModelViewSet):
queryset = models.User.objects.all()
serializer_class = serializers.UserSerializer
#detail_route(methods=['get'], url_path='relationship/(?P<related_pk>\d+)')
def relationship(self, request, pk, related_pk=None):
user = self.get_object()
query = models.Relationship.objects.filter(user=user)&\
models.Relationship.objects.filter(related_user__pk=related_pk)
serializer = serializers.RelationshipSerializer(query, many=True)
return Response(serializer.data)
Incoming status is your relationship to the user and outgoing status is the users relationship to you to so for example this can return
[
{
"user": 2,
"related_user": 1,
"incoming_status": "follows",
"outgoing_status": "none"
}
]
This means you follow the user and the user doesn't follow you back
When you follow a user my code works fine because it returns "incoming_status": "follows" and it then checks for the outgoing status in the serializers. However when the:
query = models.Relationship.objects.filter(user=user) &
models.Relationship.objects.filter(related_user__pk=related_pk)
query in views.py returns null the meaning that the incoming status is none the serializer outputs [] because the query is empty. How do I make incoming_status = "none" if the query is empty?
Thanks in advance

Categories

Resources