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

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": "{}"
}
]
}

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.

Nested serializer representation for PrimaryKeyRelatedField

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

How to assign ranking on objects based on a value in a queryset

I have a queryset that returns a list of menus that and I want to assign each Menu a rank based on the number of votes accumulated is there a way I can achieve this using django Query Expressions? I'm also open to any other suggestions.
The queryset is as follows:
qs = Menu.objects.filter(Q(created_at__date__iexact=todays_date)).order_by('-votes')
And the Menu class model is shown below:
class Menu(models.Model):
"""Represents menu class model"""
restaurant = models.ForeignKey(Restaurant,null=True,blank=True,on_delete=models.CASCADE)
file = models.FileField(upload_to='menus/')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
uploaded_by = models.CharField(max_length=50, null=True, blank=True)
votes = models.IntegerField(default=0)
After serializing the queryset returns the following data:
[
{
"id": 10,
"file": "https://res.cloudinary.com/dw9bllelz/raw/upload/v1/media/menus/index_ah660c.jpeg",
"restaurant": "Burger King",
"votes": 10,
"created_at": "2021-06-03T09:33:05.505482+03:00"
},
{
"id": 9,
"file": "https://res.cloudinary.com/dw9bllelz/raw/upload/v1/media/menus/index_ah660c.jpeg",
"restaurant": "Texas Plates",
"votes": 2,
"created_at": "2021-06-03T09:33:05.505482+03:00"
},
{
"id": 8,
"file": "https://res.cloudinary.com/dw9bllelz/raw/upload/v1/media/menus/index_ah660c.jpeg",
"restaurant": "Carlito Dishes",
"votes": 2,
"created_at": "2021-06-03T09:33:05.505482+03:00"
},
{
"id": 7,
"file": "https://res.cloudinary.com/dw9bllelz/raw/upload/v1/media/menus/index_ah660c.jpeg",
"restaurant": "Kram Dishes",
"votes": 1,
"created_at": "2021-06-03T09:33:05.505482+03:00"
},
]
You can either use the index of the object in the array as its index or you can use Window functions [Django docs] and use the Rank or DenseRank (Reference [Django docs]) function to compute the ranks as per your need:
from django.db.models import F, Window
from django.db.models.functions import Rank
qs = Menu.objects.filter(
Q(created_at__date__iexact=todays_date)
).annotate(
rank=Window(
expression=Rank(),
order_by=F('votes').desc(),
)
)
for menu in qs:
print(menu.rank, menu.votes)

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

Not able to pass last inserted_id from parent serializer to children serializer with Django restframework

I am trying to save parent (VisVisits) and children (VisVistiData) table with same api call. Data is coming into json format. I am able to save both the table except one column visit_id in the children (VisVisitData) table. visit_id does not come from the json. It is auto number type field in parent table(VisVisits) and I want to take visit_id from parent(after gets insert) table and save it to their children ids with other column in children table (VisVisitData)
I really need help to fix this issue.
Serializer
class VisVisitsSerializer(serializers.ModelSerializer):
data = VisVisitDataSerializer(many=True)
class Meta:
model = VisVisits
fields = ('visit_id','user','data')
read_only_fields = ['visit_id']
def create(self, validated_data):
visits_data = validated_data.pop('data')
visit = VisVisits.objects.create(**validated_data)
for visit_data in visits_data:
VisVisitData.objects.create(visit_id=visit.visit_id, **visit_data)
return visit
class VisVisitDataSerializer(serializers.ModelSerializer):
class Meta:
model = VisVisitData
fields = ('__all__')
model
class VisVisits(models.Model):
visit_id = models.IntegerField(primary_key=True,auto_created=True)
user = models.ForeignKey(UsrUsers, models.DO_NOTHING, blank=False, null=False)
def __str__(self):
return str(self.visit_id)
class VisVisitData(models.Model):
vdata_id = models.IntegerField(primary_key=True,auto_created=True)
visit = models.ForeignKey('VisVisits', models.DO_NOTHING, blank=True, null=True, related_name='data')
def __str__(self):
return str(self.vdata_id)
json
{
"user": "1",
"visits": [
{
"action": "i",
"local_id": "170",
"data": [
{
"Active": "1",
"LocalID": "1905",
"VisitDataID": "",
},
{
"Active": "1",
"LocalID": "1906",
"VisitDataID": "",
},
{
"Active": "1",
"LocalID": "1907",
"VisitDataID": "",
},
{
"Active": "1",
"LocalID": "1908",
"VisitDataID": "",
},
{
"Active": "1",
"LocalID": "1909",
"VisitDataID": "",
}
]
}

Resources