DRF Serialize fields from both directions of foreign key? - django

Let's say I have three models:
class ThingOne:
field1 = ...
field2 = ...
class ThingTwo:
thingone = models.ForeignKey("ThingOne")
field3 = ...
field4 = ...
class ThingTree:
thingtwo = models.ForeignKey("ThingTwo")
field5 = ...
field6 = ...
Let's also say I've made top level ViewSets and Serializers for the above. Easy peasy.
Now I'd like to create a custom endpoint (detail_route) that is based on a subset of ThingTwo and includes corresponding fields from ThingOne and ThingThree. I'll use a custom serializer to pack my data:
class MyComboThingSerializer(ModelSerializer):
field1 = serializers.SerializerMethodField()
field5 = serializers.SerializerMethodField()
def get_field1(self, obj):
return ?
def get_field5(self, obj):
return ?
class Meta:
model = ThingTwo
fields = "__all__"
What would I put into either return statement to achieve the values I'm looking for?

Something like
class ThingTwoSerializer(ModelSerializer):
thing1 = ThingOneSerializer()
thingthree_set = ThingThreeSerializer(many=True)
class Meta:
model = ThingTwo
fields = ['id', 'thing1', 'thingthree_set', 'field3', 'field4']

Related

DRF: only for representation purpose. How to combine two serializers

I have
class Serializer1(serializers.ModelSerializer):
class Meta:
model = Model1
fields = ("first_name","last_name")
class Serializer2(serializers.ModelSerializer):
class Meta:
model = Model2
fields = ("phone","email")
Now I want to show both the serializers as one only for representation purpose
like
{
first_name:
last_name:
phone:
email:
}
How can do that
One quick way to do this is unpack both serializer.data in a dict like:
s1 = Serializer1(obj)
s2 = Serializer2(obj)
combined = {**s1.data, **s2.data}
First, define the second serializer and then the first
class serializer2(serializers.ModelSerializer):
class Meta:
model = model2
fields = '__all__'
class serializer1(serializers.ModelSerializer):
s2 = serializer2()
class Meta:
model = model1
fields = '__all__'
then create a view using serializer1.

How to fetch parent child hierarchy using django rest-framework

I am new to Django rest-framework. I am writing an API to fetch details in a parent-child hierarchy. Following is my code;
models.py
class ConfigAttributes(models.Model):
attr_set_name = models.CharField(max_length=32)
product_type = models.CharField(max_length=32)
class ProductInfo(models.Model):
config_attr = models.ForeignKey(ConfigAttributes, on_delete=models.CASCADE)
product_name = models.CharField(max_length=32)
class AttributeDetails(models.Model):
product_info = models.ForeignKey(ProductInfo, on_delete=models.CASCADE)
attribute_name = models.CharField(max_length=32)
serializers.py
class ConfigAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = ConfigAttributes
fields = ['id', 'attr_set_name', 'product_type']
class ProductInfoSerializer(serializers.ModelSerializer):
class Meta:
model = ProductInfo
fields = ['id', 'product_name', 'config_attr_id']
class AttributeDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = AttributeDetails
fields = ['id', 'attribute_name', 'product_info_id']
views.py
class ConfigAttributesViewSet(viewsets.ModelViewSet):
queryset = ConfigAttributes.objects.all()
serializer_class = ConfigAttributesSerializer
class ProductInfoViewSet(viewsets.ModelViewSet):
queryset = ProductInfo.objects.all()
serializer_class = ProductInfoSerializer
class AttributeDetailsViewSet(viewsets.ModelViewSet):
queryset = AttributeDetails.objects.all()
serializer_class = AttributeDetailsSerializer
and app/urls.py
router = routers.DefaultRouter()
router.register('config', ConfigAttributesViewSet)
router.register('product', ProductInfoViewSet)
router.register('attr', AttributeDetailsViewSet)
urlpatterns = [
path('', include(router.urls)),
]
When I call the API, my required hierarchy and output is;
[
{
"attr_set_name" : "abc",
"product_type" : "efg",
"product_info" : {
"product_name" : "hij",
"attribute_details" : {
"attribute_name" : "klm"
}
}
}
]
What are the changes need to done in the files to get the above output in hierarchy (I am using Postman to check my APIs). Thank you for the help.
You can nest your serializers. To match your general API hierarchy this is as close as you can get. Unfortunately, the relationship fields will be lists.
class AttributeDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = AttributeDetails
fields = ['id', 'attribute_name', 'product_info_id']
class ProductInfoSerializer(serializers.ModelSerializer):
attribute_details = AttributeDetailsSerializer(many=True)
class Meta:
model = ProductInfo
fields = ['id', 'product_name', 'config_attr_id', 'attribute_details']
class ConfigAttributesSerializer(serializers.ModelSerializer):
product_infos = ProductInfoSerializer(many=True)
class Meta:
model = ConfigAttributes
fields = ['id', 'attr_set_name', 'product_type', 'product_infos']
You can get the specific parent elements if you start with the AttributeDetails instance though:
class ConfigAttributesSerializer(serializers.ModelSerializer):
class Meta:
model = ConfigAttributes
fields = ['id', 'attr_set_name', 'product_type',]
class ProductInfoSerializer(serializers.ModelSerializer):
config_attribute = ConfigAttributesSerializer(many=False)
class Meta:
model = ProductInfo
fields = ['id', 'product_name', 'config_attr_id', 'config_attribute']
class AttributeDetailsSerializer(serializers.ModelSerializer):
product_info = ProductInfoSerializer(many=False)
class Meta:
model = AttributeDetails
fields = ['id', 'attribute_name', 'product_info_id', 'product_info']

django-polymorphic-tree serializer

I would like to serialize all the nodes in my PolymorphicMPTTModel with their corresponding fields. Following the documentation django-polymorphic and django-mptt i get this:
{
"count":1,
"next":null,
"previous":null,
"results":[
{
"title":"Submenu",
"subcategories":[
{
"title":"Plato1",
"subcategories":[
]
},enter code here
{
"title":"Plato2",
"subcategories":[
]
}
]
}
]
}
The structure is fine, but the fields of the children are missing.
Models:
class Menu(PolymorphicMPTTModel):
parent = PolymorphicTreeForeignKey('self', blank=True, null=True, related_name='children', verbose_name='parent')
title = models.CharField("Title", max_length=200)
class SubMenu(Menu):
titulo = models.CharField("Titulo", max_length=200,default="not defined")
class Plato(Menu):
titulo = models.CharField("Titulo",max_length=200,default="not defined")
descripcion = models.TextField()
ingredientes = JSONField()
precio = models.PositiveSmallIntegerField(default=0)
# Extra settings:
can_have_children = False
Serializers:
class PlatoSerializer(serializers.ModelSerializer):
class Meta:
model = Plato
fields = ('titulo', 'descripcion', 'ingredientes', 'precio')
class SubMenuSerializer(serializers.ModelSerializer):
class Meta:
model = SubMenu
fields = ('titulo',)
class MenuItemModuleSerializer(serializers.ModelSerializer):
subcategories = serializers.ListSerializer(source="children",child=RecursiveField())
class Meta:
model = Menu
fields = ('title','subcategories')
View:
class MenuView(viewsets.ModelViewSet):
queryset = Menu.objects.all()
queryset = queryset.toplevel()
serializer_class = MenuItemModuleSerializer
I know I'm kinda late to the party but I had the same issue and finally found a solution that is working for me.
As django-rest-polymorphic states, you need a mapping between models and serializers:
class ProjectPolymorphicSerializer(PolymorphicSerializer):
model_serializer_mapping = {
Menu: MenuItemModuleSerializer,
SubMenu: SubMenuSerializer,
Plato: PlatoSerializer
}
Your RecursiveField() creates a serializer instance from its parent class. That makes all your child objects using the MenuItemModuleSerializer and thus missing child fields.
Every child needs to get mapped to its serializer using ProjectPolymorphicSerializer
RecursiveField(to='ProjectPolymorphicSerializer')
Change your MenuItemModuleSerializer to this:
class MenuItemModuleSerializer(serializers.ModelSerializer):
subcategories = serializers.ListSerializer(source="children", child=RecursiveField(to='ProjectPolymorphicSerializer'))
class Meta:
model = Menu
fields = ('title','subcategories')

serializer set custom values

I have a model serializer that is like so -
class VariableSerializer(serializers.ModelSerializer):
owner_name = serializers.Field(source='owner_id.owner_name')
class Meta:
model = Varmst
resource_name = 'varmst'
fields = ('varmst_id', 'varmst_type', 'varmst_name', 'varmst_value',
'varmst_desc')
'varmst_value' corresponds to an integer that, depending on that integer can mean a different thing. How do I return the normalized value OVER the integer?
ie.
if 'varmst_value' = 2 then I want the serializer to return 'varmst_type': 'email'
if 'varmst_value' = 3 then I want the serializer to return 'varmst_type': 'website'
i think the following solution can help you
class VariableSerializer(serializers.ModelSerializer):
owner_name = serializers.Field(source='owner_id.owner_name')
class Meta:
model = Varmst
resource_name = 'varmst'
fields = ('varmst_id', 'varmst_type', 'varmst_name', 'varmst_value',
'varmst_desc')
def transform_varmst_type(self, obj, value):
if obj.varmst_value == 2:
return "email"
if obj.varmst_value == 3:
return "Website"

Accessing model subclass attributes with django tastypie

I have a model that is sublcassed
class Foo(models.Model):
name = models.CharField(max_length=255)
child_model = models.CharField(max_length=255)
class Bar(Foo):
woots = models.ManyToManyField(Woot)
class Woot(models.Model):
color = models.CharField(max_length=255)
And some tastypie resources:
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all()
class BarResource(ModelResource):
class Meta:
queryset = Foo.objects.all()
class WootResource(ModelResource):
class Meta:
queryset = Woot.objects.all()
When accessing the FooResource, I'd ideally like it to 'reroute' and return a BarResource instead if the child_model attribute == bar.
If I can't do that, then any way that I can get an array of woots serialized and into the bundle (when child_model attribute == bar) would be fine by me.
Try this:
class FooResource(ModelResource):
class Meta:
queryset = Foo.objects.all()
def dehydrate(self, bundle):
if bundle.data['child_model']=="bar":
return json.loads(BarResource().dispatch_list(bundle.request).content)
return bundle