I want to show the name of a field and not the ID of it, so I tried a couple of different methods but with nothing worked for me. I couldn't figure out why or find a suitable solution for this, so here it goes:
Basically, my views returns a null field when in fact it is not null. Code bellow.
tags = TagListSerializer() # Returns null
tags = serializers.Field(source='tags.name') # Returns null
tags = TagListSerializer # Doesn't change anything
Comercial inherits from Project which is abstract:
class Comercial(Project):
name = models.CharField()
Project is abstract:
class Project(models.Model):
tags = models.ManyToManyField(Tag, blank=True, editable=True)
The Tag Model:
class Tag(models.Model):
name = models.CharField(null=False)
The Comercial View which searches by tag:
class ListComercialAPIView(ListAPIView):
serializer_class = ComercialListSerializer
pagination_class = StandardResultsSetPagination
def get_queryset(self):
tag = self.request.GET.get('tag')
if(tag!=None):
return Comercial.objects.filter(tags__name=tag)
else:
return Comercial.objects.all()
The Tag Serializer:
class TagListSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('__all__')
And finally the ComercialSerlialzier:
class ComercialListSerializer(serializers.ModelSerializer):
tags = TagListSerializer()
class Meta:
model = Comercial
fields = ('name', 'tags')
At listing the Comercial object I expected something like this:
{
"name": "Example Comercial Name"
"tags": {
"name":"Example Tag Name"}
}
But all i get is:
{
"name": "Example Comercial Name"
"tags": {
"name": null
}
Thanks in advance!
Well, adding many=True to the serializer solved my problem...dumb me
class ComercialListSerializer(serializers.ModelSerializer):
tags = TagListSerializer(many=True)
Related
I am trying to retrieve values from my database, but I am not getting the response I am looking for. My question is how do I get the value that is under Image in my database entry?
Database entry:
ID name image
-----------------------------
11 "example" "myimager.jpg"
When I retrieve a list, I get this response for this entry:
{ "image": null, "name": "example" }
Model:
class Images(models.Model):
name = models.CharField(max_length=255)
image = models.ImageField()
View:
class GetImageViewSet(viewsets.ReadOnlyModelViewSet):
"""
This ViewSet automatically provides `list` and `detail` actions.
"""
queryset = Images.objects.all()
serializer_class = ImageSerializer
permission_classes = (IsAuthenticated,)
Serializer:
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Images
fields = ['image', 'name']
I have 2 models
class Tag(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Question(models.Model):
ques_id = models.IntegerField(default=0)
name = models.CharField(max_length=255)
Tag_name = models.ManyToManyField(Tag)
class Meta:
ordering = ['ques_id']
def __str__(self):
return self.name
searlizers.py
class TagSerializers(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class QuestionSerializers(serializers.ModelSerializer):
class Meta:
model = Question
fields = '__all__'
This is my searilzers class.
I want the response like
{
"id": 1,
"name": "QUES 1",
"tags": [{
"id": 1,
"name": "Abcd"
}]
}
what will be query to get Fetch 10 questions, given some input tag ids
e.g Tag_id = 1 or 2 or 3.
You need to add tags field as another serializer data to your QuestionSerializer.
Your QuestionSerializer code should look like that:
class QuestionSerializers(serializers.ModelSerializer):
Tag_name = TagSerializer(many=True)
class Meta:
model = Question
fields = '__all__'
If you want exactly tags name in response, you can specify SerializerMethodField like that:
class QuestionSerializer(serializers.ModelSerializer):
tags = serializers.SerializerMethodField()
get_tags(self, instance):
return TagSerializer(instance.Tag_name, many=True).data
class Meta:
model = Question
fields = ('ques_id', 'name', 'tags')
First: I would suggest that you refactor your Question Model, since it has a ques_id, and I think it is considered a duplicate (since Django already creates an id field by default)
Then You need to change your ManyToManyField's name to tags, and makemigrations, then migrate
class Question(models.Model):
name = models.CharField(max_length=255)
tags = models.ManyToManyField(Tag)
class Meta:
ordering = ['id']
def __str__(self):
return self.name
# run make migrations
python manage.py makemigrations <<<YOUR QUESTIONS APP NAME>>>
# It will prompt you to check if you change the many to many relationship say yes
Did you rename question.Tag_name to question.tags (a ManyToManyField)? [y/N] y
# Then Run migrate
python manage.py migrate
Second: Update your QuestionSerializers to make the tags field serialize the relation
class QuestionSerializers(serializers.ModelSerializer):
tags = TagSerializers(many=True)
class Meta:
model = Question
fields = '__all__'
This way you make your code cleaner. And you are good to go.
Answer Updated (Filtering, and Pagination)
Now if you wanted to filter questions based on provided tag ids.
You need to use PageNumberPagination for your view, and for filtering use DjangoFilterBackend.
I recommend you to make them the default of DRF settings.
Make sure you have django-filter installed.
# In your settings.py
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'DEFAULT_FILTER_BACKENDS': (
'django_filters.rest_framework.DjangoFilterBackend',
),
'PAGE_SIZE': 10
}
Now create your custom filter
# Inside filters.py
import re
import django_filters
from questions.models import Question
class QuestionsFilterSet(django_filters.FilterSet):
tags = django_filters.CharFilter(field_name='tags__id', method='tags_ids_in')
def tags_ids_in(self, qs, name, value):
ids_list = list(map(lambda id: int(id), filter(lambda x: x.isdigit(), re.split(r'\s*,\s*', value))))
return qs.filter(**{f"{name}__in": ids_list}).distinct()
class Meta:
model = Question
fields = ('tags',)
Now use ListAPIView to list your questions
class QuestionsListAPIView(ListAPIView):
queryset = Question.objects.all()
serializer_class = QuestionSerializers
filter_class = QuestionsFilterSet
Now if you hit
http://<PATH_TO_VIEW>?tags=1,2,3
You will receive all Questions that have tag id 1, 2, or 3.
and all of them will be paginated 10 results at a time.
You just need to write a field tags like this in your QuestionSerializers
class QuestionSerializers(serializers.ModelSerializer):
tags = TagSerializers(source='Tag_name', many=True)
class Meta:
model = Question
fields = ('id', 'name', 'tags')
It will give you response like this
{
"id": 1,
"name": "QUES 1",
"tags": [{
"id": 1,
"name": "Abcd"
}]
}
Your query to fetch on the basis of tags will be like this
Question.objects.filter(Tag_name__in=[1, 2, 4])
I have this serializer that represents content abstract where I would like to add an hyperlink field that is not in the model, but calculated by the framework linked to the ContentsSerializer.
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
content_url = ???
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
class ContentsSerializer(serializers.HyperlinkedModelSerializer):
categories = CategoriesContentsSerializer(read_only=True, many=True)
class Meta:
model = Contents
fields = ('content_id','content_title', 'content_abstract', 'content_body','start_date','stop_date','last_date','content_status','version','sections', 'images','attaches','categories')
I would like to have a result like this:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
"content_title": "this is the title",
"content_abstract": "This is the abstract",
"start_date": "2005-01-12",
"stop_date": "3000-01-12",
"last_date": "2019-02-27T09:40:38Z",
"content_status": "PUBLISHED",
"version": 0
},
I think that instead of define your own custom field you should use the manually specify the view_name for each nested resource as part of extra_kwargs.
I think that you simply could do something like that:
class ContentsAbstractSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Contents
fields = ('content_url','content_id','content_title', 'content_abstract','start_date','stop_date','last_date','content_status','version')
extra_kwargs = {
'content_url': {'view_name': 'name_of_your_detail_view'},
}
Output:
{
"content_url":"http://mysite/Content/125",
"content_id": 125,
....
},
The Resource: 'HyperlinkedModelSerializer' with custom nested
'view_name's does not work in combination with
'depth'
Oficial Resource: How hyperlinked views are determined
Let's say I have some models:
class A(models.Model):
...
class B(models.Model):
my_reference_to_a = models.ForeignKey(A)
b_field_1 = ...
b_field_2 = ...
class C(models.Model):
my_reference_to_b = models.ForeignKey(B)
c_field_1 = ...
...
In my serializer for C, I want to include all of the fields in C, all the fields in B, as well as the reference to A in B (but not the reference to B in C), so the JSON API output would be something like this:
{
"data": [{
"type": "C",
"id": "1",
"attributes": {
"b_field_1": "...",
"b_field_2": "...",
"c_field_1": "..."
},
"relationships": {
"a": {
"data": {
"type": "A",
"id": "1"
}
}
}
}],
...
}
How would I go about this? I've already tried doing something like this inside my serializer for C:
A = ASerializer(source='my_reference_to_b.my_reference_to_a')
But that doesn't work, as DRF doesn't seem to support dotted paths for sources. I've also tried supplying a method that returns the proper model (the model is valid inside the method) as the source, but that outputs the reference in the JSON as:
"a": {
"data": null
}
On my A model, I also have a reference to another model, D, that is not explicitly stated in A, but is instead defined in D as a OneToMany relationship (Many D models to one A model) with a resource_name on the ForeignKey declared in D, and trying to reference this in C to include that relationship in the JSON doesn't work, either. I get this error (trying to reference it by doing D = DSerializer(source='B.D')):
'RelatedManager' object has no attribute 'B'
Any help would be greatly appreciated.
I figured it out. Just answering my own question in case anyone lands on this page and they need help.
You need to use the SerializerMethodResourceRelatedField from the Django Rest Framework JSON API. I had tried the regular ResourceRelatedField without it working, looking through the source code showed me that ResourceRelatedField doesn't support dotted paths. Instead, use SerializerMethodResourceRelatedField with a source pointing to a method that returns the desired relation.
# Model
from django.db import models
class Album(models.Model):
album_name = models.CharField(max_length=100)
artist = models.CharField(max_length=100)
class Track(models.Model):
album = models.ForeignKey(Album, related_name='tracks',on_delete=models.DO_NOTHING)
order = models.IntegerField()
title = models.CharField(max_length=100)
duration = models.IntegerField()
class Meta:
unique_together = ('album', 'order')
ordering = ['order']
def __unicode__(self):
return '%d: %s' % (self.order, self.title)
# View
from rest_framework import generics,viewsets
from api.models import Album
from api.serializers import AlbumSerializer
class TracksView(generics.ListAPIView):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
class TracksView(generics.CreateAPIView):
queryset = Album.objects.all()
serializer_class = AlbumSerializer
# serializers
from rest_framework import serializers
from api.models import Album
class AlbumSerializer(serializers.ModelSerializer):
tracks = serializers.StringRelatedField(many=True,)
class Meta:
model = Album
fields = ('album_name', 'artist', 'tracks')
I would like to add some logic to my serializer.py.
Currently it creates duplicate tags (giving a new ID to the item, but often it will match a tag name already).
In plain english
if exists:
# Find the PK that matches the "name" field
# "link" the key with Movie Class item
else:
# Create the "name" inside of Tag class
# "link" the key with Movie Class item
The data being posted looks like this:
{
"title": "Test",
"tag": [
{
"name": "a",
"taglevel": 1
}
],
"info": [
]
}
Models.py
class Tag(models.Model):
name = models.CharField("Name", max_length=5000, blank=True)
taglevel = models.IntegerField("Tag level", blank=True)
def __str__(self):
return self.name
class Movie(models.Model):
title = models.CharField("Whats happening?", max_length=100, blank=True)
tag = models.ManyToManyField('Tag', blank=True)
def __str__(self):
return self.title
Serializers
class MovieSerializer(serializers.ModelSerializer):
tag = TagSerializer(many=True, read_only=False)
class Meta:
model = Movie
fields = ('title', 'tag', 'info')
def create(self, validated_data):
tags_data = validated_data.pop('tag')
movie = Movie.objects.create(**validated_data)
for tag_data in tags_data:
movie.tag.create(**tag_data)
return movie
This will probably solve your issue:
tag = Tag.objects.get_or_create(**tag_data)[0]
movie.tag.add(tag)
get_or_create function returns tuple (instance, created), so you have to get instance with [0].
So the full code is:
def create(self, validated_data):
tags_data = validated_data.pop('tag')
movie = Movie.objects.create(**validated_data)
for tag_data in tags_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
movie.tag.add(tag)
return movie
To make function case insensitive, you have to do "get or create" manually (the main part is to use __iexact in filter):
tag_qs = Tag.objects.filter(name__iexact=tag_data['name'])
if tag_qs.exists():
tag = tag_qs.first()
else:
tag = Tag.objects.create(**tag_data)
movie.tag.add(tag)