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

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.

Related

django rest_framework how to display nested relationship

I'm trying to display foreign related fields like this example and it works
{
"reqid": 10,
"reqdate": "2022-12-05",
"reqdescription": "Aircon Not working",
"officerequestor": "OVCAA ",
"officeid": "PPD ",
"inspection": {
"insdate": "2022-12-06",
"diagnosis": "need to buy prism",
"inspector": "EMP-322 "
}
},
this is my serializers.py
class RequestAdditionDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = Inspection
fields = ['insdate',
'diagnosis',
'inspector'
]
class RequestorSerializer(serializers.ModelSerializer):
inspection = RequestAdditionDetailsSerializer(read_only=True)
class Meta:
model = Request
fields = ['reqid',
'reqdate',
'reqdescription',
'officerequestor',
'officeid',
'inspection'
]
My question is can I do this the other way around like this
{
"inspectid": 5,
"reqid": "10",
"insdate": "2022-12-06",
"diagnosis": "need to buy prism",
"inspector": "EMP-322",
"isinspected": {
"reqdescription": "Aircon Not working",
"reqdate": "2022-12-05",
"officerequestor": "OVCAA"
}
},
this is what I've tried, tbh I don't think this will work is there a solution for this.
if no maybe i'll add additional columns on inspection like reqdescription,reqdate etc.. just to show them
class InspectionAdditionalDetailsViewSerializer(serializers.ModelSerializer):
class Meta:
model = Request
fields = ['reqdescription',
'reqdate',
'officerequestor'
]
class InspectionSerializer(serializers.ModelSerializer):
request_details = InspectionAdditionalDetailsViewSerializer(read_only=True)
class Meta:
model = Inspection
fields = ['inspectid',
'reqid',
'insdate',
'diagnosis',
'inspector',
'isinspected',
'request_details'
]
this is my models.py
class Inspection(models.Model):
inspectid = models.AutoField(primary_key=True)
reqid = models.OneToOneField('Request', models.DO_NOTHING, db_column='reqid', blank=True, null=True)
class Meta:
managed = False
db_table = 'inspection'
class Request(models.Model):
reqid = models.AutoField(primary_key=True)
class Meta:
managed = False
db_table = 'request'
You have defined the OneToOne field name reqid therefore you should use it as serializer key.
Noted that Django will add _id to the field so it will become reqid_id in your database, it's best to name it req or request only to refer to related object.
class InspectionAdditionalDetailsViewSerializer(serializers.ModelSerializer):
class Meta:
model = Request
fields = [
'reqdescription',
'reqdate',
'officerequestor',
]
class InspectionSerializer(serializers.ModelSerializer):
reqid = InspectionAdditionalDetailsViewSerializer(read_only=True)
class Meta:
model = Inspection
fields = [
'inspectid',
'reqid',
'insdate',
'diagnosis',
'inspector',
'isinspected',
]

Django Serializer - Combine fields from two models

I have two models namely:
class B (models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=255)
class A (models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=255)
b = models.ForeignKey(B,on_delete=models.CASCADE,null=True)
I want a serializer which returns a response like this:
{
"id": 1,
"name": "test",
"b_id": 2,
"b_name": "test
}
the response format you want to get is not possible to my knowledge but you can use nestedSerializers to get both models in one response like
{
"id": 1,
"name": "test",
"b":{
"id":"test",
"name": "test"
}
}
if this is enough for you you can use the following code
serializers.py
class Bserializer(serializers.ModelSerializer):
class Meta:
model = B
fields = "__all__" #or only the fields you want
class Aserializer(serializers.ModelSerializer):
b = Bserializer()
class Meta:
model = B
fields = ["id","name", "b"]
Since b field in A model is a one to many relation so it should be a list of a items like this, also check docs
{
"b_id": 1,
"b_name": "test",
"a": [
'a_id1': 'a_name',
'a_id2': 'a_name'
]
}
If that is what you want you can do it like this:
class BSerializer(serializers.ModelSerializer):
a = serializers.StringRelatedField(many=True)
class Meta:
model = Album
fields = ['id', 'name', 'a']
This is directly from a project im working on now
class CoachLocationsSerializer(serializers.ModelSerializer):
base = LocationSerializer(read_only=True)
locations = LocationSerializer(read_only=True, many=True)
class Meta:
model = Coach
fields = ['base', 'locations']
You can achieve it without using nested serializers, like this:
class ASerializer(serializers.ModelSerializer):
b_name = serializers.CharField(source='b__name')
class Meta:
model = A
fields = ('id', 'name', 'b_id', 'b_name')
Also, note that you don't need to declare b_id in the serializer, since it's already a model field

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']

DRF Serialize fields from both directions of foreign key?

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']

Django. How to create django_easyselect_form with custom fieldset?

There is my model:
class MoneyTransfer(models.Model):
sender = models.ForeignKey(BankAccount, related_name='outcome_transfers')
receiver = models.ForeignKey(BankAccount, related_name='income_transfers')
when = models.DateTimeField()
total = models.FloatField()
comment = models.CharField(max_length=255)
objects = TransferQuerySet.as_manager()
form:
SendTransferForm = select2_modelform(MoneyTransfer)
I have two usages of form:
1. fields = 'receiver', 'comment', 'total'
2. fields = 'sender', 'receiver', 'comment', 'total'
How to implement it in code?
You can create two forms class
class FirstForm(ModelForm):
class META:
model = models.MoneyTransfer
fields = ['receiver', 'comment', 'total']
and then:
class SecondForm(ModelForm):
class META:
model = models.MoneyTransfer
fields = ['sender', 'receiver', 'comment', 'total']
I've decided not to use model form and created simple form with necessary widgets:
class SendTransferForm(forms.Form):
sender = forms.ModelChoiceField(queryset=BankAccount.objects.all(), widget=Select2(), label="Счёт отправителя")
receiver = forms.ModelChoiceField(queryset=BankAccount.objects.all(), widget=Select2(), label="Счёт получателя")
total = forms.CharField(widget=forms.TextInput(attrs={'class': 'select2fake'}), label="Сумма")
comment = forms.CharField(widget=forms.TextInput(attrs={'class': 'select2fake'}), label="Комментарий")