I'm trying to use Django Rest Framework for updating my database using the HTTP PUT, but when on my client I get the error Exception Value: update() missing 1 required positional argument: 'validated_data' and in the python code I get an error that says Validated_data unfilled.
Here is my model code:
nombre = models.CharField(max_length=200)
calle_numero = models.CharField(max_length=200)
zona_residencial = models.ForeignKey(Zona, on_delete=models.CASCADE)
telefono = models.CharField(max_length=20)
numero_habitantes = models.IntegerField()
tipo_residente = models.CharField(max_length=100,
choices=[(tag.value, tag.value) for tag in TipoHabitanteEnum])
codigo_acceso = models.CharField(max_length=6, default="000000")
status_activacion = models.BooleanField(default=False)
class Meta:
verbose_name = 'Residente'
verbose_name_plural = 'Residentes'
def __str__(self):
return self.nombre
here is my serializer code:
class Meta:
model = Residente
fields = '__all__'
And my view (PUT method where the error is) code:
"""
Modifica un residente
"""
try:
id_residente = self.queryset.get(pk=kwargs["pk"])
serializer = ResidenteSerializer
update_residente = serializer.update(id_residente, request.data)
return Response(ResidenteSerializer(update_residente).data)
except Residente.DoesNotExist:
return Response(
data={
"message": "El residente con id {} no existe".format(kwargs["pk"])
},
status=status.HTTP_404_NOT_FOUND
)
in the update_residente = serializer.update(id_residente, request.data) is the validated_data error is and that's why I can't update my database but I don't know how to fix it.
Hope you can help me.
You are not creating the serializer object.
change the code to this:
"""
Modifica un residente
"""
try:
id_residente = self.queryset.get(pk=kwargs["pk"])
serializer = ResidenteSerializer(id_residente, data=request.data)
if serializer.is_valid(raise_exception=False):
update_residente = serializer.save()
return Response(ResidenteSerializer(update_residente).data)
return Response(serializer.errors, status=400)
except Residente.DoesNotExist:
return Response(
data={
"message": "El residente con id {} no existe".format(kwargs["pk"])
},
status=status.HTTP_404_NOT_FOUND
)
Suggestion for simpler code:
id_residente = get_object_or_404(self.queryset, pk=kwargs["pk"])
serializer = ResidenteSerializer(id_residente, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
This code is same as the above but simpler.
use from rest_framework.generics import get_object_or_404 to import the function.
Related
I am using django-yasg to create an api documentation. But no parameters are showing in the documentation to create post request. Following are my codes:
After that in swagger api, no parameters are showing for post request to create the event
model.py
class Events(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
category = models.ForeignKey(EventCategory, on_delete=models.CASCADE)
name = models.CharField(max_length=250, null=True)
posted_at = models.DateTimeField(null=True, auto_now=True)
location = models.CharField(max_length=100, null=True)
banner = models.ImageField(default='avatar.jpg', upload_to='Banner_Images')
start_date = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
end_date = models.DateField(
auto_now=False, auto_now_add=False, blank=True, null=True)
description = models.TextField(max_length=2000, null=True)
completed = models.BooleanField(default=False)
def __str__(self):
return f'{self.name}'
class Meta:
verbose_name_plural = "Events"
serializers.py
class EventSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True, many=False)
category = EventCategorySerializer(read_only=True, many=False)
class Meta:
model = Events
fields = '__all__'
views.py
#api_view(['POST'])
#permission_classes([IsAuthenticated])
#user_is_organization
#swagger_auto_schema(
request_body=EventSerializer
)
def registerEvent(request):
"""
To Register an events, you must be an organization
"""
data = request.data
print("==================")
print(data)
print("==================")
try:
Event = Events.objects.create(
user = request.user,
category=EventCategory.objects.get(category=data['category']),
name=data['name'],
location=data['location'],
start_date=data['start_date'],
end_date=data['end_date'],
description=data['description'],
completed=data['completed'],
)
serializer = EventSerializer(Event, many=False)
Event = Events.objects.get(id=serializer.data['id'])
Event.banner = request.FILES.get('banner')
Event.save()
serializer = EventSerializer(Event, many=False)
return Response(serializer.data)
except:
message = {'detail': 'Event with this content already exists'}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
I got it running with following changes in views.py
#swagger_auto_schema(
methods=['post'],
request_body=openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['category','name', 'location', 'start_date', 'end_date', 'description', 'completed', 'banner'],
properties={
'category':openapi.Schema(type=openapi.TYPE_STRING),
'name':openapi.Schema(type=openapi.TYPE_STRING),
'location':openapi.Schema(type=openapi.TYPE_STRING),
'start_date':openapi.Schema(type=openapi.TYPE_STRING, default="yyyy-mm-dd"),
'end_date':openapi.Schema(type=openapi.TYPE_STRING, default='yyyy-mm-dd'),
'description':openapi.Schema(type=openapi.TYPE_STRING),
'completed':openapi.Schema(type=openapi.TYPE_BOOLEAN, default=False),
'banner': openapi.Schema(type=openapi.TYPE_FILE),
},
),
operation_description='Create an events'
)
#api_view(['POST'])
#permission_classes([IsAuthenticated])
#user_is_organization
def registerEvent(request):
"""
To Register an events, you must be an organization
"""
data = request.data
print("==================")
print(data)
print(type(data))
category=EventCategory.objects.get(category=data['category']),
print(category)
print(type(data["start_date"]))
print("==================")
try:
Event = Events.objects.create(
user = request.user,
category=EventCategory.objects.get(category=data['category']),
name=data['name'],
location=data['location'],
start_date=data['start_date'],
end_date=data['end_date'],
description=data['description'],
completed=data['completed'],
)
print("****************************")
serializer = EventSerializer(Event, many=False)
Event = Events.objects.get(id=serializer.data['id'])
Event.banner = request.FILES.get('banner')
Event.save()
serializer = EventSerializer(Event, many=False)
return Response(serializer.data)
except ValidationError as e:
return Response({"ValidationError" : e}, status = status.HTTP_400_BAD_REQUEST)
except Exception as e:
message = {'error': e}
return Response(message, status=status.HTTP_400_BAD_REQUEST)
can you modify your swagger_auto_schema decorator like this?
#swagger_auto_schema(
methods=['post',],
request_body=EventSerializer )
All you need to do is place the #swagger_auto_schema on the top. To help it display the parameters create a serializer for it. This way, you only have to change the serializer in the future keeping everything in one place.
API view
#swagger_auto_schema(request_body=serializers.RequestSerializer, method='post')
#api_view(http_method_names=['POST'])
def special_get(request):
data = JSONParser().parse(request)
unique_id = data.get("unique_id", "")
...
Serializer.py
class RequestSerializer(serializers.Serializer):
unique_id = serializers.CharField(max_length=50, allow_null=False, allow_blank=True)
sentence_list = serializers.ListField(
child=serializers.CharField(allow_blank=False, trim_whitespace=True)
)
I am new to DRF.
While creating the twitter app I faced a problem with serializers. Since the user is required - I need somehow to pass the actual user to the TweetSerializer class. I have tried different methods that did not work.
This is giving me an error
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
error image
error image continued
also i have tried to se the user by passing it tot he serializer constructor
serializer = TweetSerializer(owner=request.user)
also did not work
class TweetSerializer(serializers.ModelSerializer):
try:
owner = serializers.HiddenField(
default=serializers.CurrentUserDefault()
)
except Exception as exception:
print(exception)
class Meta:
model = Tweet
fields = '__all__'
read_only_fields = ['owner']
def validate(self, attrs):
if len(attrs['content']) > MAX_TWEET_LENGTH:
raise serializers.ValidationError("This tweet is too long")
return attrs
class Tweet(models.Model):
id = models.AutoField(primary_key=True)
owner = models.ForeignKey('auth.User', related_name='tweets', on_delete=models.CASCADE)
content = models.TextField(blank=True, null=True)
image = models.FileField(upload_to='images/', blank=True, null=True)
class Meta:
ordering = ['-id']
#api_view(['POST'])
def tweet_create_view(request, *args, **kwargs):
serializer = TweetSerializer(data=request.POST)
user = request.user
if serializer.is_valid():
serializer.save()
else:
print(serializer.errors)
return JsonResponse({}, status=400)
try:
pass
except Exception as e:
print(str(e))
return JsonResponse({}, status=400, safe=False)
return JsonResponse({}, status=201)
The solution was to pass a context as I read from drf documentation for CurrentUserDefault()
#api_view(['POST'])
def tweet_create_view(request, *args, **kwargs):
context = {
"request" : request
}
serializer = TweetSerializer(data=request.POST, context=context)
if serializer.is_valid():
serializer.save()
return JsonResponse({}, status=201)
A default class that can be used to represent the current user. In order to use this, the 'request' must have been provided as part of the context dictionary when instantiating the serializer.
models.py
class PostAdvertisment(models.Model):
# post=models.ForeignKey(Post,on_delete=models.CASCADE,null=True,blank=True)
created_at=models.DateTimeField(auto_now_add=True)
title=models.CharField(max_length=255,null=True,blank=True)
url=models.URLField(null=True,blank=True)
advertizing_content= models.TextField(null =True ,blank=True)
def __str__(self):
return f'{self.title}'
class Post(models.Model):
# created_at=models.DateTimeField(efault=datetime.now, blank=True)
created_at=models.DateTimeField(auto_now_add=True)
author=models.ForeignKey(User,on_delete=models.CASCADE,related_name="post")
title=models.CharField(max_length=128,null=True,blank=True)
rate=models.IntegerField(validators=[MinValueValidator(1),MaxValueValidator(5)],default=True,null=True,blank=True)
# rating=models.IntegerField(null=True,blank=True)
content=models.TextField(null=True,blank=True)
review=models.CharField(max_length=250,null=True,blank=True)
url=models.URLField(null=True,blank=True)
voters = models.ManyToManyField(settings.AUTH_USER_MODEL,blank=True,related_name="post_voters")
tags = TaggableManager(blank=True)
comments=models.ManyToManyField('Comment',blank=True,related_name="comments_post")
anonymous = models.BooleanField(default=False, blank=True)
fake = models.BooleanField(default=False, blank=True)
genuine = models.ManyToManyField(settings.AUTH_USER_MODEL , blank=True, related_name="post_genuines")
spam = models.ManyToManyField(settings.AUTH_USER_MODEL , blank=True, related_name="post_spames")
advertisement=models.ForeignKey(PostAdvertisment,on_delete=models.CASCADE,null=True,blank=True)
def __str__(self):
return f'{self.content}'
def get_absolute_url(self):
return reverse('post:post_detail' , kwargs={'post_id':Post.id})
so here is my serializers.py
class PostSerializer(TaggitSerializer,serializers.ModelSerializer):
tags = TagListSerializerField()
author = serializers.StringRelatedField(read_only=True)
comments = CommentSerializer(many=True, required=False, read_only=True)
# title = serializers.CharField()
advertisement = PostAdvertisementSerializer()
# advertisement = serializers.SlugRelatedField(
# queryset=PostAdvertisment.objects.all(),
# slug_field='advertisement'
# )
# category_name = serializers.CharField(source='advertisement.title')
class Meta:
model = Post
fields = ('id','title','rate','author','content','review','url','tags', 'fake','comments', 'created_at', 'anonymous','advertisement')
# def create(self, validated_data):
# tag = validated_data.pop('advertisement')
# tag_instance, created =PostAdvertisment.objects.get_or_create(title=tag)
# article_instance = Post.objects.create(**validated_data, advertisement=tag_instance)
# return article_instance
# def create(self, validated_data):
# serializer = self.get_serializer(data=self.request.data)
# advertisment = self.request.data.pop('advertisement')
# company_instance = PostAdvertisment.objects.filter(id=advertisment).first()
# if not serializer.is_valid():
# print(serializer.errors)
# data = serializer.validated_data
# serializer.save(PostAdvertisment=company_instance)
# headers = self.get_success_headers(serializer.data)
# return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def create(self,validated_data):
advertisement=validated_data.pop('advertisement')
post= Post.objects.create(**validated_data)
for advertise in advertisement:
PostAdvertisment.object.create(**advertise)
return post
so the commented part of the code is something which I've Tried
differnt appraoches gave me differnt kidn of error but none of them have worked
https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers
I've followeed this
but its no use when ever i try to post an object either that advertisment might be null or it gives me some kind of strange error depening on the create method i have used
"advertisement":[]
this is what the error
{
"advertisement": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got list."
]
}
}
when i changed it to {}
"advertisement": null
but when i tried to give data to it
AttributeError at /api/post/
type object 'PostAdvertisment' has no attribute 'object'
Request Method: POST
Request URL: http://localhost:8000/api/post/
im not sure how to add data to nested objects
You missprinted, not PostAdvertisment.object.create(**advertise) but PostAdvertisment.objects.create(**advertise) you missed "s".
Advice you to read the error(traceback).
First, I don't think the advertisement is expected to be a list. Based on the Post model and PostSerializer, advertisement is only one.
After creating a PostAdvertisement you also have to update post.advertisement/post.advertisement_id. This is how I think it would be:
advertisement=validated_data.pop('advertisement')
post = Post.objects.create(**validated_data)
post.advertisement = PostAdvertisment.objects.create(**advertisement)
post.save()
return post
You could also create the PostAdvertisement first then create the Post so there is only 2 db queries instead of 3:
validated_data["advertisement"] = PostAdvertisment.objects.create(**validated_data["advertisement"])
post = Post.objects.create(**validated_data)
return post
I am building an API using Django Rest Framework for my car-sharing app. I want to let not owner users to have access to update "participants" field in race, so they can join. Other fields should be available only to owner. I was reading about django-guardian, but i don't realy understand how to implement it. Here's my model:
from django.db import models
from django.contrib.auth.models import User
class Race(models.Model):
owner = models.ForeignKey("auth.User", related_name = 'races', on_delete=models.CASCADE)
origin_long = models.DecimalField(max_digits=8, decimal_places=3)
origin_lat = models.DecimalField(max_digits=8, decimal_places=3)
destination_long = models.DecimalField(max_digits=8, decimal_places=3)
destination_lat = models.DecimalField(max_digits=8, decimal_places=3)
start_time = models.TimeField(auto_now=False, auto_now_add=False)
participants = models.ManyToManyField(User,blank=True)
schedule = models.DurationField(blank=True,null=True)
subs = models.ManyToManyField(User, related_name='subs',blank=True)
cost = models.DecimalField(max_digits=5, decimal_places=2)
def __str__(self):
return self.user.get_full_name()
Thank you in advance.
I don't think Django has anyway to have field level permission by default.
But we can tweak and restrict the fields through the serializers.py and views.py .
In views.py
class RaceUpdateView(UpdateAPIView):
lookup_field = 'pk'
serializer_class = RaceUpdateSerializer
queryset = Race.objects.all()
permission_classes = [IsAuthenticated]
model = Race
def put(self, request, pk):
try:
try:
race_obj = self.get_object()
except Exception as error:
context = {'error': "Race Id does not exist", 'success': "false", 'message': 'Race Id does not exist.'}
return Response(context, status=status.HTTP_404_NOT_FOUND)
#I don't know how you are checking owner. So i kept it this way.
if request.user.id != race_obj.owner.id:
#passing the fields which are to be used by the serializer.
serializer = RaceUpdateSerializer(race_obj, data=request.data, partial=True, fields=('participants',))
else:
serializer = RaceUpdateSerializer(race_obj, data=request.data, partial=True)
if serializer.is_valid():
try:
serializer.save()
except Exception as error:
context = {"success": False, "message": "Update Failed. %s" % str(error), "error": str(error)}
return Response(context, status=status.HTTP_400_BAD_REQUEST)
context = {"success": True, "message": "Updated Successful", "error": "", "data": serializer.data}
return Response(context, status=status.HTTP_200_OK)
context = {"success": False, "message": "Updated Failed, Invalid Input Data", "error": str(serializer.errors)}
return Response(context, status=status.HTTP_400_BAD_REQUEST)
except Exception as error:
context = {'error': str(error), 'success': "false", 'message': 'Failed To Update Race.'}
return Response(context, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
In serializers.py
class RaceUpdateSerializer(ModelSerializer):
class Meta:
model = Race
fields = '__all__'
def __init__(self, *args, **kwargs):
# Don't pass the 'fields' arg up to the superclass
fields = kwargs.pop('fields', None)
# Instantiate the superclass normally
super(RaceUpdateSerializer, self).__init__(*args, **kwargs)
if fields is not None:
# Drop any fields that are not specified in the `fields` argument.
allowed = set(fields)
existing = set(self.fields)
for field_name in existing - allowed:
self.fields.pop(field_name)
This way only the mentioned fields which is called from the views.py will be used while updating.
serializer = RaceUpdateSerializer(race_obj, data=request.data, partial=True, fields=('participants',))
It will achieve the task that you are trying to do.
Note - You can allow multiple fields this way as well
serializer = RaceUpdateSerializer(race_obj, data=request.data, partial=True, fields=('field1','field2'))
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"