getting null in object of many to many relation object django - python

I'm saving an object with many to many relations in the database but when I'm fetching it so so it returns me IDs of that relational object but I want to the whole object instead, so I have added a property in the serializer and after that, it returns me the object.name only and the value of it is null, I don't know why it is behaving like this? anyone can help?
views.py
queryset = Tag.objects.filter(project_id=project_id).all()
serializer = TagSerializer(queryset, many=True)
return Response(serializer.data)
serializers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
# fields = '__all__'
fields = ['id', 'name', 'description', 'singleline']
singleline = SinglelineSerializer(read_only=True)
models.py
class Tag(models.Model):
name = models.CharField(max_length=255, default='')
description = models.CharField(max_length=255, default='')
singleline = models.ManyToManyField(Singleline)
class Meta:
db_table = 'tags'

A Tag can have many Singlelines, you need to work with many=True since it is a collection:
class TagSerializer(serializers.ModelSerializer):
singleline = SinglelineSerializer(read_only=True, many=True)
class Meta:
model = Tag
fields = ['id', 'name', 'description', 'singleline']
You can improve efficiency of the view by prefetcing the singleline relation and thus load all the related Singlelines in bulk:
queryset = Tag.objects.filter(project_id=project_id).prefetch_related('singleline')
serializer = TagSerializer(queryset, many=True)
return Response(serializer.data)

Related

I want to nest a model into the Serialiazer. Category' object has no attribute 'brand_set'

I want to nest a model into the serializer. Like on list of Category, there should be fields from Brand model, But I m getting error by on setting this way ?
models.py
class Category(models.Model):
title = models.CharField(max_length=50)
timestamp = models.DateTimeField(auto_now_add=True)
#....
class Brand(models.Model):
title = models.CharField(max_length=50)
category = models.ForeignKey(
Category, blank=True, null=True, on_delete=models.SET_NULL, related_name="category")
#....
Serializers
class CategorySerializerNested(serializers.ModelSerializer):
brands = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Category
fields = '__all__'
def get_brands(self, obj):
brands = obj.brand_set.all() #this thin popping error how to fix that....
serializer = BrandSerializerNested(brands, many=True)
return serializer.data
class BrandSerializerNested(serializers.ModelSerializer):
products = serializers.SerializerMethodField(read_only=True)
def get_products(self, obj):
products = obj.product_set.all()
serializer = ProductSerializer(products, many=True)
return serializer.data
class Meta:
model = Brand
fields = '__all__'
View.py
#api_view(['GET'])
def getCategoryWithBrands(request, pk):
category = Category.objects.get(id=pk)
serializer = CategorySerializerNested(category, many=False)
return Response(serializer.data)
url.py
path('nested/<str:pk>/', views.getCategoryWithBrands,
name="category-with-brands"),
Error:
AttributeError: 'Category' object has no attribute 'brand_set'
[02/Feb/2022 03:24:49] "GET /api/v1/categories/nested/1/ HTTP/1.1" 500 125121
I'm sure im doing something illogical but i dont know now, please help me to fix this , if there's any better way to do that please also help there as well. Thanks
Since you specified related_name='category', you obtain the related Brands with:
def get_brands(self, obj):
brands = obj.category.all()
serializer = BrandSerializerNested(brands, many=True)
return serializer.data
But that does not make much sense: the related_name=… [Django-doc] specifies the name of the relation in reverse so obtaining the Brands for a given Category, you thus can rename these to:
class Brand(models.Model):
category = models.ForeignKey(
Category,
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name='brands'
)
#…
and work with a subserializer:
def get_brands(self, obj):
brands = obj.brands.all()
serializer = BrandSerializerNested(brands, many=True)
return serializer.data
or just define BrandSerializerNested first and use:
class CategorySerializerNested(serializers.ModelSerializer):
brands = serializers.BrandSerializerNested(read_only=True)
class Meta:
model = Category
fields = '__all__'

ManyToManyField does not show

Want to use REST API to populate my tables but my field does not display on the API page.
Models (Series, Roster):
class Series(models.Model):
(...)
def __str__(self):
return self.title
class Roster(models.Model):
(...)
series = models.ManyToManyField(Series)
(...)
def __str__(self):
return self.name
Serializers:
class SeriesSerializer(serializers.ModelSerializer):
class Meta:
model = Series
fields = ('id', 'title', 'icon')
read_only_fields = ('slug',)
class RosterSerializer(serializers.ModelSerializer):
series = SeriesSerializer(many=True, read_only=True)
class Meta:
model = Roster
fields = ('id', 'name', 'number', 'primary_color', 'secondary_color', 'image', 'series')
Views:
class SeriesView(viewsets.ModelViewSet):
serializer_class = SeriesSerializer
queryset = Series.objects.all()
class RosterView(viewsets.ModelViewSet):
serializer_class = RosterSerializer
queryset = Roster.objects.all()
Unsure where I am mistepping here.
So it turns out that all I needed to do was remove
series = SeriesSerializer(many=True, read_only=True)
and adjust my series field to
series = models.ForeignKey(Series, on_delete=models.CASCADE, blank=True, null=True)
No idea why this ended up working though so an explanation would still be cool.

How to make a ManyToMany serialization to be able to create a object in the POST request?

I'm trying to create a serialize to handle a ManyToMany relation, but it's not working. I have read the documentation and I probably doing something wrong. Also I have read the answers here.
Here are my models.
class Author(models.Model):
name = models.CharField(verbose_name="Name", max_length=255)
class Book(models.Model):
author = models.ForeignKey(
Author,
related_name="id_author",
blank=True,
null=True,
on_delete=models.PROTECT)
price = models.FloatField(verbose_name="Price")
class FiscalDocument(models.Model):
seller = models.ForeignKey(
User,
related_name="user_id",
on_delete=models.PROTECT)
book = models.ManyToManyField(Book)
My serializer:
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ('id', 'name')
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ('id', 'author', 'price')
def to_representation(self, instance):
response = super().to_representation(instance)
response['author'] = AuthorSerializer(instance.author).data
return response
class FiscalDocumentSerializer(serializers.ModelSerializer):
book = BookSerializer()
class Meta:
model = FiscalDocument
fields = ('id', 'seller', 'book')
def create(self, validated_data):
book_data = validated_data.pop('book')
fiscal_document = FiscalDocument.objects.create(**validated_data)
Book.objects.create(FiscalDocument=FiscalDocument,**medicine_data)
return fiscal_document
When I try to access the endpoint of the FiscalDocument, django-rest-framework is throwing an error:
Got AttributeError when attempting to get a value for field price on serializer BookSerializer. The serializer field might be named incorrectly and not match any attribute or key on the ManyRelatedManager instance. Original exception text was: ManyRelatedManager object has no attribute price.
If anyone can help XD.

How to serialize result of objects.filter django based on foreign key object

I have two models one with a foreign key to the other:
class Booking(models.Model):
type_course_requested = models.ManyToManyField(TypePost, blank=True)
.....
#Presentation Message
message = models.CharField(max_length=1000)
class BookingDemand(models.Model):
booking = models.ForeignKey(Booking, on_delete=models.CASCADE, null=True, blank=True)
I want to get bookingdemands based on certain condition, and then serializing to have something like the code below:
{ 'booking1': { 'key1':...
'bookingDemands': {....}
},
'booking2': {...}
}
Filtering is done like this:
bookings=BookingDemand.objects.filter(booking__post__person=self.request.user)
which returns a queryset, but I can't find how to serialize them to have each booking separatly as mentionned above.
Create a serializer and set depth=1 as below
from rest_framework import serializers
class BookingDemandSerializer(serializers.ModelSerializer):
class Meta:
model = BookingDemand
fields = '__all__'
depth = 1
then serialize your queryset as
bookings = BookingDemand.objects.filter(booking__post__person=self.request.user)
booking_serializer = BookingDemandSerializer(bookings, many=True)
booking_serializer.data # here is the serialized data
UPDATE
# serializer.py
from rest_framework import serializers
class BookingDemandSerializer(serializers.ModelSerializer):
class Meta:
model = BookingDemand
fields = '__all__'
class BookingSerializer(serializers.ModelSerializer):
booking_demands = BookingDemandSerializer(source='bookingdemand_set', many=True)
class Meta:
model = Booking
fields = '__all__'
# serialization process
queryset = Booking.objects.all() # apply filter if you want
serializer = BookingSerializer(queryset, many=True)
serializer.data # here is the data
Create two serializers BookingSerializer and BookingDemandSerializer and add
booking(related_name) = BookingDemandSerializer(many=True) in BookingSerializer

Django rest framework serializing many to many field

How do I serialize a many-to-many field into list of something, and return them through rest framework? In my example below, I try to return the post together with a list of tags associated with it.
models.py
class post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag"??)
views.py
class PostViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
You will need a TagSerializer, whose class Meta has model = Tag. After TagSerializer is created, modify the PostSerializer with many=True for a ManyToManyField relation:
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True)
class Meta:
model = Post
fields = ('tag', 'text',)
Answer is for DRF 3
This is what I did, let´s suppose a Book can have more than one author and an Author can have more than one book:
On Model:
class Author(models.Model):
name = models.CharField(max_length=100, default="")
last_name = models.IntegerField(default=0)
class Book(models.Model):
authors = models.ManyToManyField(Author, related_name="book_list", blank=True)
name = models.CharField(max_length=100, default="")
published = models.BooleanField(default=True)
On Serializers:
class BookSerializer(serializers.ModelSerializer):
authors = serializers.PrimaryKeyRelatedField(queryset=Author.objects.all(), many=True)
class Meta:
model = Book
fields = ('id', 'name', 'published', 'authors')
class AuthorSerializer(serializers.ModelSerializer):
book_list = BookSerializer(many=True, read_only=True)
class Meta:
model = Author
fields = ('id', 'name', 'last_name', 'book_list')
Adding to #Brian's answer
"tags": [{"name": "tag1"}] can be simplified to "tags": ["tag1", "tag2",...] in this way:
class TagListingField(serializers.RelatedField):
def to_representation(self, value):
return value.name
class PostSerializer(serializers.ModelSerializer):
tag = TagListingField(many=True, read_only=True)
class Meta:
...
More info here: https://www.django-rest-framework.org/api-guide/relations/#custom-relational-fields
The default ModelSerializer uses primary keys for relationships. However, you can easily generate nested representations using the Meta depth attribute:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ("text", "tag")
depth = 1
As mentioned in the documentation :
The depth option should be set to an integer value that indicates the depth of relationships that should be traversed before reverting to a flat representation.
This works for me.
tag = TagSerializer(source="tag", read_only=True, many=True)
Django 2.0
For many to many field, if you want specific one:
class QuestionSerializer(serializers.ModelSerializer):
topics_list = serializers.SerializerMethodField()
def get_topics_list(self, instance):
names = []
a = instance.topics.get_queryset()
for i in a:
names.append(i.desc)
return names
class Meta:
model = Question
fields = ('topics_list',)
In the serializer on init method you can pass the queryset to the field and rest_framework valide the ids on that queryset
1) first extend your serializer from serializers.ModelSerializer
class YourSerializer(serializers.ModelSerializer):
2) include the field on the meta class
class YourSerializer(serializers.ModelSerializer):
class Meta:
fields = (..., 'your_field',)
3) in the init method:
def __init__(self, *args, **kwargs):
super(YourSerializer, self).__init__(*args, **kwargs)
self.fields['your_field].queryset = <the queryset of your field>
You can limit the queryset for that field under any argument using filter or exclude like normally you do. In case that you want include all just use .objects.all()
models.py
class Tag(models.Model):
name = models.CharField(max_length=100)
# ===============
# ... rest of the fields ...
class Post(models.Model):
tag = models.ManyToManyField(Tag)
text = models.CharField(max_length=100)
serialiazers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ("text", "tag")
views.py
## FUNCTION BASED VIEW
def fbvPost_ListView(request):
# list
if request.method == "GET":
posts = Post.objects.all()
serializer = PostSerializer(instance=posts, many=True)
return JsonResponse(serializer.data, safe=False)
return JsonResponse({"success": False})
# ===========================================================
## CLASS BASED VIEW
class cbvPost_ListView(viewsets.ReadOnlyModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
NB: Tag, Post are two models & we need to serialize them. Here, Post model have a dependency of Tag models, so here we explicitly mention it, [tags = TagSerializer(many=True, read_only=True)] or its return it's primary field value.
DETAILS HERE
Hi I will be showing many to many for update and create. The context is the event can have many dances and dances can have many event.
The request will be as followed.
{
"competition": 2,
"title": "the title",
"dances":[ {"id":1},{"id":2}],
"description": "the desc"
}
The Create Function will be as followed.
def create(self, validated_data):
try:
dance_ids = []
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
new_event = models.Event.objects.create(**validated_data)
if dance_ids:
for dance_id in dance_ids:
new_event.dances.add(dance_id)
new_event.save()
return new_event
except Exception as e:
raise serializers.ValidationError({'detail': e})
The Update Function will be as followed.
def update(self, instance, validated_data):
# Delete all records of genres.
try:
for current_genre in instance.dances.all():
instance.dances.remove(current_genre)
# Repopulate genres into instance.
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_obj = models.Dance.objects.get(pk=dance['id'])
instance.dances.add(dance_obj)
event_updated = super().update(instance, validated_data)
return event_updated
except Exception as e:
raise serializers.ValidationError({'detail': e})
If you want to just do "dances":[1,2] instead, just make some amendments to the
for dance in self.initial_data['dances']:
if 'id' not in dance:
raise serializers.ValidationError({'detail': 'key error'})
dance_ids.append(dance['id'])
part. I hope this will be able to help yall out! :)
First, Tag needs its own serializer too
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
Then in your PostSerializer, add one line
class PostSerializer(serializers.ModelSerializer):
tag = TagSerializer(read_only=True, many=True).data
class Meta:
model = Post
fields = ("text", "tag")
This will make it so your Tag field in Post is an array of tag ids. if you don't put the ".data" part, it will put all of the attributes of tag, which is probably too much in most cases
You can use serializers.SlugRelatedField() or serializers.StringRelatedField(many=True) Serializer relations
In your case :
class PostSerializer(serializers.ModelSerializer):
tag = serializers.StringRelatedField(many=True) # this will return a list
class Meta:
model = Post
fields = ('tag', 'text',)

Categories