I have a working API in my project that look something like this :
{
"id": 1,
"project_name": "Project A",
"user": [
{
"id": 3,
"employee_id": "001",
"official_name": "RAY PAlMER",
"nick_name": "RAY",
"date_of_birth": "1965-08-25",
"gender": "1",
"race": "CAUCASIAN",
"food_pref": "Vegetarian",
},
{
"id": 3,
"employee_id": "002",
"official_name": "LAIRA OMOTO",
"nick_name": "LAIRA",
"date_of_birth": "1990-01-15",
"gender": "2",
"race": "WHITE",
"food_pref": "Any",
}
]
}
The user field is a foreign key field and its populating everything from its source model called Employee. My question is how can I populate the user many to many field with only several field that I wanted to get from the Employee Model. For example I just want to populate only a user id, official name, and gender, therefore the output should be something like this
{
"id": 1,
"project_name": "Project A",
"user": [
{
"id": 3,
"official_name": "RAY PAlMER",
"gender": "1",
},
{
"id": 4,
"official_name": "LAIRA",
"gender": "2",
}
]
}
Below is my source code :
Project Model :
class Project(models.Model):
user = models.ManyToManyField(Employee, blank=True)
Employee Model :
class Employee(models.Model):
official_name = models.CharField(max_length=200, null=True, blank=True)
gender_choice = (
('male', 'Male'),
('female', 'Female')
)
gender = models.CharField(max_length=10, choices=gender_choice, null=True,
blank=True)
Serializer :
class MarketingReportSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('id', 'project_name','user')
depth = 1
Any help is much appreciated thanks!
API :
class MarketingReportAPI(APIView):
def get(self, request):
all_projects = Project.objects.all()
project_serializer = MarketingReportSerializer(all_projects, many=True)
return Response(project_serializer.data)
Create EmployeeSerializer serializer.
class EmployeeSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ('id', 'official_name', 'gender')
Now use it in your MarketingReportSerializer
class MarketingReportSerializer(serializers.ModelSerializer):
user = EmployeeSerializer(many=True)
class Meta:
model = Project
fields = ('id', 'project_name', 'user')
And then you can use MarketingReportSerializer in your view.
Related
I'm using Django Rest Framework for API and I wrote the model Category in which I have parent and I'm using it like this: parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
In API it looks like this:
{
"id": 7,
"name": "softwares",
"img": "",
"parent": 1
},
{
"id": 8,
"name": "databases",
"img": "",
"parent": 1
},
{
"id": 9,
"name": "appearance",
"img": "",
"parent": 2
},
{
"id": 10,
"name": "media",
"img": "",
"parent": 2
},
{
"id": 11,
"name": "system",
"img": "",
"parent": 2
},
Here is Category Serializer:
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
And Category View Set:
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
action_to_serializer = {
"retrieve": CategoryDetailSerializer,
}
def get_serializer_class(self):
return self.action_to_serializer.get(
self.action,
self.serializer_class
)
How can I make the category itself and not just the id?
Assuming you won't be handling with an infinite sequence of relations and are handling this with just one level of parenting, you can achieve that by using nested serializers, an example on your scenario would be something like this:
class NestedCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name', 'img')
class CategorySerializer(serializers.ModelSerializer):
parent = NestedCategorySerializer()
class Meta:
model = Category
fields = '__all__'
If your relationship needs more depth levels, you can improve this example to include always the serialized parent - be careful with cycles.
I have two models List and Card. I'm trying to combine these two and make a single JSON response
My List JSON response
[
{
"id": 1,
"name": "List of things to do"
},
{
"id": 2,
"name": "one more"
}
]
My Card JSON response
[
{
"id": 1,
"title": "My first scrum card",
"description": "list things todo here",
"story_points": null,
"business_value": null,
"list": 1
},
{
"id": 2,
"title": "File my taxes",
"description": "fill it before 1st of nov",
"story_points": null,
"business_value": null,
"list": 1
},
]
My serializers.py file
from rest_framework import serializers
from .models import List, Card
class CardSerializer(serializers.ModelSerializer):
class Meta:
model = Card
fields = '__all__'
class ListSerializer(serializers.ModelSerializer):
cards = CardSerializer(read_only=True, many=True)
class Meta:
model = List
fields ='__all__'
My api.py file
from rest_framework.viewsets import ModelViewSet
from .models import List, Card
from .serializers import ListSerializer, CardSerializer
class ListViewSet(ModelViewSet):
queryset = List.objects.all()
serializer_class = ListSerializer
class CardViewSet(ModelViewSet):
queryset = Card.objects.all()
serializer_class = CardSerializer
My models.py
from django.db import models
class List(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return "List : {}".format(self.name)
class Card(models.Model): # to create card table
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
list = models.ForeignKey(List, related_name = "card"
,on_delete=models.PROTECT) # creating a foriegn key for storing list
story_points = models.IntegerField(null=True, blank = True)
business_value = models.IntegerField(null=True, blank = True)
def __str__(self):
return "Card : {}".format(self.title)
How can I achieve something like below in My List JSON response ?
[
{
"id": 1,
"cards":[
{
"id": 1,
"title": "My first scrum card",
"description": "list things todo here",
"story_points": null,
"business_value": null,
"list": 1
}],
"name": "List of things to do"
},
{
"id": 2,
"cards":[
{
"id": 2,
"title": "File my taxes",
"description": "fill it before 1st of nov",
"story_points": null,
"business_value": null,
"list": 1
}],
"name": "one more"
},
]
I have tried to do it but I was unable to implement it. So I posted it here.
Heading
Many thanks.
You can also use SerializerMethodeField https://www.django-rest-framework.org/api-guide/fields/#serializermethodfield.
class ListSerializer(serializers.ModelSerializer):
cards = serializers.SerializerMethodField()
class Meta:
model = List
fields = '__all__'
def get_cards(self, obj):
data = CardSerializer(obj.card.all(), many=True).data
return data
change the list related_name to cards and ListViewSet will do what you want
class Card(models.Model): # to create card table
title = models.CharField(max_length=100)
description = models.TextField(blank=True)
list = models.ForeignKey(List, related_name = "cards"
,on_delete=models.PROTECT)
...
I'm working on building a tree-like hierarchical database system using Django Rest Framework and django-polymorphic-tree. I have two models- BaseTreeNode and DescriptionNode (the later one is derived from BaseTreeNode). Specifically, here's my models.py:
class BaseTreeNode(PolymorphicMPTTModel):
parent = PolymorphicTreeForeignKey('self', blank=True, null=True, related_name='children', verbose_name=_('parent'))
title = models.CharField(_("Title"), max_length=200)
def __str__(self):
return "{}>{}".format(self.parent, self.title)
class Meta(PolymorphicMPTTModel.Meta):
verbose_name = _("Tree node")
verbose_name_plural = _("Tree nodes")
# Derived model for the tree node:
class DescriptionNode(BaseTreeNode):
description = models.CharField(_("Description"), max_length=200)
class Meta:
verbose_name = _("Description node")
verbose_name_plural = _("Description nodes")
So, each title field (belonging to BaseTreeNode) has an associated description field (belonging to DescriptionNode) with it.
Now, all I want to have is a JSON that returns a nested representation of my entire tree.
For now, I have only defined a simple serializer with a recursive field.
My serializers.py
from rest_framework_recursive.fields import RecursiveField
class DescriptionNodeSerializer(serializers.ModelSerializer):
class Meta:
model = DescriptionNode
fields = ('description',)
class BaseTreeNodeSerializer(serializers.ModelSerializer):
subcategories = serializers.ListSerializer(source="children",child=RecursiveField())
class Meta:
model = BaseTreeNode
fields = ('id', 'title', 'subcategories')
Which gives me (for BaseTreeNodeSerializer only):
[
{
"id": 1,
"title": "Apple",
"subcategories": [
{
"id": 2,
"title": "Contact Person",
"subcategories": []
},
{
"id": 3,
"title": "Sales Stage",
"subcategories": [
{
"id": 4,
"title": "Suspecting",
"subcategories": [
{
"id": 5,
"title": "Contact verification",
"subcategories": []
}
]
},
{
"id": 6,
"title": "Prospecting",
"subcategories": [
{
"id": 7,
"title": "Client Detail",
"subcategories": []
}
]
}
]
},
{
"id": 9,
"title": "Medium",
"subcategories": [
{
"id": 10,
"title": "Status",
"subcategories": []
}
]
},
{
"id": 13,
"title": "Remainder",
"subcategories": []
}
]
}
]
My question is, how can I include the description field (from the derived model) which is associated with every single title field (from the BaseTreeNode model) in the hierarchy?
Something like:
... {
"id": 5,
"title": "Contact verification",
"description": "Verified"
"subcategories": []
} ...
The Corresponding Model would be as follows:
class DescriptionNode(BaseTreeNode):
basetreenode = models.OneToOneField(BaseTreeNode, related_name="base_tree")
description = models.CharField(_("Description"), max_length=200)
class Meta:
verbose_name = _("Description node")
verbose_name_plural = _("Description nodes")
Serializer would be as follows:
from rest_framework_recursive.fields import RecursiveField
class BaseTreeNodeSerializer(serializers.ModelSerializer):
description = serializers.SerializerMethodField()
subcategories = serializers.ListSerializer(source="children",child=RecursiveField())
class Meta:
model = BaseTreeNode
fields = ('id', 'title', 'description', 'subcategories')
def get_description(self, obj):
return obj.base_tree.description #base_tree is related name of basetreenode field
I have model Product:
class Product(TranslatableModel):
name = models.CharField(max_length=255, unique=True)
translations = TranslatedFields(
description=models.TextField(),
)
and in administration on product detail I have tabs with languages. For example tabs EN, CZ, each includes disctiption. So PUT request looks like:
{
'product': '1',
'id': 1,
'name': 'Name',
'translations': {
'cz': {'desctiption': 'Description cz'},
'en': {'desctiption': 'Description en'}
}
}
I founded in django-hvad TranslationsMixin that allows me to do that request.
in serializers I have:
class ProductTranslationSerializer(serializers.ModelSerializer):
class Meta:
exclude = ['description']
class ProductSerializer(TranslationsMixin, serializers.ModelSerializer):
class Meta:
model = Product
translations_serializer = ProductTranslationSerializer
fields = (
'name',
'description',
)
Question is how will looks ModelViewSet for this request? Can I choose language like 'language_code=en', filter that query and get something like:
[
{
id: 1
name: "name"
descritpion: "descritpion"
},
....
]
Thank you!
the most likely way i achieve is:
models.py
class Product(TranslatableModel):
category = models.ForeignKey('product.ProductCategory',
related_name='product_category',
null=True,
on_delete=models.SET_NULL,
verbose_name=u'category')
cover = models.ImageField(upload_to=product_cover,
null=True,
verbose_name=u'cover')
translations = TranslatedFields(
title=models.CharField(max_length=100,
null=True,
verbose_name=u'title'),
summary=models.TextField(null=True,
verbose_name=u'summary'),
model=models.CharField(max_length=255,
null=True,
blank=True,
verbose_name=u'model'),
price=models.DecimalField(default=0.00,
max_digits=10,
decimal_places=2,
blank=True,
validators=[MinValueValidator(0)],
verbose_name=u'price'),
content=models.TextField(verbose_name=u'content'),
publish_time=models.DateTimeField(default=timezone.now,
verbose_name=u'发布publish_time')
)
view_times = models.IntegerField(default=0,
verbose_name=u'view_times ')
views.py
class ProductViewSet(ModelViewSet):
serializer_class = ProductListSerializer
def get_queryset(self):
if 'language_code' in self.request.GET:
language_code = self.request.GET.get('language_code')
queryset = Product.objects.language(language_code).order_by('-id')
else:
queryset = Product.objects.language().order_by('-id')
return queryset
serializers.py
class ProductCategorySerializer(TranslatableModelSerializer):
class Meta:
model = ProductCategory
fields = '__all__'
class ProductListSerializer(TranslatableModelSerializer):
category = ProductCategorySerializer(read_only=True)
class Meta:
model = Product
exclude = ['is_abandon', 'content', ]
urls.py
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'product', ProductViewSet, base_name='api-product')
...
result:
http://192.168.1.108/api/product/?language_code=zh-hans you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 08:56:28",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "标题",
"summary": "简介",
"model": null,
"price": "90.00",
"publish_time": "2017-08-18 08:53:00",
"language_code": "zh-hans"
}
]
}
http://192.168.1.108/api/product/?language_code=en you get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "测试",
"language_code": "zh-hans"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
this way fk will not change language,if you want fk change language too,use:
urls.py
urlpatterns += i18n_patterns(
'''
url(r'^api/', include(router.urls)),
prefix_default_language=False,
)
change language by
http://192.168.1.108/zh-hans/api/product/
to
http://192.168.1.108/en/api/product/
your will get:
{
"count": 1,
"page_num": 1,
"page_no": 1,
"next": "",
"previous": "",
"results": [
{
"id": 2,
"category": {
"id": 2,
"create_time": "2017-08-10 16:49:41",
"update_time": "2017-08-18 08:56:02",
"name": "test",
"language_code": "en"
},
"create_time": "2017-08-18 08:53:46",
"update_time": "2017-08-18 09:00:23",
"cover": "http://192.168.1.108/media/product/20170818-085346-518_59.jpg",
"view_times": 0,
"title": "title",
"summary": "summary",
"model": "model",
"price": "91.00",
"publish_time": "2017-08-18 08:56:04",
"language_code": "en"
}
]
}
Answer from owner of django-hvad github repo #spectras:
If I understand well, you want to use a different serialized form for the collection endpoint and the item endpoint. That is, GET-1, PUT, POST would use the translations mixin, while GET-many would not.
You need two have two different serializers for this:
The one you created
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
another one that would be, for instance,
class ProductItemSerializer(TranslatableModelSerializer): ...
Given that, you can add to your ModelViewSet a method that dynamically chooses the serializer class depending on request type:
class SomeModelViewSet(ModelViewSet):
# serializer_class = not needed, we use the method instead
def get_serializer_class(self):
if self.action == 'list':
return ProductItemSerializer
return ProductSerializer
An alternate method, perhaps easier to use on the Javascript side, would be to make 2 viewsets for the Product model, a read-write one being translation aware (using ProductSerializer) and a read-only one not translation aware, using (ProductItemSerializer).
In the same vein, I usually only have translation-unaware serializers and add a /api/product/details/ endpoint that is translation-aware. This way, I only have to handle the complexity of a dict object when going into edit/detailed mode on the client side.
I've two models (Map and Place) and I want to create a JSON with both of these (a map contains several places).
models.py
class Map(models.Model):
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)
class Place(models.Model):
map = models.ForeignKey('main.Map')
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)
I use this to serialize these models individually :
serializers.py
class MapSerializer(serializers.ModelSerializer):
class Meta:
model = Map
fields = ('id', 'name')
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ('id', 'name', 'map')
I want a JSON like this, but I've no idea how to serialize this correctly...
{
"maplist": {
"maps": [
{
"id": "1",
"name": "dust2",
"places": [
{
"id": "1",
"name": "Long"
},
{
"id": "2",
"name": "Middle"
}
]
},
{
"id": "2",
"name": "inferno",
"places": [
{
"id": "1",
"name": "Middle"
},
{
"id": "2",
"name": "ASite"
}
]
}
]
}
}
Thanks in advance for your help.
Try with nested serializers:
class PlaceSerializer(serializers.ModelSerializer):
class Meta:
model = Place
fields = ('id', 'name')
class MapSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True)
class Meta:
model = Map
fields = ('id', 'name')
To make this work, you need to change your model to include a related name to your foreign key:
class Place(models.Model):
map = models.ForeignKey('main.Map', related_name="places")
name = models.CharField(max_length=100)
slug = models.SlugField(max_length=200)