In models:
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField()
sub_category = models.ForeignKey(SubCategory, on_delete=models.SET_NULL, null=True, related_name='product')
class SubCategory(models.Model):
sub_category_name = models.CharField(max_length=50)
sub_category_img = models.ImageField(upload_to='sub_category_img')
In serializers:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
class SubCategorySerializer(serializers.HyperlinkedModelSerializer):
product = ProductSerializer(many=True)
class Meta:
model = SubCategory
fields = ['url', 'id', 'sub_category_name', 'sub_category_img', 'product']
Using models, I can get access to Products.sub_category for a single queryset and also SubCategory.product reverse query using related_name in ForeignKey.
Using serializers, I can get access the ProductSerializer queryset using SubCategorySerializer but can't access SubCategorySerializer from ProductSerializer. I only get the url for sub_category when use ProductSerializer.
How can i reverse both in api endpoints?
I tried, in serializers,
class ProductSerializer(serializers.HyperlinkedModelSerializer):
sub = SubCategorySerializer()
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category', 'sub']
class SubCategorySerializer(serializers.HyperlinkedModelSerializer):
product = ProductSerializer(many=True)
class Meta:
model = SubCategory
fields = ['url', 'id', 'sub_category_name', 'sub_category_img', 'product']
It's not working. How can I get both queryset like models?
You can try like this:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
sub_category = SubCategorySerializer()
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
Or just use depth=1 in Product Serializer:
class ProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Product
fields = ['url', 'id', 'name', 'price', 'sub_category']
depth = 1
Related
I have two models, I have to make an endpoint where the results of two tables should appear in the json, which have a fongeringkey that joins them.
My code is the following:
models.py
class Property(models.Model):
address = models.CharField(max_length=120)
city = models.CharField(max_length=32)
price = models.BigIntegerField()
description = models.TextField(blank=True, null=True)
year = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'property'
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE)
status = models.ForeignKey(Status, on_delete=models.CASCADE)
update_date = models.DateTimeField()
class Meta:
managed = False
db_table = 'status_history'
views.py
class StandardResultsSetPagination(PageNumberPagination):
page_size = 10
page_size_query_param = "page_size"
max_page_size = 1000
class PropertyListView(viewsets.ModelViewSet):
http_method_names = ['get', 'head']
serializer_class = PropertyListSerializer
queryset = Property.objects.all()
pagination_class = StandardResultsSetPagination
def get_serializer_class(self):
if self.action == 'list':
return PropertyListSerializer
return PropertyListSerializer
def get_queryset(self):
queryset = Property.objects.all()
if self.request.GET.get('year'):
queryset = queryset.filter(year=self.request.GET.get('year'))
if self.request.GET.get('city'):
queryset = queryset.filter(city=self.request.GET.get('city'))
if self.request.GET.get('status'):
queryse = queryset.filter(statushistory__status=self.request.GET.get('status'))
else:
queryset = queryset.order_by('-year')
return queryset
def list(self, request):
queryset = self.filter_queryset(self.get_queryset())
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
I can correctly filter the parameters, city, year(model Property) and above all status(model StatusHistory) with the following code snippet:
if self.request.GET.get('status'):
queryset = queryset.filter(statushistory__status=self.request.GET.get('status'))
My problem is how I show in my JSON Response the new field fusion of the two models statushistory__statustry adding the following in serializers.py
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
statushistory_set =StatusHistoryListSerializer(many=True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'statushistory_set')
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
Without any result.
I think you could set related_name attribute in the foreign key field.
class StatusHistory(models.Model):
property = models.ForeignKey(Property, on_delete=models.CASCADE, related_name = 'status_histories')
status = models.ForeignKey(Status, on_delete=models.CASCADE)
...
And in the PropertyListSerializer,
class PropertyListSerializer(serializers.HyperlinkedModelSerializer):
status_histories = StatusHistoryListSerializer(many=True, read_only = True)
class Meta:
model = Property
fields = ('id', 'address', 'city', 'price', 'description', 'year', 'status_histories')
You can also get property field as an object in the StatusHistory like the following:
class StatusHistoryListSerializer(serializers.HyperlinkedModelSerializer):
property = PropertyListSerializer(read_only = True)
class Meta:
model = StatusHistory
fields = ('property', 'status', 'update_date')
I have two models which have one to many relationship and I want to serialize the related fields on both ends.
The models:
class Mechanic(models.Model):
name = models.CharField('Name', max_length=256)
status = models.CharField('Status', choices=constants.MECHANIC_STATUS, default=constants.MECHANIC_STATUS[0][0],
max_length=64)
current_lat = models.CharField('Current Latitude', max_length=64)
current_lng = models.CharField('Current Longitude', max_length=64)
def __str__(self):
return self.name
class Service(models.Model):
type = models.CharField('Service Type', choices=constants.SERVICE_TYPES,
default=constants.SERVICE_TYPES[0][0], max_length=64)
mechanic = models.ForeignKey(Mechanic, on_delete=models.CASCADE, related_name='services')
vehicle_type = models.CharField('Vehicle Type', choices=constants.VEHICLE_TYPES,
default=constants.VEHICLE_TYPES[0][0], max_length=64)
charges = models.IntegerField('Charges')
def __str__(self):
return "{}, {}".format(self.mechanic, self.type)
The serializers:
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicSerializer(read_only=True) # Throws an error
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
How do I approach this problem? I understand that I've created a cyclic dependency because both serializers depend on each other.
Is there a better way to do it?
as an extenstion to my comment in the OP, create a new serializer class of Mechanic model before the ServiceSerializer class
class MechanicShortSerializer(serializers.ModelSerializer):
class Meta:
model = Mechanic
fields = '__all__'
class ServiceSerializer(serializers.ModelSerializer):
mechanic = MechanicShortSerializer(read_only=True) # replace with new serializer
class Meta:
model = Service
fields = ('id', 'type', 'mechanic', 'vehicle_type', 'charges')
class MechanicSerializer(serializers.ModelSerializer):
services = ServiceSerializer(many=True, read_only=True)
class Meta:
model = Mechanic
fields = ('id', 'name', 'status', 'services', 'current_lat', 'current_lng')
read_only_fields = ('id',)
So I have two models here
class ProductType(models.Model):
product_type = models.CharField(max_length=50)
def __str__(self):
return self.product_type
class Product(models.Model):
product_name = models.CharField(max_length=50)
product_type = models.ForeignKey(ProductType, on_delete=models.CASCADE)
product_image = models.ImageField(blank=False, null=False, upload_to="products")
product_price = models.FloatField()
product_description = models.TextField(default="Product Description")
def __str__(self):
return self.product_name
And I have created an api using django-rest-framework to be consumed by the React frontend. However, when I try to get the product_type, it'd just give me a number instead of the name as shown below. So, how can I replace that when retrieving the data with Product.objects.all() in the view?
If you want to only show product_type instead of id you can use serializers.SerializerMethodField() in your ProductSerializer and with defining source in that method you can tell serializer to return your desired field.
Example:
class ProductSerializer(serializers.ModelSerializer):
product_type = serializers.SerializerMethodField(source='product_type__product_type')
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
But if you want to serialize all the ProductType itself you can define a serializer for this model and then use this serializer in your ProductSerializer to serialize the whole object of ProductType.
Example:
class ProductTypeSerializer(serializers.ModelSerializer):
class Meta:
model = ProductType
fields = ('id', 'product_type',)
class ProductSerializer(serializers.ModelSerializer):
product_type = ProductTypeSerializer()
class Meta:
model = Product
fields = ('id', 'product_name', 'product_image', 'product_price', 'product_description', 'product_type')
The second solution is also known as nested serializers.
I have many-to-many relations and I want to serialize reverse relations.
Here are my models:
class Nutrition(models.Model):
name = models.CharField(max_length=30, blank=False)
def __str__(self):
return self.name
class Company(models.Model):
name = models.CharField(max_length=30, blank=False)
nutritions = models.ManyToManyField(Nutrition, blank=True, related_name="companyID")
def __str__(self):
return self.name
And here are my serializers:
class NutritionSerializer(serializers.ModelSerializer):
companyID = CompanySerializer(read_only=True, many=True)
class Meta:
model = Nutrition
fields=('id', 'name', 'companyID')
class CompanySerializer(serializers.ModelSerializer):
nutritions_list = NutritionSerializer(source="nutritions", read_only=True, many=True)
class Meta:
model = Company
fields = ('id', 'name', 'nutritions_list')
And I'm getting an error:
NameError: name 'CompanySerializer' is not defined
You are getting the name error because you call CompanySerializer:
class NutritionSerializer(serializers.ModelSerializer):
companyID = CompanySerializer(read_only=True, many=True)
...
before defining it (a couple of lines under):
class CompanySerializer(serializers.ModelSerializer):
...
What i would suggest doing is adding another serializer for Company which does not include NutritionSerializer so you can place it above both NutritionSerializer and CompanySerializer. Here is how it would look:
class SimpleCompanySerializer(serializers.ModelSerializer):
class Meta:
model = Company
fields = ('id', 'name')
class NutritionSerializer(serializers.ModelSerializer):
company = SimpleCompanySerializer(read_only=True, many=True)
class Meta:
model = Nutrition
fields=('id', 'name', 'company')
class CompanySerializer(serializers.ModelSerializer):
nutritions_list = NutritionSerializer(source="nutritions", read_only=True, many=True)
class Meta:
model = Company
fields = ('id', 'name', 'nutritions_list')
I have the following models:
class User(AbstractBaseUser, PermissionsMixin):
SUPERVISOR = 1
REVIEWER = 2
VERIFIER = 3
READ_ONLY = 4
USER_TYPE = [
(SUPERVISOR, 'Supervisor'),
(REVIEWER, 'Reviewer'),
(VERIFIER, 'Verifier'),
(READ_ONLY, 'Read Only'),
]
email = models.EmailField(max_length=50, unique=True)
name = models.CharField(max_length=100)
phone = models.CharField(max_length=50, null=True)
role = models.IntegerField(
choices=USER_TYPE,
default=READ_ONLY
)
is_active = models.BooleanField(default=True)
class Comment(models.Model):
text = models.TextField()
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT
)
View:
class CommentViewSet(BaseCertViewSet):
queryset = Comment.objects.all()
serializer_class = serializers.CommentSerializer
Serializer:
class CommentSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
class Meta:
model = Comment
fields = ('id', 'text', 'user',)
read_only_fields = ('id',)
My question: when I hit the comment API endpoint, I'd like it to return the user role from the user model as well. How do I go about doing that?
I believe you can use a QuerySet.annotation:
EDIT: F is from django.db.models so you have to import that as well.
queryset = Comment.objects.annotate(user_role=F("user__role")) in your CommentViewSet
EDIT:
In order to get the serializer to recognize the field that you are trying to add to the QuerySet, you must also define the field on the serializer like this:
class CommentSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
# add line below to your code
user_role = IntegerField()
class Meta:
model = Comment
# you have to add it to the list of fields as well
fields = ('id', 'text', 'user', 'user_role')
read_only_fields = ('id',)
The solution that worked for me (not sure it's the most elegant one, happy to change to a better way of doing that):
class CommentSerializer(serializers.ModelSerializer):
"""Serializer for Comment object"""
user = serializers.SlugRelatedField(
read_only=True,
slug_field='name'
)
role = serializers.SerializerMethodField()
def get_role(self, obj):
user = obj.user_id
role = User.objects.only('id').get(
id=user).role
return role
class Meta:
model = Comment
fields = ('id', 'value', 'text', 'user', 'role',
'date_created', 'date_updated')
read_only_fields = ('id',)