two unrelated models in one serializer django rest framework - django

I have tried a few different ways to include two unrelated models in one serializer to use in DRF api response. This is the latest of tries below, which doesn't work. I'm trying to include those two values (enable_hourly_rate / vacancy_number) from Tenant model which are under TenantAppUnitExpandedSerializer to TenantAppUnitSerializer. How to do this the proper way?
Thank you in advance for any help.
Code so far:
class TenantAppUnitExpandedSerializer(serializers.ModelSerializer):
class Meta:
model = Tenant
fields = (
'enable_hourly_rate',
'vacancy_number',
)
class TenantAppUnitSerializer(TenantAppUnitExpandedSerializer):
enable_hourly_rate = serializers.SerializerMethodField()
vacancy_number = serializers.SerializerMethodField()
class Meta:
model = Unit
fields = (
'id',
'unit_name',
'city',
'phone',
'enable_hourly_rate',
'vacancy_number'
)

Include it as a field in the serializer you desire to work with.
Then field_name = ThatParticularSerializer() which you want to include.
Here is how your serializer should be.
class TenantAppUnitSerializer(TenantAppUnitExpandedSerializer):
tenant_extended_unit = TenantAppUnitExpandedSerializer(many=False) # <-- here
# similarly you can set many=True if you are expecting multiple instances of it
enable_hourly_rate = serializers.SerializerMethodField()
vacancy_number = serializers.SerializerMethodField()
class Meta:
model = Unit
fields = (
'id',
'unit_name',
'city',
'phone',
'enable_hourly_rate',
'vacancy_number',
'tenant_extended_unit`, # <-- here
)

Related

Get only one filed from a nested serializer

Her it is my django serializer:
class ShopSerializer(serializers.ModelSerializer):
rest = RestSerializer(many=True)
class Meta:
model = RestaurantLike
fields = ('id', 'created', 'updated', 'rest')
This will wrap whole RestSerializer inside ShopSerializer on response.
How can I get only one or two fields from RestSerializer instead of having all the fields inside ShopSerializer?
Get only two field of RestSerializer instead of whole RestSerializer
If you want to have limited amount of fields from RestSerializer, then you can simply limit it in the fields:
class RestSerializer(serializers.ModelSerializer):
class Meta:
model = Restaurant
fields = ('id', 'name', ...)
If you want just want field, lets say the primary key or Slug, then you can use PrimaryKeyRelatedField or SlugRelatedField for that. For example:
class ShopSerializer(serializers.ModelSerializer):
rest = serializers.SlugRelatedField(
many=True,
read_only=True,
slug_field='name'
)
class Meta:
model = RestaurantLike
fields = ('id', 'created', 'updated', 'rest')

How to Serialize different nested Serializes when the order is not allowing so

so I'm new to Django and I'm creating an API using djangorestframework
When creating the serializers I encountered a problem that I can't find a way to go around it.
I have 2 models, Site and Article
The site can have many articles
an article can have one site
I did the relationship in the models and everything looks fine.
When tried to serialize the information I started with the SiteSerializer which had a line to get the ArticleSerializer and had a related name of 'articles' (also the name of the variable) and it worked
But when trying to serialize the SiteSerializer inside the ArticleSerializer with the same way it cannot be done because the order of the classes, it cannot reference to it when it's yet to be created
CODE:
class ArticleSerializer(serializers.ModelSerializer):
site = SiteSerializer(many=false)
class Meta:
model = Article
fields = ('id', 'title', 'url', 'summary', 'img', "published_date", 'site')
class SiteSerializer(serializers.ModelSerializer):
articles = ArticleSerializer(many=True)
class Meta:
model = Site
fields = ('id', 'name', 'url', 'articles')
I can't reference to SiteSerializer when it's below, but it will be the opposite if I switch them up.
What can I do in this situation, is there another way to serialize different models with many to one or many to many relationships?
You are going to have an infinite loop (Site > Articles > Site > Article, ...).
You can create 4 serializers :
class NestedArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ('id', 'title', 'url', 'summary', 'img', "published_date", 'site')
class SiteSerializer(serializers.ModelSerializer):
articles = NestedArticleSerializer(many=True)
class Meta:
model = Site
fields = ('id', 'name', 'url', 'articles')
class NestedSiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ('id', 'name', 'url')
class ArticleSerializer(serializers.ModelSerializer):
site = NestedSiteSerializer()
class Meta:
model = Article
fields = ('id', 'title', 'url', 'summary', 'img', "published_date", 'site')

Dynamically create serializer based on model field value

I have a model like so:
class A:
name = models.CharField()
group = models.ForeignKey('SomeModel', null=True, blank=True)
When I serialize this, I would like the serielizer to have different formats based on whether the 'group' field is blank or not. Of course this can be achieved by having different serializers for different formats and calling them as required in the View layer:
class TypeASerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name')
class TypeBSerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name', 'group')
But I wanted to handle it in the serializer layer itself and have a single serializer for this. Is that possible?
Serializer.instance may be None in some cases.
And get_fields() is called only once because Serializer.fields is cached from django-rest-framework 3.10: https://github.com/encode/django-rest-framework/commit/7232586c7caf66f20f56b36f1c6a9c9648eb94a4
In other words, when a serializer is used as a list by many=True (in ListModelMixin, or as a field of another serializer), the fields of all items in the list are determined by the first instance.
In that case, the solution is to override to_representation():
class TypeASerializer(serializers.ModelSerializer)
class Meta:
model = A
fields = ('id', 'name', 'group')
def to_representation(self, instance):
ret = super().to_representation(instance)
if not instance.group:
del ret['group']
return ret
This solution is a little inefficient because all fields and values are obtained from super().to_presentation() but some of them are removed again. You can consider fully implementing to_representation() without calling super's.
you can override the get_fields methods of serializer
class YourSerializer(serializers.ModelSerializer):
id = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
group = serializers.SerializerMethodField()
class Meta:
model = A
fields = ('id', 'name', 'group')
def get_fields(self):
fields = super().get_fields()
# delete all the unnecessary fields according to your logic.
if self.instance.group: # if this is detials view other wise pass object in context
del fields['group']
return fields
You can declare every field of your serializer as SerializerMethodField as follows:
class YourSerializer(serializers.ModelSerializer):
id = serializers.SerializerMethodField()
name = serializers.SerializerMethodField()
group = serializers.SerializerMethodField()
class Meta:
model = A
fields = ('id', 'name', 'group')
def id(self, obj):
if yourcondition(obj.group):
return obj.id
return another_value
...

Give a ModelSerializer, can I get related model fields used for QuerySet.only?

I found my drf view is too slow due to Fat SQL SELECT Clause from DB.
So I want to select columns only required by bound serializer, thus I can write my view class like this:
class ProductSerializer(serializers.ModelSerializer):
company_id = serializers.IntegerField(source='company_id')
company_name = serializers.serializers.CharField(source='company.name')
class Meta:
fields = (
'id', 'name', 'company_id', 'company_name'
)
class ListRetrieveProductViewSet(UpdateNonExistentMixin, viewsets.ReadOnlyModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
def get_queryset(self):
return self.queryset.select_related(
'company'
).only(*self.serializer_class.get_model_field_names())
self.serializer_class.get_model_field_names() may result ['id', 'name', 'company_id', 'company__name'].
How to implement get_model_field_names or is there any existing implementations?
I'll give this another go. There is no automatic way (barring 3rd party packages) to do the company_name to company__name conversion. What you can do is create another variable under Meta:
class Meta:
fields = (
'id', 'name', 'company_id', 'company_name'
)
fields_for_query = (
'id', 'name', 'company_id', 'company__name'
)
then fetch it like so:
list(self.get_serializer_class().Meta.fields_for_query)
This approach makes it a bit more manageable since they're beside each other. Typically, you'd turn the fetch method into a mixin like so:
class FieldForQueryMixin(object):
def get_field_for_query(self):
assert hasattr(self.get_serializer_class().Meta, 'fields_for_query'), 'Serializer Meta is missing fields_for_query field yo.'
return list(self.get_serializer_class().Meta.fields_for_query)
# use like so
class ListRetrieveProductViewSet(UpdateNonExistentMixin, FieldForQueryMixin, viewsets.ReadOnlyModelViewSet):
pass

Django Rest Framework - image url on reverse foreignkey

I am trying to access the url of an ImageField on a model related through a reverse-ForeignKey. I have attempted various possible options based on examples in the docs, but have had no luck. Any help would be appreciated.
models.py
class Car(models.Model):
name = models.CharField(... )
#property
def default_image(self):
... ...
return image # <=== returns from the CarImage model
class CarImage(models.Model):
car = models.ForeignKey(Car) # <=== no related_name set, but technically we could use carimage_set
image = models.ImageField(... ...)
serializers.py (attempt)
class CarSerializer(serializers.ModelSerializer):
... ...
image = fields.SerializerMethodField('get_image')
class Meta:
mode = Car
def get_image(self, obj):
return '%s' % obj.default_image.url
exception
'SortedDictWithMetadata' object has no attribute 'default_image'
The new DRF 2.3 seems to be helpful with reverse relationships and has solved my issues.
DRF 2.3 Announcement
For example, in REST framework 2.2, reverse relationships needed to be included explicitly on a serializer class.
class BlogSerializer(serializers.ModelSerializer):
comments = serializers.PrimaryKeyRelatedField(many=True)
class Meta:
model = Blog
fields = ('id', 'title', 'created', 'comments')
As of 2.3, you can simply include the field name, and the appropriate serializer field will automatically be used for the relationship.
class BlogSerializer(serializers.ModelSerializer):
"""Don't need to specify the 'comments' field explicitly anymore."""
class Meta:
model = Blog
fields = ('id', 'title', 'created', 'comments')