M2M field doesn't get updated - django

I was trying to update an M2M field but it just doesn't get updated neither from admin panel nor the serializer!
We have a "Book" model which has a "Category" field that is M2M to BookCategory Model
this is my model:
class Book(models.Model):
#...
category = models.ManyToManyField('BookCategory', related_name='bookCategory')
def __str__(self):
return self.name
class BookCategory(models.Model):
name = models.CharField(max_length=100)
parent = models.ForeignKey('BookCategory', on_delete=models.PROTECT, null=True, blank=True)
description = models.TextField()
def __str__(self):
return self.name
and this is my view:
class BookChange(RetrieveUpdateDestroyAPIView):
serializer_class = BookSerializer
lookup_field = 'pk'
def get_queryset(self):
return Book.objects.all()
and the Model Serializer:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
The funny thing is that when I remove the current category it works but it doesn't add the new ones
I also tried Overriding the serializer.save() in def update
Can you help me out?

Related

Django Modelviewset Filtering

I have two models Category & Post. In Post model there is foreign key of category. Based on category I want to filter the data to show the post category wise. Here's my code.
models.py
class Category(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField()
parent = models.ForeignKey('self',blank=True, null=True ,related_name='news', on_delete=models.CASCADE)
class Meta:
unique_together = ('slug', 'parent',)
verbose_name_plural = "Category"
def __str__(self):
full_path = [self.name]
k = self.parent
while k is not None:
full_path.append(k.name)
k = k.parent
return ' -> '.join(full_path[::-1])
class Post(models.Model):
NEWS_TYPE = (('Images','Images'),('Multi-Images','Multi-Images'),('Image-Text','Image-Text'),
('Audio-Video','Audio-Video'),('Audio-Video-Text','Audio-Video-Text'),('Audio','Audio'),
('Audio-Text','Audio-Text'))
POST_STATUS = (('Pending','Pending'),('Verified','Verified'),('Un-Verified','Un-Verified'),
('Published','Published'),('Mint','Mint'))
category = models.ForeignKey(Category, related_name='posts', on_delete=models.CASCADE)
post_type = models.CharField(max_length=100, verbose_name='Post Type', choices=NEWS_TYPE)
title = models.TextField(verbose_name='News Title')
content = models.TextField(verbose_name='News Content')
hash_tags = models.CharField(max_length=255, verbose_name='Hash Tags')
source = models.CharField(max_length=255, verbose_name='News Source')
author = models.ForeignKey(User, related_name='Post', on_delete=models.CASCADE)
views = models.ManyToManyField(User,related_name='Views', blank=True)
likes = models.ManyToManyField(User, related_name='Likes', blank=True)
dislikes = models.ManyToManyField(User, related_name='Dislikes', blank=True)
status = models.CharField(max_length=20, verbose_name='Status', choices=POST_STATUS, default='Pending')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return (self.post_type)+ '-' +self.title
serializers.py
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
class PostSerializer(serializers.ModelSerializer):
category = CategorySerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
views.py
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
serializer_class = PostSerializer
def get_queryset(self):
news_post = Post.objects.all()
return news_post
def retrieve(self, request, *args, **kwargs):
params = kwargs
print(params['pk'])
category = Category.objects.filter(name=params['pk'])
serializer = CategorySerializer(category, many=True)
return Response(serializer.data)
urls.py
from django.urls import path, include
from rest_framework import routers
from rest_framework.routers import DefaultRouter
from news.views import PostAPI, CategoryAPI
from . import views
router = DefaultRouter()
router.register('posts', views.PostAPI, basename='posts'),
router.register('category', views.CategoryAPI, basename='category'),
urlpatterns = router.urls
I tried solving in these way but it tells 'PostSerializer' object has no attribute 'get_category'. Is there anything i'm doing wrong. Please your support would be helpful. Thank you
I think then your approach should be the other way round, meaning you should add the list of Posts to your Category:
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('category','post_type','title','content','hash_tags','source','author','views',
'likes','dislikes','status')
class CategorySerializer(serializers.ModelSerializer):
posts = PostSerializer(many=True, read_only=True)
class Meta:
model = Category
fields = ['name', 'slug', 'parent', 'posts']
Attention: I changed the related name of your category field in the Post model to 'posts'
This should show you all Posts when retrieving a category. No need to override any method in your views:
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
class PostAPI(viewsets.ModelViewSet):
queryset = Post.obejcts.all()
serializer_class = PostSerializer
If do not want identify the category by id but by category name, e.g.:
http://127.0.0.1:8000/news/category/sports/
add a custom lookup field to your category view, e.g.
class CategoryAPI(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
lookup_field = 'name'
but make sure the lookup_field is unique

Django Rest Framework: how to create and update many to many field?

I'm finding a way to serialize model fields of a Many To Many relation object.
Models
class Tag(models.Model):
name = models.CharField(max_length=150)
description = models.TextField(blank=True)
class Book(models.Model):
name = models.CharField(max_length=150)
slug = models.SlugField(max_length=255, unique=True)
tag= models.ManyToManyField(Tag,
related_name='books',
blank=True)
Serializers -
class BookSerializer(serializers.ModelSerializer):
author = serializers.StringRelatedField(read_only=True)
slug = serializers.SlugField(read_only=True)
class Meta:
model = Book
fields = '__all__'
Views -
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
lookup_field = 'slug'
serializer_class = BookSerializer
def perform_create(self, serializer):
return serializer.save(owner=self.request.user)
I want to add the tag name to the response of the detailed view as well as the list view of books. If I try to create a serializer for the Tag model and use it in the book serializer, I'm not able to create or update the Book.
What should I do?
Also, is there a way to update only the tag field?

Serializer Error When Using Rest Framework to Serialize Nested Objects from Model

I have seen some related posts, but I am not sure what I need to do.
I have set up a view to serialize my test model which has nested models. I have set up the serializers, but I get the error "Got AttributeError when attempting to get a value for field Question on serializer TestSerializer.\nThe serializer field might be named incorrectly".
My Serializers:
class AnswerSerializer(serializers.ModelSerializer):
class Meta:
model = Answer
fields = ('id', 'number', 'text', 'iscorrect')
class QuestionSerializer(serializers.ModelSerializer):
answer = AnswerSerializer()
class Meta:
model = Question
fields = ('id', 'number', 'text', 'answer')
related_object = 'answer'
class TestSerializer(serializers.ModelSerializer):
question = QuestionSerializer()
class Meta:
model = Test
fields = ('id', 'name', 'question')
related_object = 'question'
My Models:
class Test(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=255,default='',blank=False)
datecreated = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
class Question(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
def __str__(self):
return self.text
class Answer(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
iscorrect = models.BooleanField(default=False)
def __str__(self):
return self.text
The call from the view:
serializer = TestSerializer(test, many=True)
You have set the related_name in the foreign key other wise default related name is {model_name}_set.
class Question(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE, related_name='questions')
text = models.CharField(max_length=255,default='',blank=False)
number = models.IntegerField()
def __str__(self):
return self.text
in serializer you can access that fields
class TestSerializer(serializers.ModelSerializer):
questions = QuestionSerializer(many=True)
class Meta:
model = Test
fields = ('id', 'name', 'question')
related_object = 'question'

StringRelatedField not working as expected

I have models,
class Reporter(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Article(models.Model):
title = models.CharField(max_length=100)
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
def __str__(self):
return self.title
and serializer,
class ReporterSerializer(serializers.ModelSerializer):
article = serializers.StringRelatedField(source='article_set')
class Meta:
model = Reporter
fields = '__all__'
and views
class ReporterAPI(viewsets.ModelViewSet):
queryset = Reporter.objects.all()
serializer_class = ReporterSerializer
Everything seems fine, but, my response showing something weird
response
Here is the RESPONSE IMAGE
The response article is showing wrong output
Since article_set is list of objects you should add many=True argument:
class ReporterSerializer(serializers.ModelSerializer):
article = serializers.StringRelatedField(source='article_set', many=True)
class Meta:
model = Reporter
fields = '__all__'

Django Rest Framework - How to POST foreign keys in ListCreateAPIView

Post model
class Post(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.CASCADE) # Profile is another model
title = models.CharField(max_length=300)
content = models.CharField(max_length=1000)
votes = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
subreddit = models.ForeignKey(Subreddit, on_delete=models.CASCADE) # Subreddit is another model
PostSerializer
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
depth = 1
ListPostsOfReddit
class ListPostsOfReddit(ListCreateAPIView):
serializer_class = PostSerializer
def get_queryset(self):
return Post.objects.filter(subreddit__name=self.kwargs['r_name'])
In the ListCreateAPIView of rest-framework, I am able to GET all the foreign key data. In the form that rest-framework provides, only the
Title
Content
Votes
are asked, I want the foreign key fields to be also asked as input. How do I achieve that?
Use Two serializers, and manage those in get_serializer() method.
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
depth = 1
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class ListPostsOfReddit(ListCreateAPIView):
def get_serializer_class(self):
if self.request.method == 'GET':
return PostListSerializer
return PostSerializer
def get_queryset(self):
return Post.objects.filter(subreddit__name=self.kwargs['r_name'])
Note: I didn't tested/verified the solution. Please let me know if any error occured