Reputation: 418
I have an API (using django-rest-framework) with two models (e.g. Users and Cars).
I'm trying to get the user's latest (highest ID) Car also returned when the User is queried.
I've tried including cars = CarSerializer(many=True)
in the User serializer, which returns all cars for that user.
I've tried cars = CarSerializer(many=False)
, which returns the format I want, but no cars turn up.
car model
class Car(models.Model):
name = models.TextField(default='')
year = models.IntegerField(null=True)
owner = models.ForeignKey('User', related_name='cars')
car serializer
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = ('id', 'name', 'year', 'owner')
user serializer
class UserSerializer(serializers.ModelSerializer):
car = CarSerializer(many=True)
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'car')
car view
class CarViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Car.objects.all()
serializer_class = CarSerializer
user view
class UserViewSet(viewsets.ReadOnlyModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
Given two cars belonging to the user with an ID of 1:
id: 1
owner: 1
name: "kinda okay car"
year: 2012
id: 2
owner: 1
name: "the fastest one"
year: 2020
JSON Result of GET /users/1
{
"id": 1,
"first_name": "Bob",
"last_name": "McFastCar",
"car": {
"name": "the fastest one",
"year": 2020
}
}
JSON Result of GET /users/1
{
"id": 1,
"first_name": "Bob",
"last_name": "McFastCar",
"cars": [
{
"id": 2,
"owner": 1
"name": "the fastest one",
"year": 2020
},
{
"id": 1,
"owner": 1
"name": "kinda okay car",
"year": 2012
}
],
}
Thanks, I wheely hope you can help drive me towards a solution.
Upvotes: 3
Views: 2887
Reputation: 5958
You can actually create a @property
in your User
model that returns the last car only:
class User:
...
@property
def last_car(self):
return self.cars.last()
And then in the UserSerializer
you can just serialize this property:
class UserSerializer:
last_car = CarSerializer(read_only=True)
class Meta:
model = User
fields = (..., 'last_car')
This would do the trick.
P.S. : If you still want your latest car entry to be serialized as "car": { ... }
but not "last_car": { ... }
, you can use the source
attribute on the CarSerializer
itself:
class UserSerializer:
car = CarSerializer(source='last_car', read_only=True)
...
Upvotes: 5
Reputation: 126
You can write UserSerializers like bellow
class UserSerializer(serializers.ModelSerializer):
cars = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('id', 'first_name', 'last_name', 'cars')
def get_cars(self, instance):
cars = instance.cars.all().order_by('-year')
return CarSerializer([cars[0], ], many=True).data
and calling GET /users/1
will return results like this
{
"id": 2,
"first_name": "Bob",
"last_name": "McFastCar",
"cars": [
{
"id": 2,
"name": "the fastest one",
"year": 2020,
"owner": 2,
"first_name": "Bob"
}
]
}
The bellow line gave me an unknown error so I've written it with many=True
return CarSerializer(cars, many=False).data
Upvotes: 0
Reputation: 2299
The CarSerializer
must be linked with a queryset or an object otherwise it will return all objects
# get the car object with the highest using order_by
car = Car.objects.all().order_by('-id')[0]
# build a car serializer from the car object
car_serializer = CarSerializer(car, many=False)
Upvotes: 1