I'm using Django 2.2 and Django REST Framework
I have three models like
class Plan(models.Model):
name = models.CharField(_('Plan Name'), max_length=100)
default = models.NullBooleanField(default=None, unique=True)
created = models.DateTimeField(_('created'), db_index=True)
quotas = models.ManyToManyField('Quota', through='PlanQuota')
class Quota(models.Model):
codename = models.CharField(max_length=50, unique=True)
name = models.CharFieldmax_length=100)
unit = models.CharField(max_length=100, blank=True)
class PlanQuota(models.Model):
plan = models.ForeignKey('Plan', on_delete=models.CASCADE)
quota = models.ForeignKey('Quota', on_delete=models.CASCADE)
value = models.IntegerField(default=1, null=True, blank=True)
I have to get all quota and their value from PlanQuota in the plan serializer while getting a list of plans.
I have the following serializer
class PlanQuotaSerialier(serializers.ModelSerializer):
class Meta:
model = PlanQuota
depth = 1
fields = ['quota', 'value']
class PlanListSerializer(serializers.ModelSerializer):
plan_quota = PlanQuotaSerialier(read_only=True, many=True)
class Meta:
model = Plan
depth = 1
fields = ['name', 'default', 'created', 'plan_quota']
But there is no plan_quota in the response.
How can I add all Quota and their value for each plan in a single query (SQL JOIN)?
Edit 2:
Adding source to the serializer field worked
plan_quota = PlanQuotaSerialier(source='planquota_set', many=True)
And the result is like
"results": [
{
"name": "Test Plan 1",
"default": true,
"plan_quotas": [
{
"quota": {
"id": 1,
"order": 0,
"codename": "TEST",
"name": "Test Domain",
"unit": "count",
"description": "",
"is_boolean": false,
"url": ""
},
"value": 10
},
]
}
]
Can I club all fields from quota with value field in the plan_quotas list?
class PlanQuota(models.Model):
plan = models.ForeignKey('Plan', on_delete=models.CASCADE, related_name='plan_quotas')
quota = models.ForeignKey('Quota', on_delete=models.CASCADE)
value = models.IntegerField(default=1, null=True, blank=True)
class PlanListSerializer(serializers.ModelSerializer):
class Meta:
model = Plan
depth = 1
fields = ['name', 'default', 'created', 'plan_quotas']
This is how I got it solved.
For the first query, added source
plan_quota = PlanQuotaSerialier(source='planquota_set', many=True)
For removing quota key, added to_presentation() in the PlanQuotaSerializer
def to_representation(self, instance):
representation = super().to_representation(instance)
if 'quota' in representation:
representation['quota']['quota_id'] = representation['quota'].pop('id')
representation.update(representation.pop('quota'))
return representation
Related
I am working on a NextJS + Django REST Framework project where I have three models; Document, MySource, and QuestionBlock.
Document is created along with several “question_blocks” linked to the created document. They are created together, and I have already implemented this with nested serializers.
After the Document is created, I want to be able to POST a MySource model linked to the document. Then, when I make a GET request of a document, all mysource objects should be displayed as well.
POST request: notice how I just put the document’s id that I want to link with.
{
"url": "urlasdf",
"title": "tuitle",
"publisher": "afdfas ",
"desc": "qwefqwef",
"summary": "asdfasdf",
"document": "J9DY2pE"
}
GET request: I want the document GET request to show something like below.
"id": "LwpQr6Y",
"question_blocks": [
{
"id": 16,
"document": "LwpQr6Y",
"title": "question 4",
"content": "qweqgadssdffasdf asdf"
},
]
"mysource": [
{
"id": 16,
"url": "google.com",
etc. . .
},
],
"title": "renamed title",
"template": "commonapp",
"updated": "2022-05-19T02:16:00+0000",
"created": "2022-04-21T06:59:05+0000"
The weird part is that I do not see any errors with the code below, and the functionality itself is working properly. But when I attempt to GET the document which has at least one mysource object, it takes at a couple minutes to load, which is making me think there’s something wrong with my code that is perhaps making DRF repeat itself.
models.py
class Document(models.Model):
id = HashidAutoField(primary_key=True)
title = models.CharField(max_length=100, default="Untitled")
template = models.CharField(max_length=100, default="")
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
class QuestionBlock(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(
Document,
related_name="question_blocks",
on_delete=models.CASCADE,
null=True,
blank=True,
)
title = models.CharField(max_length=500, default="")
content = models.CharField(max_length=100000, default="", blank=True)
class MySource(models.Model):
id = models.AutoField(primary_key=True)
document = models.ForeignKey(
Document,
related_name="mysource",
on_delete=models.CASCADE,
null=True,
blank=True,
)
url = models.CharField(max_length=500, default="")
title = models.CharField(max_length=500, default="", blank=True)
publisher = models.CharField(max_length=500, default="", blank=True)
desc = models.CharField(max_length=500, default="", blank=True)
summary = models.CharField(max_length=500, default="", blank=True)
serializers.py
class MySourceSerializer(serializers.ModelSerializer):
class Meta:
model = MySource
fields = ("id", "url", "title", "publisher", "desc", "summary")
def to_representation(self, instance):
self.fields["document"] = DocumentSerializer(read_only=True)
return super(MySourceSerializer, self).to_representation(instance)
class DocumentSerializer(serializers.ModelSerializer):
id = HashidSerializerCharField(source_field="documents.Document.id", read_only=True)
question_blocks = QuestionBlockSerializer(many=True)
mysource = MySourceSerializer(many=True)
class Meta:
model = Document
fields = "__all__"
def create(self, validated_data):
question_blocks = validated_data.pop("question_blocks")
document = Document.objects.create(**validated_data)
for qBlock in question_blocks:
QuestionBlock.objects.create(document=document, **qBlock)
document.save()
return document
EDIT: adding the QuestionBlockSerializer for more context
class QuestionBlockSerializer(serializers.ModelSerializer):
document = serializers.PrimaryKeyRelatedField(
pk_field=HashidSerializerCharField(source_field="documents.Document.id"),
read_only=True,
)
class Meta:
model = QuestionBlock
fields = "__all__"
optional_fields = ["content"]
I think that an appropriate way to do this maybe be this one:
###imports
from django.forms.models import model_to_dict
class DocumentListingField(serializers.RelatedField):
def to_representation(self, instance):
return model_to_dict(instance.document)
and then in MySourceSerializer remove the to_representation function and update to something like this:
class MySourceSerializer(serializers.ModelSerializer):
document = DocumentListingField(many=False, read_only=True)
class Meta:
model = MySource
fields = (
"id", "url", "title", "publisher", "desc", "summary", "document")
*Edit: I added the read_only set to True, 'caus the those models are using Hashfields thar are not easily selializable.
**edit:The reason that cause the slow response it's becaus you have a circle call of serializators so the system never know when to stop.
right here:
class MySourceSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
...
self.fields["document"] = DocumentSerializer(read_only=True)
#Mysource its calling Documentserializer
...
#And here:
class DocumentSerializer(serializers.ModelSerializer):
...
mysource = MySourceSerializer(many=True) #this one its calling the MysorceSerialize, so there are a endless loop recursion
source : Django Rest Framework-Custom relational fields
Django Rest Api - ManyToManyField, Display 'title' instead of 'id' in the Exercises Array
HTTP 200 OK
Allow: GET, POST, PUT, DELETE, PATCH
Content-Type: application/json
Vary: Accept
[
{
"id": 1,
"title": "Push Workout Bjarred",
"description": "Kör Hårt!",
"exercises": [
3,
4,
5,
6,
9,
10
],
"cardio": [
4
]
},
{
"id": 2,
"title": "Pull Workout Loddekopinge",
"description": "",
"exercises": [
1,
2,
7,
8
],
"cardio": []
},
{
"id": 3,
"title": "CardioPass",
"description": "",
"exercises": [],
"cardio": [
2,
3,
4
]
}
]
Serializer (So I want to display the title and not the id for every exercise)
class WorkoutSerializer(serializers.ModelSerializer):
class Meta:
model = Workout
fields = ('id', 'title', 'description', 'exercises', 'cardio')
Here are the models, note that I want to display the exercise name for every exercise in the api array, I don't want the id! - That is passed right now! :)
class Bodypart(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Exercise(models.Model):
name = models.CharField(max_length=40)
bodyparts = models.ManyToManyField(Bodypart, blank=True)
def __str__(self):
return self.name
class Cardio(models.Model):
name = models.CharField(max_length=40)
time = models.IntegerField(default=10)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'cardio'
class Workout(models.Model):
title = models.CharField(max_length=120)
description = models.CharField(max_length=1000, blank=True)
exercises = models.ManyToManyField(Exercise, blank=True)
cardio = models.ManyToManyField(Cardio, blank=True)
def __str__(self):
return self.title
You can get this by changing your WorkoutSerializer like this
class WorkoutSerializer(serializers.ModelSerializer):
exercises = serializers.StringRelatedField(many=True, read_only=True)
class Meta:
fields = ('id', 'title', 'description', 'exercises', 'cardio', )
You will get the result json like this
[
{
"id": 1,
"title": "Push Workout Bjarred",
"description": "Kör Hårt!",
"exercises": [
"Exercise 1",
"Exercise 2",
"Exercise 4",
"Exercise 3"
],
"cardio": [
4
]
},
...
More about django rest serializers see https://www.django-rest-framework.org/api-guide/relations/
Create a serializer for the Exercise model with the name field:
class ExerciseSerializer(serializers.ModelSerializer):
class Meta:
model = Exercise
fields = ('name', )
And add it to the Workout serializer:
class WorkoutSerializer(serializers.ModelSerializer):
exercises = ExerciseSerializer(many=True, read_only=True)
class Meta:
fields = ('id', 'title', 'description', 'exercises', 'cardio', )
Try adding exercises field in serializer as below
exercises = serializers.SlugRelatedField(read_only=True, slug_field='title')
I had the similar issue but and this is how I solved it:
class WorkoutSerializer(serializers.ModelSerializer):
exercises = serializers.StringRelatedField(many=True, read_only=True)
class Meta:
fields = ('id', 'title', 'description', 'exercises', 'cardio', )
Updated the Model:
class Workout(models.Model):
title = models.CharField(max_length=120)
description = models.CharField(max_length=1000, blank=True)
exercises = models.ManyToManyField(Exercise, **related_name='exercises'**, blank=True)
cardio = models.ManyToManyField(Cardio, blank=True)
def __str__(self):
return self.title
I came across the same problem and now I know the answer:
class WorkoutSerializer(serializers.ModelSerializer):
exercise = serializers.SlugRelatedField(read_only=True, slug_field='name', many=True)
cardio = serializers.SlugRelatedField(read_only=True, slug_field='name', many=True)
class Meta:
fields = ('id', 'title', 'description', 'exercise', 'cardio', )
It must be exercise and cardio which are the exact fields in workout (rather than exercises and cardios. So basically, the answer from Mwangi Kabiru above is correct except that slug_field must be "name" and not title because the fields in class Exercise and Cardio are name, not title.
Just like Youtube, I want to see what categories my post(outfit) is in.
My approach is...
Get whole list of categories belong to user
Get whole list of categories belong to user and the post
compare them and return JSON
While building a Seriailizer, I feel like I'm totally stock. So frustraing... :(
Please feel free to change my approach if you have any better idea.
Expected JSON outputis below
{
...
"categories": [
{
"id": 1,
"name": "asd"
"added": True <- since the post is added to 'asd'
},
{
"id": 2,
"name": "workout"
"added": True
},
...
{
"id": 5,
"name": "bgm"
"added": False <- since the post is not added to 'bgm'
},
]
}
Here is views.py
class OutfitDetailView(generics.RetrieveAPIView):
queryset = Outfit.objects.all()
serializer_class = OutfitDetailSerializer
lookup_field = 'pk'
Here is serializers.py (this is a problem)
class OutfitDetailSerializer(serializers.ModelSerializer):
...
def get_categories(self, obj):
# Right Here! Get whole categories associated with user
all_categories = Category.objects.filter(owner=self.context['request'].user)
# And the categories associated with user AND the post
categories = obj.categories.filter(owner=self.context['request'].user)
return CategorySerializer(categories, many=True).data
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = (
'id',
'name',
'added' <- here I want to add 'added' field but I don't know how.
)
In case you need a model
class Outfit(models.Model):
...
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
content = models.CharField(max_length=30)
...
class Category(models.Model):
name = models.CharField(max_length=20)
owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
outfits = models.ManyToManyField(Outfit, related_name="categories", blank=True)
main_img = models.ImageField(
upload_to=upload_location_category,
null=True,
blank=True)
...
if i understand you correct, you can try:
class OutfitDetailSerializer(serializers.ModelSerializer):
def get_categories(self, obj):
user = self.context['request'].user
categories = Category.objects.filter(owner=user)
added = categories.extra(select={'added': '1'}).filter(outfits__pk=obj.pk)
added = list(added.values('added', 'name', 'id'))
added_f = categories.extra(select={'added': '0'}).exclude(outfits__pk=obj.pk)
added_f = list(added_f.values('added', 'name', 'id'))
categories = added + added_f
return CategorySerializer(categories, many=True).data
Note in the values you need add all fields you need for the CategorySerializer
class CategorySerializer(serializers.ModelSerializer):
added = serializers.BooleanField()
class Meta:
model = Category
fields = (
'id',
'name',
'added'
)
I am quite new to Django and Django Rest Framework.
What I like to do in my project is display intermediate model's information using ListAPIView and also include detailed information about another Model connected to the intermediate model with a Foreign Key Relationship in the form of nested representation.
I have 3 models in my project, User, Content, Bookmark.
My model goes like below.
class MyUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=50)
joined_date = models.DateTimeField(auto_now_add=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
facebook_id = models.CharField(max_length=50, blank=True)
is_facebook = models.BooleanField(default=False)
is_active = models.BooleanField(default=False)
class Content(models.Model):
seq = models.CharField(max_length=20, unique=True)
title = models.CharField(max_length=100, null=True)
start_date = models.DateField(null=True)
end_date = models.DateField(null=True)
place = models.TextField(null=True)
realm_name = models.TextField(null=True)
area = models.TextField(null=True)
price = models.TextField(null=True)
content = models.TextField(null=True)
ticket_url = models.TextField(null=True)
phone = models.TextField(null=True)
thumbnail = models.TextField(null=True)
bookmarks = models.ManyToManyField(User, through='Bookmark')
The last model, an intermediate model to connect MyUser and Content.
class Bookmark(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.ForeignKey(Content, on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
description = models.CharField(max_length=200, null=True)
class Meta:
unique_together = (('user', 'content'),)
ordering = ['-created_date']
def __str__(self):
return '{} bookmarked by user {}'.format(self.content, self.user)
I want to use BookmarkListAPIView to show certain user's bookmark information and some detailed information of Contents that the user added such as title, price, and start_date.
Below is my Serializer and ListAPIView.
class BookmarkListView(generics.ListAPIView):
queryset = Bookmark.objects.all()
serializer_class = BookmarkSerializer
pagination_class = DefaultResultsSetPagination
def get_queryset(self):
user = self.request.user
return user.bookmark_set.all().order_by('-created_date')
class BookmarkedContentSerializer(serializers.ModelSerializer):
class Meta:
model = Content
fields = ('title', 'start_date', 'price')
class BookmarkSerializer(serializers.ModelSerializer):
content = BookmarkedContentSerializer(many=True, read_only=True)
#bookmark = BookmarkedContentSerializer(many=True, read_only=True)
class Meta:
model = Bookmark
fields = ('content', 'user')
Currently, the API gives me results just like below.
{
"count": 6,
"next": null,
"previous": null,
"results": [
{
"content": 8,
"user": 4
},
{
"content": 6,
"user": 4
},
{
"content": 1,
"user": 4
},
{
"content": 2,
"user": 4
},
{
"content": 3,
"user": 4
},
{
"content": 10,
"user": 4
}
]
}
As mentioned above, I want to use the content ID to fetch more detailed information about each bookmark instance.
It will look like
{
"count": 6,
"next": null,
"previous": null,
"results": [
{ "content_id": 4
"title": A,
"price": 10$,
"user": 4
},
{
"content_id": 6,
"title": B,
"price": 13$,
"user": 4
},
{
"content_id": 1,
"title": C,
"price": 4$,
"user": 4
},
]
}
I tried many things written in the DRF doc but was not able to find any materials related to my situation.
If you have any idea, please help me.
I think the key you're missing is the source keyword argument for each field. Here's some docs on it for you: http://www.django-rest-framework.org/api-guide/fields/#source
And I believe this implementation should work for you or at least be very close:
class BookmarkSerializer(serializers.ModelSerializer):
content_id = serializers.IntegerField(source='content.id')
title = serializers.CharField(source='content.title')
price = serializers.CharField(source='content.price')
user = serializers.IntegerField(source='user.id')
class Meta:
model = Bookmark
fields = ('content_id', 'user', title', 'price')
I am new to Django.I am using Django REST Framework and I need to get json from serializers in the following format:
{
"result" : 200,
"categories" : [{
"id" : "V001",
"name": "Vehicles",
"description": "All types of motor and non-motor vehicles",
"icon": "http://images.maa.ae/static/icons/vehicles.png",
"subcategories": [{
"id" : "V00101",
"name": "Cars",
"description": "Motor Cars",
"subcategories": [{
"id" : "V0010101",
"name": "Passenger Cars",
"description": "All types of passenger cars"
}]
},
{
"id" : "V00102",
"name": "Bikes",
"description": "Bikes",
"subcategories": [{
"id" : "V0010201",
"name": "Motor Bikes",
"description": "All kinds of motor bikes"
},
{
"id" : "V0010202",
"name": "Sports Bikes",
"description": "All kinds of sports bikes"
}]
}]
}]
}
My Model class is:
class Category(models.Model):
_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
description = models.CharField(max_length=150,null=True, blank=True)
icon = models.ImageField(upload_to='icons',null=True, blank=True)
parent_id = models.ForeignKey('self', null=True, blank=True)
and my serializers class is:
class CategorySerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Category
fields = ('_id', 'name', 'description', 'icon')
From above implementation I can get Array of Category objects in JSON format.
But I don't know how can I include 'subcategories' in my serializer class.Please help me to get an output similar to json shown in above format.
Use Django Rest Framework.
http://www.django-rest-framework.org/
Keep your code DRY
from rest_framework import serializers
class CategoriesSerializer(serializers.ModelSerializer):
class Meta:
model = Categories
Views
from rest_framework import viewsets
from .serializers import CategoriesSerializer
class CategoryViewSet(viewsets.ModelViewSet):
queryset = Categories.objects.all()
serializer_class = CategoriesSerializer
Example
models.py:
class Sprints(models.Model):
name = models.CharField(default='', blank=True, max_length=90)
description = models.TextField(default='')
class Tasks(models.Model):
date_posted = models.DateTimeField(auto_now_add=True)
name = models.CharField(default='', blank=True, max_length=90)
end = models.DateTimeField(null=True, blank=True)
sprint = models.ForeignKey(Sprints, related_name='tasks')
class Meta:
ordering = ['-date_posted']
unique_together = ['name', 'sprint']
serializers.py:
class TaskSerializer(serializers.ModelSerializer):
class Meta:
model = Tasks
fields = ('id', 'name', 'date_posted', 'end')
class SprintSerializer(serializers.ModelSerializer):
tasks = TaskSerializer(many=True, read_only=True)
class Meta:
model = Sprints
fields = ('id', 'name', 'description', 'tasks')
views.py
class SprintsViews(viewset.ModelViewSet):
queryset = Sprints.objects.all()
serializer_class = SprintSerializer
You can also add a slug field to the models to make them easier to deal with, but this is how for now :)
You should consider having a look at THIS. The usage of depth meta attribute enables you to retrieve related objects to the depth you set.
By doing so, it automatically retrieves nested data.
It is very convenient to avoid using serializers in both sides and thus having ImportError caused by cycles too.
Use related_name and on_delete on your parent_id field in models.py file
class Category(models.Model):
_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=128)
description = models.CharField(max_length=150,null=True, blank=True)
icon = models.ImageField(upload_to='icons',null=True, blank=True)
parent_id = models.ForeignKey('self', null=True, blank=True, related_name='children', on_delete=models.CASCADE)
Now add the packege pip install djangorestframework-recursive on your project
serializers.py:
##################################
from rest_framework_recursive.fields import RecursiveField
##################################
class CategorySerializer(serializers.ModelSerializer):
children = RecursiveField(many=True)
class Meta:
model = Category
fields = ('_id', 'name', 'description', 'icon', 'parent_id', 'children')
views.py (Serializers view file):
class CategoryListAPIView(generics.ListAPIView):
serializer_class = CategorySerializer
queryset = Category.objects.filter(parent_id__isnull=True)
urls.py:
############################
############################
urlpatterns = [
path('category/', views.CategoryListAPIView.as_view(), name="categoryAPI"),
######################################
]