my model
class Vacatures(models.Model):
title = models.CharField(max_length=50)
employer = models.ForeignKey(Employer, on_delete=models.CASCADE)
my .view
class VacaturesOverzichtMobile(generics.ListAPIView):
model = Vacatures
serializer_class = VacaturesSerializer
queryset = Vacatures.objects.all()
def get_queryset(self):
queryset = Vacatures.objects.all()
return queryset
So in the model there is Employer as foreign key. The api call in the view is working fine. The only thing is I get the employer as employer.pk , but I want the name of the Employer which is in the Employers model.
Can I tune the queryset so that it returns employer.name instead of employer.pk
I'd probably go with serializers for both models of the relationship as described in the DRF documentation. Then you have full control over which attributes of Employer you want to include. This approach is called "serializers as fields" in DRF.
class EmployerSerializer(serializers.ModelSerializer):
class Meta:
model = Employer
fields = ['name']
class VacaturesSerializer(serializers.ModelSerializer):
employer = EmployerSerializer(read_only=True)
class Meta:
model = Vacatures
fields = ['title']
Related
I have an example model which has a fk relation with user model and Blog model. Now I have a get api which only requires certain fields of user to be displayed.
My model:
class Example(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
null=True,
related_name="user_examples",
)
blog = models.ForeignKey(
Blog,
on_delete=models.CASCADE,
null=True,
related_name="blog_examples",
)
/................./
Now my view:
class ExampleView(viewsets.ModelViewSet):
queryset = Example.objects.all()
serializer_class = ExampleSerializer
def list(self, request, *args, **kwargs):
id = self.kwargs.get('pk')
queryset = Example.objects.filter(blog=id)
serializer = self.serializer_class(queryset,many=True)
return Response(serializer.data,status=200)
My serializer:
class ExampleSerializer(serializers.ModelSerializer):
class Meta:
model = Example
fields = ['user','blog','status']
depth = 1
Now when I call with this get api, I get all example objects that is required but all the unnecessary fields of user like password, group etc . What I want is only user's email and full name. Same goes with blog, I only want certain fields not all of them. Now how to achieve this in a best way??
You will have to specify the required fields in nested serializers. e.g.
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = Blog
fields = ['title', 'author']
class ExampleSerializer(serializers.ModelSerializer):
blog = BlogSerializer()
class Meta:
model = Example
fields = ['user','blog','status']
are you setting depth in serializer's init method or anywhere else? beacause ideally it should only display id's and not anything else. if yes then set depth to zero and use serializer's method field to return data that you need on frontend. I can provide you with example code samples
I have following serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id']
class PersonSerializer(serializers.ModelSerializer):
user = UserSerializer()
comments = CommentSerializer(source='comment_set', many=True)
class Meta:
model = Person
fields = '__all__'
And related views
class PersonRetrieveView(generics.RetrieveAPIView):
queryset = Person.objects.all()
serializer_class = PersonSerializer
class UserRetrieveView(generics.RetrieveAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = (permissions.IsAuthenticated, )
The models are standard, Person related to User by Foreign key.
So my url path('persons/<int:pk>/', PersonRetrieveView.as_view()), returns Person object with user field with user id. I want to make kinda reverse logic. By user id I want to query all users with field person with all persons related to this user by user id.
I can't just make
class UserSerializer(serializers.ModelSerializer):
person = PersonSerializer()
class Meta:
model = User
fields = ['id']
because Person serializer defined below the User serializer. Is there any way to do it simple with generics?
Your models.py should look like this:
class Person(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='persons', # This is important
on_delete=models.CASCADE
)
# Other fields...
Then serializers.py:
class PersonSerializer(serializers.ModelSerializer):
persons=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Person
For more info or other relation types you can look at the DRF doc:
https://www.django-rest-framework.org/api-guide/relations/
I'm trying to retrieve only some of the fields in the "Appointments" associated to a rental property "Unit". From the UnitSerializer, I call a SerializerMethodField() to do a reverse lookup for the "appointment" field. This works out well. However, the queryset returns all the fields (id, time, unit, staff, prospect) in each object, when I only need a few (id, time).
I tried .values() on the queryset like so:
queryset = instance.appointment_set.values('id', 'appointment_time')
But I get "Got KeyError when attempting to get a value for field unit on serializer AppointmentSerializer.\nThe serializer field might be named incorrectly and not match any attribute or key on the dict instance.\nOriginal exception text was: unit."
Note sure if you need all the code, but here's the essential.
Models
class Appointment(models.Model):
appointment_time = models.DateTimeField()
unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
staff = models.ForeignKey(Staff, on_delete=models.CASCADE)
prospect = models.ForeignKey(Prospect, on_delete=models.CASCADE)
Serializers
class AppointmentSerializer(serializers.ModelSerializer):
class Meta:
model = Appointment
fields = ['id','appointment_time']
class UnitSerializer(serializers.ModelSerializer):
appointment = SerializerMethodField()
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']
def get_appointment(self, instance):
cutoff = _datetime.date.today() + timedelta(hours=72)
queryset = instance.appointment_set.exclude(appointment_time__gt=cutoff)
return AppointmentSerializer(queryset, many=True).data
There is a better way to handle reverse relationship in serializer:
class UnitSerializer(ModelSerializer):
appointment = AppointmentSerializer(many=True, source='appointment_set')
class Meta:
model = Unit
fields = ['id', 'address', 'appointment']
I feel like this is a super basic question but am having trouble finding the answer in the DRF docs.
Let's say I have a models.py set up like so:
#models.py
class Person(models.Model):
name = models.CharField(max_length=20)
address = models.CharField(max_length=20)
class House(models.Model):
name = models.CharField(max_length=20)
owner = models.ForeignKey(Person)
And I have a ModelSerializer set up like so:
#serializers.py
class House(serializers.ModelSerializer):
class Meta:
model = House
fields = '__all__'
What I want to do is to be able to POST new House objects but instead of having to supply the pk of the Person object, I want to be able to supply the name of the Person object.
E.g.
post = {'name': 'Blue House', 'owner': 'Timothy'}
The actual models I'm using have several ForeignKey fields so I want to know the most canonical way of doing this.
One solution may be to use a SlugRelatedField
#serializers.py
class House(serializers.ModelSerializer):
owner = serializers.SlugRelatedField(
slug_field="name", queryset=Person.objects.all(),
)
class Meta:
model = House
fields = '__all__'
This will also change the representation of your serializer though, so it will display the Person's name when you render it. If you need to render the Person's primary key then you could either override the House serializers to_representation() method, or you could implement a small custom serializer field by inheriting SlugRelatedField and overriding to_representation() on that instead.
Change your serializer as below by overriding the create() method
class House(serializers.ModelSerializer):
owner = serializers.CharField()
class Meta:
model = House
fields = '__all__'
def create(self, validated_data):
owner = validated_data['owner']
person_instance = Person.objects.get(owner=owner)
return House.objects.create(owner=person_instance, **validated_data)
I have a Cart model and a CartItem model. The CartItem model has a ForeignKey to the Cart model.
Using Django Rest Framework I have a view where the API user can display the Cart, and obviously then I want to include the CartItem in the respone.
I set up my Serializer like this:
class CartSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
cartitem_set = CartItemSerializer(read_only=True)
class Meta:
model = Cart
depth = 1
fields = (
'id',
'user',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
My problem is the second line, cartitem_set = CartItemSerializer(read_only=True).
I get AttributeErrors saying 'RelatedManager' object has no attribute 'product'. ('product' is a field in the CartItem model. If I exclude product from the CartItemSerializer I just get a new AttributeError with the next field and so on. No matter if I only leave 1 or all fields in the Serializer, I will get a error.
My guess is that for some reason Django REST Framework does not support adding Serializers to reverse relationships like this. Am I wrong? How should I do this?
PS
The reason why I want to use the CartItemSerializer() is because I want to have control of what is displayed in the response.
Ahmed Hosny was correct in his answer. It required the many parameter to be set to True to work.
So final version of the CartSerializer looked like this:
class CartSerializer(serializers.ModelSerializer):
cartitem_set = CartItemSerializer(read_only=True, many=True) # many=True is required
class Meta:
model = Cart
depth = 1
fields = (
'id',
'date_created',
'voucher',
'carrier',
'currency',
'cartitem_set',
)
It's important to define a related name in your models, and to use that related name in the serializer relationship:
class Cart(models.Model):
name = models.CharField(max_length=500)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, related_name='cart_items')
items = models.IntegerField()
Then in your serializer definition you use those exact names:
class CartSerializer(serializers.ModelSerializer):
cart_items = CartItemSerializer(read_only=True)
class Meta:
model = Cart
fields = ('name', 'cart_items',)
It would be wise to share your whole code, that is model and serializers classes. However, perhaps this can help debug your error,
My serializer classes
class CartItemSerializer(serializers.ModelSerializer):
class Meta:
model = CartItem
fields = ('id')
class CartSerializer(serializers.ModelSerializer):
#take note of the spelling of the defined var
_cartItems = CartItemSerializer()
class Meta:
model = Cart
fields = ('id','_cartItems')
Now for the Models
class CartItem(models.Model):
_cartItems = models.ForeignKey(Subject, on_delete=models.PROTECT)
#Protect Forbids the deletion of the referenced object. To delete it you will have to delete all objects that reference it manually. SQL equivalent: RESTRICT.
class Meta:
ordering = ('id',)
class Cart(models.Model):
class Meta:
ordering = ('id',)
For a detailed overview of relationships in django-rest-framework, please refer their official documentation