django drf about many-to-many RelatedField custom field - python

I use the latest version of The Django REST Framework,
and The table in model is many-to-many related
My current model code looks like this:
model.py
class LvsDeploy(models.Model):
MASTER = 'MASTER'
BACKUP = 'BACKUP'
ROLE_CHOICES = (
(MASTER, 'MASTER'),
(BACKUP, 'BACKUP')
)
id = models.AutoField(primary_key=True)
cluster_name = models.CharField(max_length=30)
dip = models.CharField(max_length=15, verbose_name='DR IP')
role = models.CharField(max_length=10, choices=ROLE_CHOICES, verbose_name="role")
class LoadBalance(models.Model):
id = models.AutoField(primary_key=True)
lb_name = models.CharField(max_length=30, verbose_name='load balance')
cluster = models.ManyToManyField('LvsDeploy',related_name='cluster')
vip = models.CharField(max_length=50, verbose_name='VIP')
port = models.IntegerField(verbose_name='port')
def __str__(self):
return self.lb_name
My serializer code looks like this:
serializers.py
class LvsDeployReadOnly(serializers.ModelSerializer):
class Meta:
model = LvsDeploy
fields = '__all__'
class LoadBalanceSerializer(serializers.ModelSerializer):
cluster_name = serializers.RelatedField(source='cluster.name',read_only=True)
cluster = LvsDeployReadOnly(many=True)
##rs = RSInfoSerializer(many=True, read_only=True)
class Meta:
model = LoadBalance
fields = '__all__'
##extra_fields = ['rs','cluster','cluster_name']
extra_fields = ['rs','cluster','cluster_name']
My views code:
class BalanceList(views.APIView):
def get(self, request):
queryset = LoadBalance.objects.all()
serializer = LoadBalanceSerializer(queryset, many=True)
print(serializer.data)
return Response(serializer.data)
and request actual output:
[
{
"id": 2,
"cluster_name": null,
"cluster": [
{
"id": 1,
"cluster_name": "lvs_sz01",
"dip": "1.1.1.6",
"role": "BACKUP",
},
{
"id": 2,
"cluster_name": "lvs_sz01",
"dip": "1.1.1.5",
"role": "BACKUP",
}
],
"lb_name": "lb001",
"vip": "1.1.1.1",
"port": 80,
}
]
But the cluster_name filed value is same in the dictionary of lists .
I want the output to look like this:
[
{
"id": 2,
"cluster_name": "lvs_sz01",
"cluster": [
{
"dip": "1.1.1.6",
"role": "BACKUP",
},
{
"dip": "1.1.1.5",
"role": "BACKUP",
}
],
"lb_name": "lb001",
"vip": "1.1.1.1",
"port": 80,
}
]
How should I change it? Can you help me ?

I solved it myself with the following method:
class LoadBalanceSerializer(serializers.ModelSerializer):
cluster_name = serializers.SerializerMethodField()
cluster = LvsDeployReadOnly(many=True, read_only=True)
rs = RSInfoSerializer(many=True, read_only=True)
class Meta:
model = LoadBalance
fields = '__all__'
extra_fields = ['rs','cluster','cluster_name']
def get_cluster_name(self,obj):
print(type(obj.lb_name))
lvs_cluster = obj.cluster.all()
cluster_name = [ i.cluster_name for i in lvs_cluster ][0]
print(cluster_name)
return cluster_name
Thanks!

Related

How can I serialize one to many models in Django Rest?

So I have two models Invoice and Items. Invoices can have many Items. Here's how they are defined:
class Invoice(models.Model):
customer_name = models.CharField(max_length = 200, verbose_name='Customer Name')
customer_phone = models.IntegerField(null = True, blank = True, verbose_name='Customer Phone')
customer_address = models.TextField(null = True, blank = True, verbose_name='Customer Address')
invoice_id = models.UUIDField(primary_key = True, unique = True, default=uuid.uuid4, verbose_name='Invoice ID')
invoice_date = models.DateField(auto_now_add=True, verbose_name='Invoice Date')
def __str__(self):
return self.customer_name + ' - ' + str(self.invoice_id)
class Meta:
verbose_name = 'Invoice'
verbose_name_plural = 'Invoices'
class Items(models.Model):
invoice = models.ForeignKey(Invoice, on_delete = models.CASCADE, related_name='invoice')
item_name = models.CharField(max_length = 200, null = False)
item_quantity = models.IntegerField()
item_price = models.IntegerField()
item_id = models.AutoField(primary_key=True)
def __str__(self):
return self.item_name
class Meta:
verbose_name = 'Items'
verbose_name_plural = 'Items'
I want to implement two API endpoints, one to get the list of invoices, and another to get a specific invoice with its items.
For example, /api/ will return all invoices
[
{
"customer_name": "John Doe",
"customer_phone": 111666,
"customer_address": "Palo Alto, California",
"invoice_id": "a8aeb5a8-5498-40fd-9b4f-bb09a7057c71",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Cassian Green",
"customer_phone": 111000,
"customer_address": "2112 Illinois Avenue",
"invoice_id": "7d7b7878-3ffc-4dd2-a60a-fa207c147860",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Chelsea Davis",
"customer_phone": 666777,
"customer_address": "2260 Coplin Avenue",
"invoice_id": "3dda2054-49d7-49dc-9eba-ddc0fdacfd3b",
"invoice_date": "2022-05-04"
},
{
"customer_name": "Derek Elliott",
"customer_phone": 111100,
"customer_address": "3236 Sundown Lane",
"invoice_id": "da112c0d-aff4-43d3-a465-910cc1483fc5",
"invoice_date": "2022-05-04"
}
]
and now if I request a specific invoice with its items, like /api/a8aeb5a8-5498-40fd-9b4f-bb09a7057c71 should return the following response
{
"invoice_id": "a8aeb5a8-5498-40fd-9b4f-bb09a7057c71",
"items": [
{
"item_name": "PC Cabinet",
"item_quantity": 1,
"item_price": 200
},
{
"item_name": "Motherboard",
"item_quantity": 1,
"item_price": 1000
},
{
"item_name": "PSU",
"item_quantity": 1,
"item_price": 300
}
]
}
Here are the serializers:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = [
'item_name',
'item_quantity',
'item_price',
]
class InvoiceSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = [
'customer_name',
'customer_phone',
'customer_address',
'invoice_id',
'invoice_date',
'items'
]
And the views:
#api_view(['GET',])
def InvoiceList(request):
if request.method == 'GET':
queryset = Invoice.objects.all()
serializer_class = InvoiceSerializer(queryset, many=True)
return Response(serializer_class.data)
#api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
queryset = list(Items.objects.filter(invoice = pk))
serializer_class = ItemSerializer(instance=queryset, many=True)
return Response(serializer_class.data)
The first API endpoint is working as expected but the I need help with implementing the second one. How can I get that nested response?
Thanks.
Your related_name of invoice in Items model is wrong, you should set it as "items" because you are going to call that from Invoice model like that.
Also if you want to have different response from that second API you should change it to use other serializer than InvoiceList:
#api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
queryset = list(Items.objects.filter(invoice = pk))
serializer_class = InvoiceDetailSerializer(instance=queryset, many=True)
return Response(serializer_class.data)
I changed name of serializer to InvoiceDetailSerializer so you need to create new serializer:
class InvoiceDetailSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = [
'invoice_id',
'items'
]
you can try something like this. (not tested)
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Items
fields = ['item_name', 'item_quantity', 'item_price',]
class InvoiceDetailsSerializer(serializers.ModelSerializer):
items = ItemSerializer(many=True, read_only=True)
class Meta:
model = Invoice
fields = ['id', 'items']
#api_view(['GET',])
def InvoiceDetail(request, pk):
if request.method == 'GET':
invoice = Invoice.objects.get(id=pk)
serializer_class = InvoiceDetailsSerializer(instance=invoice)
return Response(serializer_class.data)

Django Rest Framework List of Strings Serializer

I have two models created in my Django app and am looking for a serialization approach so that the IPs are shown in JSON as a list of strings instead of a list of IPAddress objects.
Desired JSON
[
{
"hostname": "www.example.com",
"ip_addresses": [ "1.1.1.1", "2.2.2.2" ]
}
]
Current JSON
[
{
"hostname": "www.example.com",
"ip_addresses": [
{
"id": 1,
"ip_address": "1.1.1.1"
},
{
"id": 2,
"ip_address": "2.2.2.2"
}
]
]
urls.py
class HostSerializer(serializers.ModelSerializer):
hostname = serializers.CharField(source='name', read_only=True)
class Meta:
model = Host
fields = ['hostname', 'ip_addresses']
depth = 1
models.py
class IPAddress(models.Model):
ip_address = models.GenericIPAddressField()
def __str__(self):
return str(self.ip_address)
class Host(models.Model):
name = models.CharField(max_length=100)
ip_addresses = models.ManyToManyField(IPAddress)
def __str__(self):
return self.name
Try out this !
class HostSerializer(serializers.Serializer):
hostname = serializers.CharField(source='name', read_only=True)
ip_addresses = serializers.SerializerMethodField(read_only=True)
def get_ip_addresses(self, instance):
return [item.ip_address for item in instance.ip_addresses.all()]
You can use serializers.SerializerMethodField() to achieve what you want:
class HostSerializer(serializers.ModelSerializer):
hostname = serializers.CharField(source='name', read_only=True)
ip_addresses: serializers.SerializerMethodField()
class Meta:
model = Host
fields = ['hostname', 'ip_addresses']
depth = 1
def get_ip_addresses(self, instance):
return [item.ip_address for item in instance.ip_addresses]

Source field in DRF serializer with multiple foreign keys to the same model

I have a Django model as follows:
class Team(models.Model):
name = models.CharField(max_length=100, unique=True)
managers = models.ManyToManyField(User, through=MgrToTeam, related_name='mng')
users = models.ManyToManyField(User, through=UsrToTeam,related_name='usr')
I now have a serializer where i need to display all the users and managers associated with a team:
class TeamDetailSerializer(serializers.ModelSerializer):
managers = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='name')
users = serializers.SlugRelatedField(queryset=User.objects.all(), slug_field='name')
class Meta:
model = Team
fields = ['id', 'name', 'managers', 'users']
However, this gives me the same output for both the users and managers.How do i fix this ?
I'm curious as to why you want to use a SlugRelatedField. Assuming you do have a UserSerializer of some sort, would it work for you to do it like this?
class TeamDetailSerializer(serializers.ModelSerializer):
managers = UserSerializer(many=True)
users = UserSerializer(many=True)
class Meta:
model = Team
fields = ['id', 'name', 'managers', 'users']
if you just want Just primary key as below
[
{
"id": 1,
"name": "helo",
"users": [
1,
2
],
"managers":[
1,
2,
]
}
]
class TeamDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Team
fields = ['id', 'name', 'users','managers']
if you want return object properties as below
[
{
"id": 1,
"name": "helo",
"users": [
{
"email": "admin#gmail.com",
"first_name": ""
},
{
"email": "admin1#gmail.com",
"first_name": ""
}
],
"managers": [
{
"email": "admin#gmail.com",
"first_name": ""
},
{
"email": "admin1#gmail.com",
"first_name": ""
}
]
}
]
class TeamDetailSerializer(serializers.ModelSerializer):
users = UserDetailSerializer(many=True)
managers = UserDetailSerializer(many=True)
class Meta:
model = Team
fields = ['id', 'name', 'users', 'managers']

How to get the value of the key in the serializer in manytomanyfield in Django Rest Framework?

I'm having a problem with displaying the data during serialization.
This is my model:
from django.db import models
class Paradigmn(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Language(models.Model):
name = models.CharField(max_length=50)
paradigm = models.ForeignKey(Paradigmn, on_delete=models.CASCADE)
def __str__(self):
return self.name
class Programmer(models.Model):
name = models.CharField(max_length=50)
languages = models.ManyToManyField(Language, related_name='languages')
def __str__(self):
return self.name
And this is my serializer:
from languages.models import Language, Paradigmn, Programmer
class LanguageSerializer(serializers.ModelSerializer):
paradigms = serializers.ReadOnlyField(source='paradigm.name')
class Meta:
model = Language
fields = ('id', 'name', 'paradigms')
class ParadigmnSerializer(serializers.ModelSerializer):
class Meta:
model = Paradigmn
fields = ('id', 'name',)
class ProgrammerSerializer(serializers.ModelSerializer):
languages = LanguageSerializer(many=True, read_only=True)
class Meta:
model = Programmer
fields = ('id', 'name', 'languages')
And this is the result:
[
{
"id": 1,
"name": "Ryan",
"languages": [
{
"id": 1,
"name": "Java",
"paradigms": "Object-Oriented"
}
]
},
{
"id": 2,
"name": "Jean",
"languages": [
{
"id": 3,
"name": "Python",
"paradigms": "Object-Oriented"
}
]
},
{
"id": 3,
"name": "Michael",
"languages": [
{
"id": 2,
"name": "Elixir",
"paradigms": "Functional"
}
]
}
I just want to show on the languages array, the name of the language instead of the all the details of the language array. What is the best solution for this?
One solution:
from rest_framework.serializers import SerializerMethodField
class ProgrammerSerializer(serializers.ModelSerializer):
languagelist = SerializerMethodField()
def get_languagelist(self, obj):
return [{'name': i.name} for i in obj.languages.all()]
class Meta:
model = Programmer
fields = ('id', 'name', 'languagelist')

Django Rest Framework: Get related specific model field in serializer from another model

I'm trying to return a Response including data from 2 linked models, where I can get a specific field value from another model. The models are:
class Author(models.Model):
author = models.CharField(max_length=200)
author_created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('author_created',)
class Book(models.Model):
book_name = models.CharField(max_length=200)
author_name = models.ForeignKey(Author, on_delete=models.CASCADE)
book_created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('book_created',)
Serializers are:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'author')
class BookSerializer(serializers.ModelSerializer):
name = AuthorSerializer(source='author_name')
class Meta:
model = Book
fields = ('id', 'book_name','name')
My response shows like this:
[
{
"id": 1,
"book_name": "Himu",
"name": {
"id": 1,
"author": "Humayun Ahmed"
}
},
{
"id": 2,
"book_name": "Shanta's family",
"name": {
"id": 2,
"author": "Jafar Iqbal"
}
}
]
But i want to show it like this:
[
{
"id": 1,
"book_name": "Himu",
"name": {
"author": "Humayun Ahmed"
}
},
{
"id": 2,
"book_name": "Shanta's family",
"name": {
"author": "Jafar Iqbal"
}
}
]
How can i get this output?
Try something like this name = AuthorSerializer(source='author_name.author')
Reference : http://www.django-rest-framework.org/api-guide/fields/#source

Categories