Nested serializer representation for PrimaryKeyRelatedField - python-3.x

I want to add and remove products to an order by only using their ids, but I want the order representation to look like a nested serializer.
My Models:
class Product(models.Model):
title = models.CharField(max_length=200)
price = models.DecimalField(max_digits=1_000, decimal_places=2)
def __str__(self):
return self.title
class Order(models.Model):
date = models.DateField()
products = models.ManyToManyField(Product, blank=True, related_name='orders')
My Serializers:
class ProductSerializer(serializers.ModelSerializer):
price = serializers.DecimalField(max_digits=1_000,
decimal_places=2,
coerce_to_string=False)
class Meta:
model = Product
fields = ['id', 'title', 'price']
class OrderSerializer(serializers.ModelSerializer):
products = serializers.PrimaryKeyRelatedField(queryset=Product.objects.all(), many=True)
class Meta:
model = Order
fields = ['id', 'date', 'products']
I'm trying to get the below representation:
{
"id": 1,
"date": "2021-08-12",
"products": [
{
"id": 1,
"title": "Item 1",
"price": 19.99
},
{
"id": 3,
"title": "Item 3",
"price": 49.99
}
]
}
However, if I want to create the above order the json should look like this:
{
"date": "2021-08-12",
"products":[1, 3]
}
And if I want to add a product with id==2 to the above order it should look like this:
{
"id": 1,
"date": "2021-08-12",
"products":[1, 3, 2]
}
I've tried overriding the to_representation() method and adding a nested serializer there, but I have no idea how to go about it. Should it look something like this, or am I going in a completely wrong direction here?
def to_representation(self, instance):
data = super().to_representation(instance)
data['products'] = ProductSerializer(data=instance['products'])
return data

Ended up fixing it with the to_representation() method. I don't know if this is the best way to do it, but it works for now.
def to_representation(self, instance):
data = super().to_representation(instance)
products_list = []
for product_id in data['products']:
product = instance.products.get(pk=product_id)
products_list.append(
{
'id': product.id,
'title': product.title,
'price': product.price
}
)
data['products'] = products_list
return data

use Nested Serializer Relationships.
and your OrderSerializer serializer class will be like this:
class OrderSerializer(serializers.ModelSerializer):
products = ProductSerializer(many=True)
class Meta:
model = Order
fields = ['id', 'date', 'products']

Related

Print the output in my desired format using nested serializers?

I am trying to send messages using django-rest framework.I have created serializers for both user and message.I am using django default user model for storing user's details.
I am pasting my model and serializers here:
class MessageModel(models.Model):
message = models.CharField(max_length=100)
created_at = models.DateTimeField(default=now)
updated_at = models.DateTimeField(default=now)
user = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user')
class UserSerializer(ModelSerializer):
class Meta:
model = get_user_model()
fields = ['id','username','email']
class MessageSerializer(ModelSerializer):
created_by = UserSerializer(read_only=True)
class Meta:
model = MessageModel
fields = ['id','message', 'created_at', 'updated_at','user','created_by']
View :
class MessageViewSet(viewsets.ModelViewSet):
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
queryset = MessageModel.objects.all()
serializer_class = MessageSerializer
After creating message,I am getting response like below:
{
"id": 6,
"message": "Lorem ipsum",
"created_at": "2022-11-07T09:21:19.492219Z",
"updated_at": "2022-11-07T09:21:19.492237Z",
"user": 2
}
but my output should looks like in below format,everytime when a new message is created :
{
"id": 102,
"message": "Lorem ipsum",
"created_at": "created time in UTC",
"updated_at": "last updated time in UTC",
"created_by": {
"id": 3,
"username": "testuser",
"email": "test#mail.com",
}
}
In case of any error, return the error message.
The name of the field is user, not created_by, so:
class MessageSerializer(ModelSerializer):
user = UserSerializer(read_only=True)
class Meta:
model = MessageModel
fields = ['id', 'message', 'created_at', 'updated_at', 'user']
or you can specify user as source:
class MessageSerializer(ModelSerializer):
created_by = UserSerializer(read_only=True, source='user')
class Meta:
model = MessageModel
fields = ['id', 'message', 'created_at', 'updated_at', 'created_by']
Note: Models normally have no …Model suffix. Therefore it might be better to rename MessageModel to Message.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the User model to the MessageModel
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the user relation to messages.

How to resolve multiple querysets in graphene.Union?

I want to create a graphene.Union type of multiple existing types. I am able to resolve it but it is not in my required format.
Schema
from graphene_django import DjangoObjectType
class ageType(DjangoObjectType):
class Meta:
model = age
class ethnicityType(DjangoObjectType):
class Meta:
model = ethnicity
class combinedType(graphene.Union):
class Meta:
types = (ageType, ethnicityType)
class Query(graphene.ObjectType):
defaultPicker = graphene.List(combinedType)
def resolve_defaultPicker(self, info):
items = []
age_q = age.objects.all()
items.extend(age_q)
ethnicity_q = ethnicity.objects.all()
items.extend(ethnicity_q)
return items
The query I am using in the graphql admin:
{
defaultPicker{
... on ageType{
id
age
}
... on ethnicityType{
id
ethnicity
}
}
}
I want to get a output like this:
{
"data": {
"defaultPicker": [
'ageType': [{
"id": "2",
"ethnicity": "American",
"ethnicityFr": "Test"
}],
'ethnicityType': [{
"id": "1",
"familyPlans": "3 kids",
"familyPlansFr": "3 enfants"
}],
]
}
}
I tried many things but couldn't find a way to resolve it.

Scalarize a nested field using marshmallow

I have two Database Models like this
class Setting(db.Model):
id = db.Column(db.Integer, id=True)
container_id = ST_db.Column(ST_db.Integer, db.ForeignKey('settings_container.id'))
setting_name = ST_db.Column(ST_db.String(50))
setting_value = ST_db.Column(ST_db.String(50))
class SettingsContainer(db.Model):
id = db.Column(db.Integer, id=True)
settings = db.relationship('Settings', backref='container', cascade='all, delete')
When I serialize the model SettingsContainer using the following schemas
class Setting(ma.Schema):
class Meta:
fields = (
'setting_name',
'setting_value',
)
class SettingsContainer(ma.Schema):
settings = ST_ma.Nested(Setting, many=True)
class Meta:
fields = (
'id',
'settings'
)
What I get is
[
{
"id": 1
"settings": [
{
"setting_name": 'name1',
"setting_value": 'value1'
},
{
"setting_name": 'name2',
"setting_value": 'value2'
},
}
"id": 2
"settings": [
{
"setting_name": 'name3',
"setting_value": 'value3'
},
{
"setting_name": 'name4',
"setting_value": 'value4'
},
]
But I want to get data in the format of
[
{
"id": 1
"settings": [['name1', 'value1'], ['name2', 'value2']]
}
{
"id": 2
"settings": [['name3', 'value3'], ['name4', 'value4']]
}
]
I want to able to extract the Nested fields, I don't understand how to do it using the ma.Function method, thanks a lot to everyone in advance.
Okay so I found out the answer, what I have to do is to use ma.Function with ma.Pluck
The appropriate serializers are.
class Setting(ma.Schema):
complete_setting = ma.Function(lambda setting:(
setting.setting_name,
setting.setting_value,
))
class SettingsContainer(ma.Schema):
settings = ma.List(ma.Pluck(Setting, 'complete_setting'))
class Meta:
fields = (
'id',
'settings'
)

DRF Serializer not displaying fields (Foreign Key and many2many) in default HTML form page API. Field is available in GET

DRF Serializer contains a group and inventory field which are many2many and foreign key. It is missing in default DRF HTML Form but available in GET view. currently, the depth field is enabled in Serializer. If i am removing depth then Foreign key is available in default HTML form, but still group many2many field is missing. I need both the fields for POST call or in DRF HTML Form.
Do i have to write some create method, but I do not want to create new record for Foreign key and many2many just want to utilize the existing field.
My Serializer class.
class MainHostSerializer(serializers.ModelSerializer):
class Meta:
model = MainHost
fields = (
'host_id',
'host_name',
'inventory',
'group'
)
# depth = 2
Raw view for default DRF HTML Form
{
"host_id": null,
"host_name": ""
}
Model Class
class MainHost(models.Model):
host_id = models.IntegerField(verbose_name='HOST ID', primary_key=True)
host_name = models.CharField(verbose_name='HOST NAME', max_length=512)
inventory = models.ForeignKey(related_name='inv_ins', on_delete=models.SET_NULL, to='hosts.MainInventory', blank=True, null=True)
group = models.ManyToManyField(MainGroup, related_name='hostgroups', through ='HostGroup')
Create Method for MainHost Serializer
def create(self, validated_data):
inv_data = validated_data.pop('inventory')
inv_res = MainInventory.objects.create(**inv_data)
group_data = validated_data.pop('group')
host_data = MainHost.objects.create(inventory = inv_res, **validated_data)
for g_data in group_data:
inv_data = g_data.pop('inv_id')
inv = MainInventory.objects.create(**inv_data)
group_res = MainGroup.objects.create(inv_id = inv, **g_data)
print(validated_data)
HostGroup.objects.create(host = host_data, group = group_res)
This was sample JSON
{
"count": 1692,
"next": "http://127.0.0.1:8000/api/mainhost/?page=2",
"previous": null,
"results": [
{
"host_id": 4087,
"host_name": "10.240.144.2",
"inventory": {
"inv_id": 91,
"inv_name": "GNS Switches (TestNet)",
"total_hosts": 539,
"total_groups": 1,
"org_name": "ABC_TestNet",
"description": "Inventory of ABC switches on Testnet",
"inv_variables": "environ: testnet"
},
"group": [
{
"group_id": 280,
"group_name": "aruba",
"total_hosts": 539,
"total_groups": 0,
"inv_id": {
"inv_id": 91,
"inv_name": "ABC Switches (TestNet)",
"total_hosts": 539,
"total_groups": 1,
"org_name": "ABC_TestNet",
"description": "Inventory of ABC switches on Testnet",
"inv_variables": "environ: testnet"
},
"description": "imported",
"group_variables": "{}",
"groupinv_name": "ABC Switches (TestNet)",
"groupinv_description": "",
"groupinv_source": "scm",
"groupinv_path": "TEC/GNS/Switches/testnet.ini"
}
],
"description": "imported",
"foreman_group": "[{'id': 280, 'name': 'aruba'}]",
"host_variables": "{}",
"ansible_facts": "{}"
}
]
}

flask-marshmallow custom fields

I use flask-marshmallow and mongoengine.
Also flask-restplus for my API server.
Here is my api.py
class BoardSchema(ma.Schema):
class Meta:
fields = ('no', 'title', 'body', 'tags', 'created_at', 'views')
board_schema = BoardSchema()
boards_schema = BoardSchema(many=True)
class ArticleList(Resource):
def get(self):
articles = Board.objects.all()
return boards_schema.jsonify(articles)
model.py
from datetime import datetime
from mongoengine import *
from config import DB_NAME
connect(DB_NAME)
class Board(Document):
d = datetime.now()
date = "{}-{}-{}".format(d.year, d.month, d.day)
no = SequenceField()
title = StringField(required=True)
body = StringField(required=True)
tags = ListField(StringField())
likes = ListField(StringField())
views = ListField(StringField())
password = StringField(required=True)
created_at = DateTimeField(default=date)
updated_at = DateTimeField(default=date)
When I access to /article, it's result like this ->
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": [
"127.0.0.1"
]
}
in "views", ip will be added who read article.
But I want to count of all the list of views and include it to my result.
The result I wanted is here.
{
"body": "123",
"created_at": "2018-08-20T00:00:00+00:00",
"no": 1,
"tags": [
"MySQL",
"C"
],
"title": "\ud14c\uc2a4\ud2b8",
"views": 20
}
I'm new at flask-marshmallow so I'm so confused how can I solve this issue.
Thanks.
Maybe you can try like this:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.method(deserialize="_custom_serializer")
def _custom_serializer(self, obj):
return len(obj.views)
Create instance of your custom schema:
custom_board_schema = BoardSchemaCustom()
and dump it:
dump, errors = custom_board_schema.schema.dump(Board.query.first())
>>> dump
i've got the same problem. and my code works after installing marshmallow-sqlalchemy
pip install marshmallow-sqlalchemy
see from offical documentation
https://flask-marshmallow.readthedocs.io/en/latest/
Below snippet would also work:
class BoardSchemaCustom(ma.ModelSchema):
class Meta:
model = Board
views = ma.fields.Function(lambda obj: len(obj.views))

Resources