The problem: when I pass someModelSerializer.data as a nested data for anotherModelSerializer, and after that I pass anotherModelSerializer.data to Response, in the response I see only two fields of SomeModel instead of 5. But when I pass someModelSerializer.data directly to Response, I can see that all model fields are present.
Details below.
I have TranslationHistory model
class TranslationHistory(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
string = models.CharField(max_length=255)
count = models.IntegerField(default=1)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='translation_history')
and it's TranslationHistorySerializer
class TranslationHistorySerializer(serializers.ModelSerializer):
class Meta:
model = TranslationHistory
user = serializers.PrimaryKeyRelatedField(read_only=True, default=None)
count = serializers.IntegerField(read_only=True)
def validate_user(self, value):
return self.context['request'].user
def update(self, instance, validated_data):
instance.count += 1
instance.save()
return instance
I also have a virtual entity Translation, which is not a model. It has it's own serializer.
class TranslationSerializer(serializers.Serializer):
translation_history = TranslationHistorySerializer() # nested serializer
translation = serializers.CharField()
In my view, if I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
return Response(historySerializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"id": 18,
"user": 1,
"count": 72,
"created": "2015-07-15T15:35:50.751219Z",
"updated": "2015-07-24T15:37:04.247469Z",
"string": "hello"
}
But if in my view I do like this,
history = TranslationHistory().findByUserAndString(request.user, string)
historySerializer = TranslationHistorySerializer(history)
serializer = TranslationSerializer(
data={
'translation_history': historySerializer.data,
'translation': 'hello'
},
context={'request': request}
)
serializer.is_valid(raise_exception=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have the response like this.
{
"translation_history": {
"user": 1,
"string": "hello"
},
"translation": "hello"
}
Why am I getting only user and string fields in this case?
Related
Whenever i called my booking function with a post method in that it throws me an error of 'Invalid Data Type it expected dict but its an int'. So when i debug that i found a problem in my serializers so Please help me to resolve the error
model.py
class Booking(models.Model):
details = models.ForeignKey(PersonalDetails, on_delete=models.CASCADE)
quantity = models.IntegerField(null=False)
total_amount = models.IntegerField()
show = models.ForeignKey(ShowTime, on_delete=models.CASCADE)
def __str__(self) -> str:
return self.total_amount
views.py
class BookingTicketsQuery(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
booking_id = request.query_params.get('booking_no', None)
if booking_id is not None:
queryset = Booking.objects.get(id=booking_id)
else:
return Response("invalid", status=status.HTTP_409_CONFLICT)
seriliazer = BookingSerializer(queryset)
return Response(seriliazer.data)
def post(self, request, format=None):
recieve_data = JSONParser().parse(request)
showtime = ShowTime.objects.get(id=recieve_data['show'])
print(showtime)
if recieve_data['quantity'] > (showtime.total_seats - showtime.booked_seats):
return Response("Error: No seats available", status=status.HTTP_409_CONFLICT)
recieve_data['total_amount'] = showtime.total_price * \
recieve_data['quantity']
showtime.booked_seats += recieve_data['quantity']
showtime.save()
serializer = BookingSerializer(data=recieve_data)
if serializer.is_valid():
booking_obj = serializer.save()
return Response(booking_obj.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers.py
class ShowMovieSerializer(serializers.ModelSerializer):
cinema = CinemaSerializer()
movie = MovieSerializer()
class Meta:
model = ShowTime
fields = ('id', 'show_start_time', 'show_end_time',
'total_seats', 'booked_seats', 'cinema', 'movie')
class BookingSerializer(serializers.ModelSerializer):
show = ShowMovieSerializer()
details = PersonalDetailsSerializer()
class Meta:
model = Booking
fields = ('id', 'total_amount', 'quantity', 'details', 'show')
traceback
Bad Request: /api/v1/bookings/
[19/Jul/2021 12:07:20] "POST /api/v1/bookings/ HTTP/1.1" 400 168
{
"details": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
},
"show": {
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
}
}
my post data:
{
"details": 1,
"quantity":1,
"show":1
}
You added a ShowMovieSerializer() and PersonalDetailsSerializer(). Therefore drf expects data that represents both models (nested data). What you have to do instead of posting { "details": 1, "quantity":1, "show":1 }-data you have to adjusted your data that represents your model. You are posting data with a pk of your Movie and Show.
For instance:
{
"id":....,
"total_amount": ....,
....
"movie": {
"title": ...., # fields are only for explanation
"length": ....
},
"show": {
"title": ....,
"actor": ....
}
}
If you only want to establish a relationship between you ShowTime and Booking you have to remove your serializers.
dealing with nested data
I tried several ways to make this work and I couldn't.
It is very likely, because I believe it should be working. I may be missing something that I am not aware of.
I thank you for your attention.
request.data
{
"first_name": "Marcelo",
"last_name": "Wanderley",
"username": "marcelo",
"cpf": "1234",
"telefone": "99999999",
"email": "marceloa#teste.com",
"observacao": "",
"groups": [
{
"id": 2
},
{
"id": 4
}
],
"password": "111111",
"password_again": "111111",
"token_user_chain": "TMWTIeGs2t1YPpKke2RZh2tLVMuMWdLFxaFYdD",
"private_key": ""
}
'groups': [{'id': 2},{'id': 4}]
View
if serializer.is_valid():
serializer.create(validated_data=serializer.validated_data)
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
Model
class GroupSerializer(serializers.ModelSerializer):
class Meta:
model = Group
fields = ('id',)
class serializerUser(serializers.Serializer):
class Meta:
model = User
id = serializers.PrimaryKeyRelatedField(read_only=True)
first_name = serializers.CharField()
last_name = serializers.CharField()
email = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all())])
cpf = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all())])
token_user_chain = serializers.CharField(validators=[UniqueValidator(queryset=User.objects.all())])
telefone = serializers.CharField()
groups = GroupSerializer(many=True)
observacao = serializers.CharField(allow_null=True, allow_blank=True)
password = serializers.CharField(write_only=True)
username = serializers.CharField(write_only=True,validators=[UniqueValidator(queryset=User.objects.all())])
password_again = serializers.CharField(write_only=True)
Output Print
OrderedDict([('first_name', 'Marcelo'), ('last_name', 'Wanderley'),
('email', 'marcelo#teste.com'), ('cpf', '123'), ('token_user_chain',
'TMWTIeGs2vpH7DGCYNiSCttdirrMqPFEgPnczA'), ('telefone', '999999'),
('groups', [OrderedDict(), OrderedDict()]), ('observacao', ''),
('password', '999999'), ('username', 'Marcelo Wanderley'),
('password_again', '999999')])
('groups', [OrderedDict(), OrderedDict()])
I went through the source codes, and maybe got the cause. Because it has a nested serializer, so it will exclude id field which not in _writable_fields when calling run_validation(). then return a empty OrderedDict instance.
if keep using nested serializer in order to creating instances of manytomany relation model.
groups_data = request.data.pop('groups')
if serializer.is_valid():
instance = serializer.create(validated_data=serializer.validated_data)
groups = [Group.objects.create(**kwargs) for kwargs in groups_data] #NOQA
instance.groups.set(groups)
instance.save()
return Response(serializer.data, status=HTTP_201_CREATED)
return Response(serializer.errors, status=HTTP_400_BAD_REQUEST)
OR if do not need to create new instances of related model, it could use the default ManyRelatedField rather than nested serializer. request.data['groups'] is just a list of id value:
groups: [
1,
2
]
So I am creating follower system but there is a problem. Everything is working properly. but in follower serializer I want the username's and other details of users.
models.py
class Follow(models.Model):
user = models.OneToOneField(User,related_name="user" ,on_delete=models.CASCADE)
''' to obtain user ' eeee = User.objects.first() , eeee.user' '''
following = models.ManyToManyField(User,related_name='following_user',blank=True)
''' to obtain followers ' eeee.following_user.all()' '''
''' to obtain following ' eeee.user.following.all()' '''
def __str__(self):
return self.user.username
In field following , user.following.all() is used to get the user in manytomany field of request.user and
following_user.all() is used get all the users who has added request.user in their following field.
serializers.py
class FollowerSerializer(ModelSerializer):
user = UserSerializer(many=False)
follower = SerializerMethodField()
class Meta:
model = Follow
fields = ('user','follower')
def get_follower(self, obj):
context = self.context
request = context.get("request")
return request.user.following_user.all().values()
Here I am serializing all the user who has added request.user in their following field
views.py
class FollowerView(RetrieveAPIView):
queryset = Follow.objects.all()
serializer_class = FollowerSerializer
permission_classes = [IsAuthenticated]
lookup_field = 'id'
api
{
"user": {
"name": "eeee"
},
"is_follower": [
{
"id": 2,
"user_id": 9
},
{
"id": 5,
"user_id": 16
},
{
"id": 3,
"user_id": 10
}
]
}
These is the api I am getting of all the user who has added request.user in their following list.
the problem here is I am getting the pk of the user from the key user_id. But I want there username and other information like email, full name of the user who has that primary key. So how can I achieve that?
Modify the get_follower method in serializers.py as follows:
def get_follower(self, obj):
context = self.context
request = context.get("request")
qs = request.user.following_user.all()
data = [{'id': obj.pk, 'user_id': obj.user_id, 'name': obj.req_field} for obj in qs]
return data
I'm currently trying to make a post request with only data field in models.py:
class Filter(models.Model):
class Meta:
verbose_name_plural = "Filter"
verbose_name = "Filter"
db_table= "listing_filter"
data = JSONField(default={})
user = models.ForeignKey('backend.user', on_delete=models.CASCADE)
I have the following Serializer , i use JSONField following the drf document docs:
class FilterSerializer(serializers.ModelSerializer):
data = serializers.JSONField(required=True)
user = serializers.CharField(required=False)
class Meta:
model = Filter
fields = ('__all__')
and use it in the APIView:
class FilterView(APIView):
def post(self, request):
login_user = request.user
received_json_data=json.loads(request.body)
valid_ser = FilterSerializer(data=received_json_data)
if valid_ser.is_valid():
post_data = received_json_data["data"]
filter = Filter.objects.create(data=post_data, user=login_user)
filter.save()
return JsonResponse({'code':'200','data': filter.id}, status=200)
else:
return JsonResponse({'code':'400','errors':valid_ser.errors}, status=400)
When i send the following data in body it worked and saved the object:
{
"data": {
"http://example.org/about": {
"http://purl.org/dc/terms/title": [
{
"type": "literal",
"value": "Anna's Homepage"
}
]
}
}
}
But when i send data as a string(which is not a json object) it still save, how do i prevent this ?
{
"data" : "abc"
}
Using django-rest framework 3.3.3
I am trying to update existing data in my database using the django-rest framework, following a previously posted solution :django-rest-framework 3.0 create or update in nested serializer
I'm fairly new to django in general, and the rest framework.
Given the following models:
class Clinic(models.Model):
name = models.TextField()
streetNumber = models.IntegerField()
unitNumber = models.CharField(max_length=10, blank=True, null=True)
streetName = models.CharField(max_length=50)
city = models.CharField(max_length=50)
province = models.CharField(max_length=3)
country = models.CharField(max_length=30)
postalCode = models.CharField(max_length=6)
phone = models.CharField(max_length=10)
def __str__(self):
return self.name
class Time (models.Model):
clinic = models.ForeignKey(Clinic, related_name='times')
appointmentTime = models.CharField(max_length=100)
def __str__(self):
return self.appointmentTime
And the following serializers:
class TimeSerializer(serializers.ModelSerializer):
class Meta:
model = Time
fields = ('appointmentTime',)
class ClinicSerializer(serializers.ModelSerializer):
times = TimeSerializer(many=True)
class Meta:
model = Clinic
fields = '__all__'
def update(self, instance, validated_data):
instance.name = validated_data['name']
instance.streetName = validated_data['streetName']
instance.unitNumber = validated_data['unitNumber']
instance.city = validated_data['city']
instance.province = validated_data['province']
instance.country = validated_data['country']
instance.postalCode = validated_data['postalCode']
instance.phone = validated_data['phone']
#print(instance)
instance.save()
times_data = [item['id'] for item in validated_data['times']]
print(times_data)
for time in instance.clinics:
if time.id not in times_data:
time.delete()
for item in validated_data['times']:
time = Time(id=item['id'], clinic=instance,
appointmentTime=item['appointmentTime'])
time.save()
return instance
And the following view:
class ClinicList(APIView):
def get(self,request):
clinics = Clinic.objects.all()
serializer = ClinicSerializer(clinics, many=True)
return Response({'results': serializer.data})
def post(self, request):
serializer = ClinicSerializer(data=request.data)
if serializer.is_valid():
serializer.update(Clinic, serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
And the following existing data:
{
"id": 2,
"times": [
{
"appointmentTime": "time3"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
With my JSON POST data being:
{
"id": 2,
"times": [
{
"appointmentTime": "AAAA"
},
{
"appointmentTime": "time3"
}
],
"name": "Example Clinic",
"streetNumber": 123,
"unitNumber": "",
"streetName": "Example Street",
"city": "Exampletown",
"province": "Ont",
"country": "Canada",
"postalCode": "A1A1A1",
"phone": "9059059059"
}
My problem is that I am receiving an error stating:
File"C:\Users\colin\Documents\GitHub\Walk_inExpressAPI\clinics\serializer.py",
line 31, in update
instance.save()
TypeError: save() missing 1 required positional argument: 'self'
I'm not sure what Im doing wrong here, or what value to supply to instance.save() to correct this error. I've checked that instance was an object of type Clinic.
What i'm trying to accomplish is that each Clinic can have multiple appointment times. They wont share appointmentTime objects, because I would like to be able to delete an appointentTime each time a new appointment is booked, and each clinic will require its own set of available appointmentTimes.
Hopefully you guys can help lead me in the right direction! Thanks!
You should user the method PUT in your APIView:
def put(self, request, pk):
clinics = Clinic.objects.get(pk=pk)
serializer = ClinicSerializer(clinics, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
so in you url.py should have a pk, to identify the object you want to modify:
urlpatterns = [
url(r'^snippets/$', views.ClinicList.as_view()),
url(r'^snippets/(?P<pk>[0-9]+)/$', views.ClinicList.as_view()),
]
In update method change the instance attributes like:
instance.name = validated_data.get('name',instance.name)
Try to see the example in DRF page as well.