I wanna do something like below, but the code as it is inefficient,
How can I return paginated response of related object?
class Bar(models.Model):
pass
class Foo(models.Model):
bar = models.ForeignKey('bar')
foo_id = request.data.get('foo_id')
foos = Foo.objects.get(id=foo_id)
bars = [
foo.bar
in
foo
for
foos
]
page = self.paginate_queryset(bars)
serializer = BarSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
Actually I found an answer here https://stackoverflow.com/a/37657376/433570
Idea is you flatten the nested object
class FooSerializer():
bar = BarSerializer()
def to_representation(self, obj):
"""Move fields from profile to user representation."""
representation = super().to_representation(obj)
profile_representation = representation.pop('bar')
for key in profile_representation:
representation[key] = profile_representation[key]
return representation
Related
I'm wanting to know how you would pass nested data to a ModelSerializer if the child of the nested data is not a model on its own.
The data that I'm working with looks like this:
{
'leadId': 12345,
'updateTime': 1651250096821,
'changeInfo': {
'oldstage': 'New Leads',
'newstage': 'Attempting Contact'
}
}
From previous experience, I know that if I was only working with the leadId and the updateTime, my serializer would look like this:
class LogSerializer(serializers.ModelSerializer):
leadId = serializers.IntegerField(source="lead_id")
updateTime = serializers.IntegerField(source="update_time")
class Meta:
model = Log
fields = ["leadId", "updateTime"]
Which would then make it possible to do this:
data = {
'leadId': 12345,
'updateTime': 1651250096821
}
serializer = LogSerializer(data=data)
serializer.is_valid()
serializer.save()
If I'm not wanting to turn changeInfo into its own model, is it possible to map the fields to the nested data? Something that might look like this (but this obviously doesn't work):
class LogSerializer(serializers.ModelSerializer):
leadId = serializers.IntegerField(source="lead_id")
updateTime = serializers.IntegerField(source="update_time")
oldstage = serializers.IntegerField(source="oldstage")
newstage = serializers.IntegerField(source="newstage")
class Meta:
model = Log
fields = ["leadId", "updateTime", "oldstage", "newstage]
You can use a custom serializer for your changeInfo field (you don't need to create a model for that):
class ChangeInfoSerializer(serializers.Serializer):
oldstage = serializers.CharField(max_length=100, source="old_stage") # Set max_length to a value that suits your needs
newstage = serializers.CharField(max_length=100, source="new_stage")
def create(self, validated_data):
pass
def update(self, instance, validated_data):
pass
class LogSerializer(serializers.ModelSerializer):
leadId = serializers.IntegerField(source="lead_id")
updateTime = serializers.IntegerField(source="update_time")
changeInfo = ChangeInfoSerializer(required=False) # Change to required=True if you want this field to be mandatory
class Meta:
model = Log
fields = ["leadId", "updateTime", "changeInfo"]
def create(self, validated_data):
change_info = validated_data.pop('changeInfo')
for key, value in change_info.items():
if key == "old_stage":
validated_data['old_stage'] = value
elif key == "new_stage":
validated_data['new_stage'] = value
log = Log.objects.create(**validated_data)
return log
def update(self, instance, validated_data):
change_info = validated_data.pop('changeInfo')
instance.lead_id = validated_data.get('leadId', instance.lead_id)
instance.update_time = validated_data.get('updateTime', instance.update_time)
# Here you can use change_info['oldstage'] and change_info['newstage'] if 'changeInfo' is sent (otherwise you'll get a KeyError)
instance.save()
return instance
As mentioned in the comments, a SerializerMethodfield is a good way to go:
serializers.py
class LogSerializer(...):
...
changeInfo = serializers.SerializerMethodField()
def get_changeInfo(self, obj): return {
"leadId" : obj.lead_id,
"updateTime": obj.update_time
}
class Meta:
fields = ["changeInfo", ...]
...
This is just a layout of my serializer
class SerializerA(serializers.ModelSerializer):
field_a = serializers.SerializerMethodField()
field_b = serializers.SerializerMethodField()
field_c = serializers.SerializerMethodField()
field_d = serializers.SerializerMethodField()
class Meta:
model = TestModel
fields = ("field_a", "field_b", "field_c", "field_d", "name", "designation")
def get_field_a(self, obj):
temp = TempUserModel.objects.get(pk=obj.uuid)
field_a = "some ORM query related to temp and TestModel model"
return field_a
def get_field_b(self, obj):
temp = TempUserModel.objects.get(pk=obj.uuid)
field_b = "some ORM query related to temp and TestModel model"
return field_b
def get_field_c(self, obj):
temp = TempUserModel.objects.get(pk=obj.uuid)
field_c = "some ORM query related to temp and TestModel model"
return field_c
def get_field_d(self, obj):
field_d = "some ORM query related to TestModel model"
return field_d
Here temp = TempuserModel.objects.get(pk=obj.uuid) is same in 3 out of 4 of the class methods. So is there any way that I can define it once somewhere in a serializer loop and use it without doing the same ORM query again and again?
Need help , i am trying to push nested relations inside DB don't know where I am going wrong in this, is there something wrong with validated_data , which is a list of dict here , thanks in advance
class CatalogSerializer(serializers.ModelSerializer):
catalog_products = CatalogProductsSerializer(source = 'catalogproducts_set',many=True)
class Meta:
model = Catalog
fields = ['created_by','client','catalog_products','created_datetime','is_active']
def create(self,validate_data):
client_id = validate_data.pop('id')
client = User.objects.get(id=client_id),
catalog_obj = Catalog.objects.create(
client = client,
created_by = self.context['user'],
is_active =True,
)
for pricelist_ins in validate_data:
CatalogProducts.objects.create(
catalog = catalog_obj,**pricelist_ins)
return catalog_obj
Basic Viewset
class CatalogViewset(viewsets.ModelViewSet):
queryset = Catalog.objects.all()
serializer_class = CatalogSerializer
permission_classes = []
authentication_classes = []
def create(self, request, *args, **kwargs):
if request.data:
try:
serialized_data = self.get_serializer(data = request.data)
if serialized_data.is_valid(raise_exception=True):
serialized_data.save()
return Response(serialized_data.data,status=200)
except Exception as e:
return Response({'error':str(e)},status=400)
return Response({'status':'invalid request'},status=400)
the error I am getting in Postman
{
"error": "{'catalog_products': [ErrorDetail(string='This field is required.', code='required')]}"
}
data i am posting
{
"id":"2",
"pricing_list":[
{
"from_quantity":"101",
"to_quantiy":"34",
"price":"1000"
},
{
"from_quantity":"10",
"to_quantiy":"501",
"price":"2000"
}
]
}
You have catelogue_products in the fields, it is by default required. But you are not posting any catelogue_products. You need to post data based on the fields of the serializer. validated data will not contain any other data, but valid data that was set in serializer.
To make it optional you may try to add required=False in the serialzier like this:
class CatalogSerializer(serializers.ModelSerializer):
catalog_products = CatalogProductsSerializer(source = 'catalogproducts_set',many=True, required=False)
class Meta:
model = Catalog
fields = ['created_by','client','catalog_products','created_datetime','is_active']
I am new in Django and I would like to overwrite the field value in create and update method of serializer. Here is my model=>
class Holiday(models.Model):
HolidayDay = models.DateField()
Created_DT = models.DateTimeField()
Created_Usr = models.CharField(max_length=20)
LastModified_Usr = models.CharField(max_length=20,blank=True)
LastModified_DT = models.DateTimeField(blank=True,null=True)
def __str__(self):
return str(self.HolidayDay)
Here is my serializer=>
class HolidaySerializer(serializers.ModelSerializer):
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]
def create(self,validated_data):
validated_data['Created_Usr'] ="Testing"
return Holiday.objects.create(**validated_data)
I would like to update Create_usr field value in create method and LastModified_usr field in update method. But why I can't overwrite the create_usr field as "Testing"?
Here is my views=>
def post(self,request):
holiday = request.data.get('holiday')
serializer = HolidaySerializer(data=holiday)
serializer.is_valid()
print(serializer.errors)
if serializer.is_valid():
holiday_saved=serializer.save()
return Response({"success":"Holiday '{}' created successfully".format(holiday_saved.HolidayDay)})
def put(self,request,pk):
save_holiday = get_object_or_404(Holiday.objects.all(),pk=pk)
data = request.data.get('holiday')
serializer = HolidaySerializer(instance=save_holiday,data=data,partial=True)
if serializer.is_valid(raise_exception = True):
holiday_saved=serializer.save()
return Response({"sucess": "Holiday '{}' updated successfully".format(holiday_saved.HolidayDay)})
Your create method is not defined in your Serializer class, instead it is part of your Meta class. You should be able to solve it by moving your create method to your HolidaySerializer:
class HolidaySerializer(serializers.ModelSerializer):
def create(self,validated_data):
validated_data['Created_Usr'] = "Testing"
return Holiday.objects.create(**validated_data)
class Meta:
model=Holiday
fields = [
'id',
'HolidayDay',
'Created_DT',
'Created_Usr',
'LastModified_Usr',
'LastModified_DT'
]
serializer
class CarrGetOrderDetails(serializers.Serializer):
order = CarrGetOrderDetail(required=False)
template = CarrGetOrderDetailTemplate(required=False, many=True)
extra_fields = CarrGetOrderDetailExtraFields(required=False, many=True)
View File
class CarrierOrderDetails(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
order_id = request.GET['order_id']
#order_obj = Order.objects.filter(id=order_id)
obj = self.get_objects(order_id)
#print('#####',obj)
serializ = CarrGetOrderDetails(obj, many=True)
return Response(serializ.data)
def get_objects(self, order_obj):
model1 = Order.objects.filter(id=order_obj)
model2 = OrderTemplate.objects.filter(id=1)
model3 = OrderExtraField.objects.filter(id=1)
obj = {'order': model1, 'template': model2, 'extra_fields': model3}
return obj
here i'm try to hit multiple serializer objects but got null data in serializer.data
Output
[
{},
{},
{}
]
You are initializing your serializer with many=True, so it is expecting a list of objects. You should either pass in a list of objects as the first argument to the serializer class constructor or remove the many=True kwarg.
Also, since you are passing in a dictionary, I would use the data={...} kwarg instead of the first argument which is instance.
As a quick test, you can perform the following modification:
obj = self.get_objects(order_id)
serializ = CarrGetOrderDetails(obj, many = True)
obj = self.get_objects(order_id)
serializ = CarrGetOrderDetails(data=[obj], many = True)
References
DRF Serializer Documentation
serializers.py:BaseSerializer