Django Rest Framework List of Strings Serializer - python

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]

Related

Django - filtering related objects

I have a puzzle.
These are my models:
class StatusGroup(models.Model):
name = models.TextField()
def __str__(self):
return self.name
class StatusDetail(models.Model):
action = models.CharField(choices=[("CORRECT", "CORRECT"),
("INCORRECT", "INCORRECT")],
max_length=64)
status_group = models.ForeignKey(to=StatusGroup,
on_delete=models.CASCADE,
related_name="status_details")
def __str__(self):
return f"Detail: {self.action}"
serializers:
class StatusDetailSerializer(serializers.ModelSerializer):
class Meta:
model= models.StatusDetail
fields = "__all__"
class StatusGroupSerializer(serializers.ModelSerializer):
status_details = StatusDetailSerializer(many=True)
class Meta:
model = models.StatusGroup
fields = [
"pk",
"status_details",
"name"
]
And a view:
class Status(viewsets.ModelViewSet):
queryset = models.StatusGroup.objects.all()
serializer_class = serializers.StatusGroupSerializer
authentication_classes = []
permission_classes = [permissions.AllowAny]
filter_backends = (DjangoFilterBackend,)
filterset_fields = ['status_details__action']
When I hit localhost:8000/api/status?status_details__action=INCORRECT
I get:
[
{
"pk": 2,
"status_details": [
{
"id": 3,
"action": "CORRECT",
"status_group": 2
},
{
"id": 4,
"action": "INCORRECT",
"status_group": 2
}
],
"name": "Mixed"
}
]
Whereas I would like to have:
[
{
"pk": 2,
"status_details": [
{
"id": 4,
"action": "INCORRECT",
"status_group": 2
}
],
"name": "Mixed"
}
]
How do I force Django to filter the related objects? I can get the result I want in SQL console, but Django adds, all the related objects that belong to the StatusGroup.
I have a misconception, but I don't know what that is.
try this way (using query set):
from django.db.models import Prefetch
queryset = StatusGroup.objects.prefetch_related(
Prefetch('status_details', queryset=StatusDetail.objects.filter(action='what_you_want'), to_attr='action'))
and in your serializer class:
class StatusGroupSerializer(serializers.ModelSerializer):
status_details = StatusDetailSerializer(source='action', many=True, read_only=True)
First you can create a FilterSet class like this:
from django_filters import rest_framework as filters
class StatusGroupFilter(filters.FilterSet):
action = filters.CharFilter(
field_name="status_details__action",
lookup_expr="iexact"
)
class Meta:
model = StatusGroup
fields = ["action"]
Then in your views:
class Status(viewsets.ModelViewSet):
queryset = models.StatusGroup.objects.all()
serializer_class = serializers.StatusGroupSerializer
authentication_classes = []
permission_classes = [permissions.AllowAny]
filter_backends = (DjangoFilterBackend,)
# Set the filterset class here
filterset_class = StatusGroupFilter
Then in the URL you can call localhost:8000/api/status?action=INCORRECT

django drf about many-to-many RelatedField custom field

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!

Django Rest Framework - Group data by its parent tag

Django Rest Framework - Group data by its parent tag
I have 3 serializers, One for tactics, one for techniques, one for sub techniques, and I'll be adding the sub techniques serializer as an explicit field to the techniques serializer.
models
class Tag(models.Model):
_id = models.CharField(max_length=10)
name = models.CharField(max_length=100)
def __str__(self):
return self._id
def __unicode__(self):
return self._id
class ModelA(models.Model):
_id = models.CharField(max_length=10)
title = models.CharField(max_length=100)
tags = models.ManyToManyField(Tag)
queries = ArrayField(models.TextField(),null=True, blank=True, default=list)
def __str__(self):
return self._id
def __unicode__(self):
return self._id
class ModelB(models.Model):
subtitle = models.ForeignKey(ModelA, on_delete=models.CASCADE)
def __str__(self):
return self.subtitle
serializers
# serializers
class TagSerializer(serializers.ModelSerializer):
def to_representation(self, value):
print(value)
return value.name
class Meta:
model = Tag
fields = ('name',)
class QueriesSerializer(serializers.ModelSerializer):
class Meta:
model = ModelB
fields = '__all__'
class ASerializer(serializers.ModelSerializer):
queries = QueriesSerializer(source='modelb_set', many=True)
tag = TagSerializer(many=True)
class Meta:
model = ModelA
fields = ('_id','title', 'tag','queries',)
class EndUserSerializer(serializers.ModelSerializer):
data = ??
class Meta:
model = ??
fields = '__all__'
I want to create a data visualization API where I need to use the results of ASerializer by their given tag in order to categorize each object into categories based on their associated tag if the object contains two tags, then it should be displayed in both . e.g
desired response
{
[
{
"tag": "Biologyl,
"data": [
{
"_id": str
"title": str
"tag": []
"queries": []
}
]
},
{
"tag": "Denoi2,
"data": [
{
"_id": str
"title": str
"tag": []
"queries": []
}
]
},
]
}
actual response
{
"_id": str
"title": str
"tag": []
"queries": []
}
viewset
#viewsets
class BooksViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = ??.objects.all()
serializer_class = EndUserSerializer
You can use django-rest-multiple-models for receiving multiple querysets in the same view.
https://github.com/MattBroach/DjangoRestMultipleModels
It should look like this:
class BooksViewSet(ObjectMultipleModelAPIView):
querylist = [
{
'querylist': ModelA.objects.filter(tags__name="Biologyl"),
'serializer_class': EndUserSerializer,
'label': 'Biologyl',
},
{
'querylist': ModelA.objects.filter(tags__name="Denoi2"),
'serializer_class': EndUserSerializer,
'label': 'Denoi2',
},
....
]
You can also make it dynamic, but it seems a little bit dangerous👀.
class BooksViewSet(ObjectMultipleModelAPIView):
querylist = [
{
'querylist': ModelA.objects.filter(tags=tag),
'serializer_class': EndUserSerializer,
'label': tag.name,
}
for tag in Tag.objects.all()
]
See more https://django-rest-multiple-models.readthedocs.io/en/latest/object-options.html

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

Creating a Serialiser for a Reverse Generic Relationship

Within my Page Serialisers PageSerializer shown below, I would like to get the Generic relationship from Collection and
show all Collection Items (which is a many-to-many) nested within PageSerializer.
I want to achieve an output something like this....
"results": [
{
"url": "http://127.0.0.1:8000/v1/page/00c8015e-9b03...",
"title": "Test Page",
"collections":
{
"title": "Test Collection",
[
{
"image": "http://www.demo.com/test.png",
"video": None
},
{
"image": None,
"video": "http://www.demo.com/test.mpeg"
}
]
}
}
]
}
This is what I currenrlty have....
class Page(models.Model):
title = models.CharField(max_length=80)
class PageSerializer(serializers.ModelSerializer):
class Meta:
model = Page
fields = ("title", )
class Collection(models.Model):
title = models.CharField(max_length=80, help_text="Title of collection")
content_type = models.ForeignKey(ContentType)
object_id = models.UUIDField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
collection_items = models.ManyToManyField('collections.CollectionItem')
class CollectionItem(models.Model):
image = models.ImageField(upload_to='/test')
video = models.URLField(max_length=512, blank=True, null=True)
Becuase the Generic relationship is on the Collection Model how can this be done in DRF?
I was thinking of creating a method on the Page model itself which gets the data and adding this to the serializer.
I'm sure there is a better way. I have looked at http://www.django-rest-framework.org/api-guide/relations/#generic-relationships
but it only describes getting the relationship the other way around.
Create a GenericRelation on your Page model for example:
class Page(models.Model):
title = models.CharField(max_length=80)
stuff = GenericRelation('app_name_model_here')
Then used your nested serializer like this...
class PageSerializer(serializers.ModelSerializer):
stuff = YOURColltionserializer(many=True)
class Meta:
model = Page
fields = ("title", "stuff" )
Once you have defined your YOURColltionserializer this will work as excepted.
First of all, your JSON is incorrect, the propher way is something like this:
{
"results":[
{
"url":"http://127.0.0.1:8000/v1/page/00c8015e-9b03...",
"title":"Test Page",
"collections":{
"title":"Test Collection",
"collection_items":[
{
"image":"http://www.demo.com/test.png",
"video":null
},
{
"image":null,
"video":"http://www.demo.com/test.mpeg"
}
]
}
}
]
}
To achieve this You should define serializer like this:
class CollectionItemSerializer(serializers.ModelSerializer):
class Meta:
model = CollectionItem
fields = ['image', 'video']
class CollectionSerializer(serializers.ModelSerializer):
collection_items = CollectionItemSerializer(many=True, read_only=True)
class Meta:
model = Collection
fields = ['title', 'collection_items']
class PageSerializer(serializers.Serializer):
title = serializes.CharField(max_length=200)
url = serializers.SerializerMethodField()
collections = CollectionSerializer(many=true, read_only=True)
def get_url(self, obj):
return self.context.request.path_info
class ResultSerializer(serializers.Serializer):
result = PageSerializer(read_only=True)
Then in your view:
page = Page.objects.get(...)
collections = Collection.objects.filter(...)
result = {
'title': page.title,
'collections':collections
}
serializer = ResultSerializer(
{'result':result},
context={'request': request}
}
return Response(serializer.data)

Categories