Getting all of foreign key's fields - python

I have 3 models. One model is the User model, another is a Post model, and the other is a notifications model. I want to use Django's REST framework to show all of the notifications for the signed in user, plus each user that interacted with the signed in user.
class Notification(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='actors')
actor = models.ForeignKey(User, on_delete=models.CASCADE, related_name='users')
post = models.ForeignKey(Post, on_delete=models.CASCADE, blank=True, null=True)
type = models.ForeignKey(NotifType, on_delete=models.CASCADE)
is_read = models.BooleanField()
created_at = models.DateTimeField(auto_now_add=True)
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.CharField(max_length=200)
share_count = models.IntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
url = models.CharField(max_length=150)
serializers.py
class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
views.py
class GetNotifications(ListAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
user = get_object_or_404(User, id=self.request.user.id)
return User.objects.select_related().filter(users__user=user).order_by("-users__created_at")
What I have currently returns all users who have interacted with the signed in user. However, it doesn't return the details about the notification. I want all details about each notification, including the details about the post. So the output should be something like this:
{
"post": {
"id": 1,
"status": "Hello!",
.
.
.
},
"user": {
"username": "Ben",
"avatar": "example.png"
.
.
},
is_read: 1,
created_at: 2017-07-21,
type: 2
}

change your view as:
class GetNotifications(ListAPIView):
serializer_class = NotificationSerializer
def get_queryset(self):
## user = get_object_or_404(User, id=self.request.user.id)
## i believe the above line is redundant, you can use self.request.user to access the loged-in user
return Notification.objects.select_related().filter(user=self.request.user).order_by("created_at")
then change your notification serializer as:
class NotificationSerializer(serializers.ModelSerializer):
class Meta:
model = Notification
exclude = ('user',)
depth = 1
or you can define new serializer for actor and post and use it like this:
class NotificationSerializer(serializers.ModelSerializer):
actor = ActorSerializer()
post = PostSerializer()
class Meta:
model = Notification
exclude = ('user',)

You define the serializer for user and posts. Your serializers.py should be
class NotificationSerializer(serializers.ModelSerializer):
post = PostSerializer()
user = UserSerializer()
class Meta:
model = Notification
fields = '__all__'

try this
class NotificationSerial(serializers.ModelSerializer):
class Meta:
model = Notification
fields = '__all__'
class PostSerial(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class NotificationSerializer(serializers.ModelSerializer):
post = PostSerial(many=True, source='post_set')
user = NotificationSerial(many=True, source='actors')
class Meta:
model = User
fields = '__all__'

Related

Django REST Framework, Serializers: Additional data?

Good day,
I would like to ask, if there's a possibility to gain additional data inside my serializers?
These are my models...
models.py
class Chair(models.Model):
name = models.CharField(max_length=100, null=False, blank=False, unique=True)
bookable = models.BooleanField(default=False)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
class Booking(models.Model):
chair = models.ForeignKey(Chair, on_delete=models.CASCADE)
day = models.DateField()
user_name = models.CharField(max_length=100)
user_created = models.CharField(max_length=100)
date_created = models.DateField(auto_now_add=True)
and these my serializers...
serializers.py
class BookingSerializer(serializers.ModelSerializer):
class Meta:
model = Booking
fields = '__all__'
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = '__all__'
When making a request inside js like this...
views.py
#api_view(['GET'])
def bookings_by_date(request, pk):
bookings = Booking.objects.filter(day=pk)
serializer = BookingSerializer(bookings, many=True)
return Response(serializer.data)
script.js
let url = '...here's my url for Booking...';
fetch(url)
.then((resp) => resp.json())
.then(function(data) {
// do something here
});
...I would like to get not only the id of the Chair (models.Foreignkey), but also it's name. My first thought was doing something like this...
class ChairSerializer(serializers.ModelSerializer):
class Meta:
model = Chair
fields = [
...
'chair',
'chair__name',
...
]
...but this doesn't seem to work! Does anyone know a solution for my problem? Thanks for all your help and have a great weekend!
You can use one of this two ways:
1-) Using SerializerMethodField. You can add readonly fields with this way. You should add get_<field_name> method or give a method name that you want to run for this field with name keyword. You can look the document for more details.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.SerializerMethodField()
class Meta:
model = Booking
fields = '__all__'
def get_chair_name(self, obj):
return obj.chair.name
2-) Using CharField with source attribute:
You can define basically this field fill from where.
class BookingSerializer(serializers.ModelSerializer):
chair__name = serializers.CharField(source='chair__name')
class Meta:
model = Booking
fields = '__all__'

Django REST RetrieveAPIView combining querysets

I'm trying to build a queryset which combines two query results, namely from Category and Course. Every Course has a Category foreign key. Is there a way to add the respective Courses to each Category?
Example:
{
"id": 61,
"name": "fgfdf",
"courses":
{
"id": 1,
"category": 61,
"title": "mytitle"
"active": true
},
{
...
}
}
Url
path('dict/<pk>/', DictView.as_view(), name='detail')
Models
class Category(models.Model):
name = models.CharField(max_length=255, blank=False, null=False)
class Course(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
title = models.CharField(max_length=255, blank=False, null=False)
active = models.BooleanField(default=True)
View
This is what I imagined but it's obviously incorrect, I've done some research but I couldn't find what I needed.
class DictView(RetrieveAPIView):
queryset = Category.objects.all()
serializer_class = CategorySerializer
def get_queryset(self):
queryset = Category.objects.all()
courses = list(Course.objects.filter(category=pk))
queryset['courses'] = courses;
return queryset
One way is defining serializers like this:
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
fields = "__all__"
class CategorySerializer(serializers.ModelSerializer):
courses = CourseSerializer(source='course_set', many=True)
class Meta:
model = Category
fields = "__all__"
Then, you don't need to override get_queryset anymore.
If you wish to apply filters for courses, say you only want active courses, you can do the following:
class CategorySerializer(serializers.ModelSerializer):
courses = serializers.SerializerMethodField()
def get_courses(self, obj):
active_courses = obj.course_set.filter(active=True)
return CourseSerializer(active_courset, many=True).data
class Meta:
model = Category
fields = "__all__"

check if object in ManyToMany field django rest framework

here is my models.py:
class Post(models.Model):
id = models.AutoField(primary_key=True)
body = models.TextField(max_length=10000)
date = models.DateTimeField(auto_now_add=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
liked_by = models.ManyToManyField(User, blank=True, related_name='liked_by')
class Meta:
ordering = ['-date']
serializers.py:
class PostSerializer(serializers.ModelSerializer):
user = UserSerializers()
total_likes = serializers.SerializerMethodField()
total_comments = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('id','body','date','user','total_likes','total_comments')
def get_total_likes(self, instance):
return instance.liked_by.count()
def get_total_comments(self, instance):
return instance.comment_set.count()
Q1: how do i check if a user exists in ManyToManyField of a single post?
Q2: shouldn't i use ManyToManyField in drf? then which would be better?
I don't have enough reps to comment, but if you have a post instance and a user instance, you could do something like:
post.liked_by.filter(id=user.id).exists()
Does that help you or are you asking where you should be implementing this? e.g. in your view or serializer etc...

how to merge models in django rest framework in one serializer class

i am trying to add CommentSerializer class to write comments with PostListSerializer , i want to display comment field bottom at every exist post
django:2.2
models.py
class Comment(models.Model):
id = models.UUIDField(default=uuid.uuid4,primary_key=True,editable=False)
author = models.ForeignKey(Account,on_delete=models.CASCADE,related_name='comments')
post= models.ForeignKey(Post,on_delete=models.CASCADE,related_name='comments')
comment_content = models.TextField(max_length=400)
commented_at = models.DateTimeField(auto_now_add=True)
edited_at = models.DateTimeField(auto_now=True)
image = models.ImageField(upload_to=random_url,blank=True,null=True)
parent = models.ForeignKey('self',null=True,blank=True,on_delete=models.CASCADE,related_name='replies')
active = models.BooleanField(default=True)
this is the serializers.py
class CommentCreateSerialzier(ModelSerializer):
class Meta:
model = Comment
fields = [
'comment_content','image'
]
class TripListSerializer(TaggitSerializer, GeoFeatureModelSerializerGIS):
tags = NewTagListSerializerField()
comments = SerializerMethodField()
detail_url = post_detail_url
user= SerializerMethodField()
class Meta:
model = Post
geo_field = 'location'
fields = [
'detail_url','user','name','city','tags','comments'
]
def get_user(self,obj):
return str(obj.user.username)
def get_comments(self,obj):
comment = Comment.objects.filter(post=obj,active=True)
comments = CommentSerialzier(comment,many=True).data
return comments
and this is my views.py
class CommentCreateSerializer(CreateAPIView):
serializer_class = CommentCreateSerialzier
message = 'you dont have permission to comment'
permission_classes = [IsAuthenticated]
def perform_create(self):
serializer.save(author=self.request.username)
how to add default post object in every single post ? and does it the same for replies as well
thanks for replying

How to serialize a one to many relation in django-rest using Model serializer?

These are my models and serializers. I want a representation of Question Model along with a list of people the question was asked to.
I am trying this:
#api_view(['GET', 'PATCH'])
def questions_by_id(request,user,pk):
question = Question.objects.get(pk=pk)
if request.method == 'GET':
serializer = QuestionSerializer(question)
return Response(serializer.data)
But I get an empty dictionary ({}). However when I remove the asked field from QuestionSerializer I get a complete representation of Question along with Places serialized nicely. What am I missing ?
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
class QuestionSerializer(serializers.ModelSerializer):
class Meta:
model = Question
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
class Question(BaseModel):
title = models.CharField(max_length=200, null=False)
places = models.ManyToManyField(Place, blank=True)
answered = models.BooleanField(default=False)
class AskedTo(BaseModel):
ques = models.ForeignKey(Question, on_delete=models.CASCADE)
to_user = models.ForeignKey(settings.AUTH_USER_MODEL)
replied = models.BooleanField(default=False)
class Place(models.Model):
g_place_id = models.CharField(max_length=20,primary_key=True)
json = models.TextField(null=True)
name = models.CharField(max_length=40)
I figured it out. There were two errors.
Changed this:
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'answered')
to this (notice the change in fields, fields on model and serializer didn't match)
class AskedToSerializer(serializers.ModelSerializer):
class Meta:
model = AskedTo
fields = ('to_user', 'replied')
Secondly, I needed to define any extra fields outside class Meta
class QuestionSerializer(serializers.ModelSerializer):
places = PlaceSerializer(many=True, required=False)
asked = AskedToSerializer(source='askedto_set', many=True)
class Meta:
model = Question
fields = ('id', 'created_on', 'title', 'places', 'answered','asked')
extra_kwargs = {'created_by': {'read_only': True}}
Notice the change in definition of places and asked.
In my case, I have this models.py:
class Section(models.Model):
title = models.CharField(max_length=500)
description = models.TextField(blank=True)
user = models.ForeignKey(Profile, related_name="sections", on_delete=models.CASCADE)
class Feed(models.Model):
title = models.CharField(max_length=500)
link_rss = models.URLField(max_length=500)
link_web = models.URLField(max_length=500)
description = models.TextField(blank=True)
language = models.CharField(max_length=50, blank=True)
logo = models.URLField(blank=True)
sections = models.ManyToManyField(Section, related_name="feeds")
And I completed the serializers.py this:
class FeedSerializer(serializers.ModelSerializer):
sections = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Feed
fields = '__all__'
class SectionSerializer(serializers.ModelSerializer):
feeds = FeedSerializer(many=True, read_only=True)
class Meta:
model = Section
exclude = ('user',)

Categories