How do I get the object from the reverse relation in serializers.py?
I have a model like this
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
def __str__(self):
return self.title
class Category(models.Model):
post = models.ForeignKey(Post, related_name='category')
name = models.CharField(max_length=200)
slug = models.SlugField(max_length=70, unique=False)
def __str__(self):
return self.title
And from the Django Rest Framework documentation, I can access the category directly through the related name and this is my serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('title','content','category')
The problem is the the view only return the category post id:
HTTP 200 OK
Vary: Accept
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"title": "Post title 1",
"content": "test content..blah blah blah",
"category": [
1
]
}
]
}
How can I get the category name and slug??
The related_name will only return id's, and this is not wrong at all. If you want the full representation, you will also need to add a serialized version of each child object in your parent. Like this:
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True, required=False)
So you first need to have a CategorySerializer, and then you must add the relationship in the PostSerializer. All parameters are optional. Here is a small example.
P.S. : I suggest using 'categories' as related_name, since you can have more than just one.
Related
I need to extract categories and subcategories in Post serializer, because I need to put pagination, if I put pagination in category view, different amount of posts will come, so I need to put pagination in Post view, I need to return response so that it looks like this
and I want to create rest api to return nested json like this
[
{
"id": 1,
"title": "Taomlar",
"subcat": [
{
id: 2,
title: "Milliy",
post: [
{
id: 1,
title: 'Palov',
summa: 300000,
...
},
{
id: 2,
title: 'Palov',
summa: 300000,
...
},
]
},
]
}
]
models.py
class Category(Base):
title = models.CharField(max_length=200)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children')
def __str__(self):
return self.title
class Post(Base):
title = models.CharField(max_length=225)
category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='post')
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
Can anyone please give me the solution for this problem
As much as I understand you need to get each Category with it's related posts.
Overwriting like this :
class CategorySerializer(serializers.ModelSerializer):
posts = serializers.SerializerMethodField()
class Meta:
model = Category
fields = (
'id',
'title',
'posts',
)
def get_posts(self, obj):
serializer = PostSerializer(obj.posts.all(), context=self.context, many = True)
return serializer.data
you can read more about nested serializer in Django Rest Framework
you should make it in this way
class CategorySerializer(serializers.ModelSerializer):
post_serializer = PostSerializer(many=True)
class Meta:
model = Category
fields = ['your_field', 'post_serializer']
this a basic example, try to read more about it to know how you can implement nested relations
So let's say I have this 2 models
Poll:
class Poll(models.Model):
title = models.CharField(max_length=255)
is_active = models.BooleanField(default=True)
is_available = models.BooleanField(default=True)
date_created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
Options:
class Option(models.Model):
title = models.CharField(max_length=255)
poll = models.ForeignKey(Poll, on_delete=models.CASCADE)
def __str__(self):
return f'{self.poll.title} - {self.title}'
These two objects are connected with ForeignKey. Now let's say I have a frontend that renders both options and the question (title of the poll) but I only want make a single API call to the backend. Basically I need the API to looked like this:
{
"title": "Which is the best frontend framework?",
"options":[
{
"id": 1,
"title": "React"
},
{
"id": 2,
"title": "Vue"
},
{
"id": 3,
"title": "Angular"
}
]
}
How what method/technique should I use to merge two objects like this?
Based on DRF docs
class OptionSerializer(serializers.ModelSerializer):
class Meta:
model = Option
fields = ['id', 'title']
class PollSerializer(serializers.ModelSerializer):
options = OptionSerializer(source='option_set', many=True)
class Meta:
model = Poll
fields = ['title', 'options']
I use ModelSerializer to return serialized data of a model:
class CategorySerializer(serializers.ModelSerializer):
category_count = serializers.SerializerMethodField('_category_count')
def _category_count(self, obj):
category_obj = Category.objects.get(id=obj.id)
questions_count = Question.objects.filter(category=category_obj).count()
return questions_count
class Meta:
model = Category
fields = ["id", "category", "category_count"]
What I get write now is here:
[{
"category": "orzecznictwo lekarskie",
"category_count": 0,
"id": 9
},
{
"category": "zdrowie publiczne",
"category_count": 1,
"id": 10
}
]
I want to remove objects with category_count == 0 from serialization
[
{
"category": "zdrowie publiczne",
"category_count": 1,
"id": 10
}
]
Thank you for your help.
EDIT:
class Question(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
question = models.TextField(blank=True, null=True)
(...)
class Category(models.Model):
category = models.TextField()
category_slug = models.CharField(max_length=50)
You're generating the field inside the serializer and then want to discard the object. That's not how things work: serializers merely change representation of data. Which data is displayed is determined in views.
class CategoryListView(generics.ListAPIView):
def get_queryset(self):
return models.Category.objects.annotate(
question_count=Count("question_set")
).exclude(question_count=0)
And in the serializer, you don't have to create the count any more and add the 'question_count' to Meta.fields.
Recommended reading.
Edit:
Forgot that DRF will check the field on the model and fail, so you need to declare the field:
class CategoryWithQuestionCount(serializers.ModelSerializer):
question_count = serializers.IntegerField(read_only=True)
class Meta:
model = models.Category
fields = ("pk", "category", "question_count")
I'm not sure about removing it in the ModelSerializer. But you can try overriding get_queryset() or changing the queryset in your View:
queryset = Class.objects.exclude(dzial_liczba_pytan=0)
I am having trouble in showing cross relation data in my api end-point
this is my serlization class:
class ArticleSerializer(serializers.ModelSerializer):
# these two variables are assigned coz, author and category are foregin key
author = serializers.StringRelatedField()
category = serializers.StringRelatedField()
class Meta:
model = Article
fields = '__all__'
Above serialization working fine but not satisfy needs.
And this is my models
from django.db import models
import uuid
class Organization(models.Model):
organization_name = models.CharField(max_length=50)
contact = models.CharField(max_length=12, unique=True)
def __str__(self):
return self.organization_name
class Author(models.Model):
name = models.CharField(max_length=40)
detail = models.TextField()
organization = models.ForeignKey(Organization, on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
class Category(models.Model):
name = models.CharField(max_length=100)
def __str__(self):
return self.name
class Article(models.Model):
alias = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='author')
title = models.CharField(max_length=200)
body = models.TextField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
this is my views:
class ArticleSingle(RetrieveAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'alias'
and currently my end-point showing like these but i dont like it:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"alias": "029a4b50-2e9a-4d35-afc6-f354d2169c05",
"author": "jhon doe",
"category": "sic-fi",
"title": "title goes here",
"body": "this is body"
}
I want the end-point should show the data with author details like, author, organization_name, contact, etc if you dont understand it, please see my models how i designe it.
The end-point should show each and every related data that with foreign key, i hope you got my issue, thanks for your time
What you are looking for is nested serialization.
Rather than specifying author and category as StringRelatedFields, you should assign them as serializer classes (the same applies to the Organization model):
class OrganizationSerializer(serializers.ModelSerializer):
...
class Meta:
model = Organization
fields = '__all__'
class AuthorSerializer(serializers.ModelSerializer):
organization = OrganizationSerializer()
...
class Meta:
model = Author
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
...
class Meta:
model = Category
fields = '__all__'
class ArticleSerializer(serializers.ModelSerializer):
author = AuthorSerializer()
category = CategorySerializer()
class Meta:
model = Article
fields = '__all__'
This mechanism will serialize your data as you desire:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
{
"alias": "029a4b50-2e9a-4d35-afc6-f354d2169c05",
"author": {
"name": "jhon doe",
"organization": {
"organization_name": "..."
}
...
}
"category": {
"name": "sic-fi"
}
"title": "title goes here",
"body": "this is body"
}
Note: If you want to perform POST requests for creation of those resources, you can find some useful information here.
django==1.9.10
djangorestframework==3.4.7
Here are the models.
class Dealer(models.Model):
first_name = models.CharField(max_length=255)
...
class Form(models.Model):
dealer = models.ForeignKey(Dealer, blank=True, null=True)
...
Here are the serializers.
class FormShortSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Form
fields = ('url',)
class DealerSerializer(serializers.HyperlinkedModelSerializer):
forms = FormShortSerializer(source='form_set', many=True)
class Meta:
model = Dealer
Here are the viewsets.
class DealerViewSet(viewsets.ModelViewSet):
queryset = Dealer.objects.all()
serializer_class = DealerSerializer
class FormViewSet(viewsets.ModelViewSet):
queryset = Form.objects.all()
serializer_class = FormSerializer
So, the API response for Dealer URL is this.
{
"url": "http://localhost:8000/rest-api/dealer/1/",
"forms": [
{
"url": "http://localhost:8000/rest-api/form/1/"
},
{
"url": "http://localhost:8000/rest-api/form/2/"
}
],
"first_name": "First Name"
}
But, when I do PATCH to this URL, say, updating the first_name to something else, for some reason DRF sets dealer field on all forms to null, and the response looks like this.
{
"url": "http://localhost:8000/rest-api/dealer/1/",
"forms": [], # Forms are empty.
"first_name": "Changed Name" # Name changed.
}
Any idea why guys? None of the models overload save methods or doing any magic behind the scenes. Only Meta class __unicode__ methods are defined there, but I think it's irrelevant here, so I haven't included it in the code.
Thanks ahead.