So I've created an API with the Django rest framework. I have a simple model relationship which is, User -> Posts. Users are linked to the posts because the user is the AUTHOR of a post. All I want to do is display the username associated with a POST in my rest API. How do I reference other fields from a model that I linked as a foreign key? IF I have a user model that has a username, profile picture and email, how can I display those in my Post model???
Here is an example of what I am trying to do with the models
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=50)
content = models.TextField()
image = models.ImageField(default='default.mp4', upload_to='video_thumbnails')
videoFile = models.FileField(default='default.mp4', upload_to='videos')
date_posted = models.DateTimeField(default=timezone.now)
user = models.ForeignKey(User , #Something to get the username here, on_delete=models.CASCADE)
Right now this displays
{
"title": "HOLROYD SWIPE ACCESS(CS ROOMS)",
"content": "Yeet",
"image": "http://127.0.0.1:8000/media/video_thumbnails/Screenshot_from_2019-08-03_23-37-50.png",
"videoFile": "http://127.0.0.1:8000/media/videos/Screenshot_from_2019-08-03_23-37-52.png",
"date_posted": "2019-10-22T21:01:07Z",
"user": 1
}
All I want it to do is display the name of the user instead of the USER ID which is 1 in this case.
I JUST want it to look like this instead
{
"title": "HOLROYD SWIPE ACCESS(CS ROOMS)",
"content": "Yeet",
"image": "http://127.0.0.1:8000/media/video_thumbnails/Screenshot_from_2019-08-03_23-37-50.png",
"videoFile": "http://127.0.0.1:8000/media/videos/Screenshot_from_2019-08-03_23-37-52.png",
"date_posted": "2019-10-22T21:01:07Z",
"user": "usernameassociatedwithpost"
}
Here is my serializer
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('title', 'content', 'image', 'videoFile', 'date_posted', 'user' )
and here is my views.py
class VideoList(generics.ListCreateAPIView):
queryset = Video.objects.all()
serializer_class = VideoSerializer
class VideoDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Video.objects.all()
serializer_class = VideoSerializer
If anymore code is needed to answer this, please let me know. Thanks!
You can use SlugRelatedField:
class PostSerializer(serializers.ModelSerializer):
user = serializers.SlugRelatedField(slug_field="username", queryset=User.objects.all())
class Meta:
model = Post
fields = ('title', 'content', 'image', 'videoFile', 'date_posted', 'user' )
Related
In my case,I use JWT authentication, and when I create new "Post"(my model), I want automatically set author to user that request it.But when I do it, I got an error
{
"author": [
"This field is required."
]
}
I know,I'm not passing user, but I want to set it automatically, and I dont know how.
I just want to know how to avoid error, because when I pass value, that allows me to go ahead, the user is set automatically from context.
Serializer
class PostSerializer(FlexFieldsModelSerializer):
class Meta:
model = Post
fields = ('title','content',author','category','likedBy')
expandable_fields = {
'category': ('blogApi.CategorySerializer', {'many': True}),
'comments': ('blogApi.CommentSerializer', {'many': True}),
'likedBy': ('blogApi.LikedBySerializer', {'many': True}),
}
def create(self, validated_data):
user = self.context['request'].user
post = Post.objects.create(
author=user, title=validated_data['title'], content=validated_data['content'])
post.category.set(validated_data['category'])
return post
My create view
class PostCreate(generics.CreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthenticated]
Model
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
category = models.ManyToManyField(Category, related_name='posts')
author = models.ForeignKey(User, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
likedBy = models.ManyToManyField(User, related_name='posts', blank=True)
class Meta:
ordering = ['-created']
def __str__(self):
return self.title
and when I create
You can make author a read-only field, or if you're just using this serializer to create users and not retreive them. You can just remove 'author' from fields in the serializer meta.
Read-only field
from rest_framework import serializers
class PostSerializer(FlexFieldsModelSerializer):
author = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Post
fields = ('title','content','author','category','likedBy')
I am using django rest framework to create an api endpoint. I am using the default user model django offers. I need to create a post which uses the user as a foreign key. A user called "author" in the post can have multiple posts.
This is an example of a post json.
[
{
"author": {
"id": 1,
"username": "sorin"
},
"title": "First Post",
"description": "Hello World!",
"created_at": "2020-08-05T14:20:51.981163Z",
"updated_at": "2020-08-05T14:20:51.981163Z"
}
]
This is the model.
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
This is the serializer.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username')
class PostSerializer(serializers.HyperlinkedModelSerializer):
author = UserSerializer()
class Meta:
model = Post
fields = ('author', 'title', 'description', 'created_at', 'updated_at')
I am getting the error "The .create() method does not support writable nested fields by default." when trying to make a post request using a "username", "title" and "description".
Any help to how to solve this?
I like hooking in the create function of the serializer for these kind of use cases.
Make sure your UserSerializer is set to read_only=True.
class PostSerializer(serializers.HyperlinkedModelSerializer):
author = UserSerializer(read_only=True)
class Meta:
model = Post
fields = ('author', 'title', 'description', 'created_at', 'updated_at')
def create(self, validated_data):
request = self.context['request']
author_data = request.data.get('author')
if author is None or not isinstance(author.get('id'), int):
raise ValidationError({'author': ['This field is invalid.']})
author_instance = get_object_or_404(User, id=author.get('id'))
return Post.objects.create(author=author_instance, **validated_data)
simple-jwt currently issues token using superuser but, i wanna use my custom User Model. (i defined custom User model as below.)
class User(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10, unique=True, blank=False)
password = models.CharField(max_length=128)
def __repr__(self):
return self.__class__
class UsersSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ("name", "password")
my question is that could i receive token using custom user model at simple-jwt?
if simple-jwt uses custom User model, please tell me how to use custom User model.
This works for me try
Assuming you installed djangorestframework_simplejwt correctly
Just paste this at the end of your
settings.py
REST_AUTH_SERIALIZERS = {
'USER_DETAILS_SERIALIZER': 'users.serializers.UserSerializer',
}
here UserSerializer uses CustomUserModel i.e.
serializers.py
class UserSerializer(serializer.ModelSerializer):
class Meta:
model = get_user_model()
fields = []
read_only_fields = ( ,)
And add the serializer which overrides the default serializer validate method of the TokenObtainPairView
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
def validate(self, attrs):
# The default result (access/refresh tokens)
data = super().validate(attrs)
refresh = self.get_token(self.user)
# assign token
data['refresh'] = str(refresh)
data['access'] = str(refresh.access_token)
# extra fields
data['age'] = self.user.age
return data
With the help of this I get the "age" model field of the CustomUser i made,on calling the login end points.
User this serializer in your views class and then call the url of this views
After callilng this url
Returned data as follow:
{
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..............",
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...............",
"username": "Shubham",
"id": 4,
"age": 100,
"description": "Slow shubham gadwal mehra try to understand."
}
I'm trying to use the Django Rest-Framework to produce some JSON that shows all the user's posts, but also shows the images for that post. Image is a foreign key to Post. Here are the models:
models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
status = models.CharField(max_length=200)
class Image(models.Model):
post = models.ForeignKey(Post, on_delete=models.CASCADE)
img = models.CharField(max_length=120)
views_count = models.IntegerField(default=0)
views.py
class GetPosts(ListAPIView):
serializer_class = PostSerializer
def get_queryset(self):
requested_user = get_requested_user(self)
return Post.objects.filter(user=requested_user).order_by('-created_at')
def get_requested_user(self):
filter_kwargs = {'username': self.kwargs['username']}
return get_object_or_404(User.objects.all(), **filter_kwargs)
serializers.py
class PostSerializer(serializers.ModelSerializer):
image_img = serializers.RelatedField(source='Image', read_only=True)
class Meta:
model = Post
fields = ('status', 'image_img ')
In the serializers.py, I'd like to show all of the fields for Image (img, views_count) What I get with my current code is this:
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"status": "I am number 1"
}
]
}
Which contains the user's posts, but not the user's posts and each post's images. Note: Query url looks like this: /api/posts/user/
You should use Nested serializer here,
class ImageSerializer(serializers.ModelSerializer):
class Meta:
model = Image
fields = ('img',)
class PostSerializer(serializers.ModelSerializer):
image_img = ImageSerializer(source='image_set', many=True)
class Meta:
model = Post
fields = '__all__'
Hence the response will be like,
{
"count": 1,
"next": null,
"previous": null,
"results": [
{
"status": "I am number 1",
"image_img": [
{"img": "image_url"},
{"img": "image_url"},
....
]
}
]
}
How to display all field of model class in serializer?
From the doc,
You can also set the fields attribute to the special value '__all__' to indicate that all fields in the model should be used.
Reference
1. DRF- Nested Realtions
2. source argument
3. Specifying which fields to include
In my app which use DRF, I want to use model serializer with multiple related objects.
models.py:
class JobType(models.Model):
name = models.CharField(null=False, max_length=250)
class Offer(models.Model):
user = models.ForeignKey(User, null=False)
job_type = models.ForeignKey(JobType, null=False)
salary = models.DecimalField(null=False, max_digits=8,
decimal_places=2)
serializers.py:
class JobTypeSerializer(serializers.ModelSerializer):
class Meta:
model = JobType
fields = ('id', 'name')
class OfferSerializer(serializers.ModelSerializer):
job_type = JobTypeSerializer()
class Meta:
model = Offer
fields = (
'salary', 'job_type', 'user'
)
views.py:
class SalaryViewSet(viewsets.ModelViewSet):
queryset = Salary.objects.all()
serializer_class = SalaryFullSerializer
What I want to achieve:
when I do GET request on my api/offers I want to have something like:
[
{
"salary": 1000,
"user: 1,
"job_type": {
"id": 1,
"name": "Developer",
}
}
]
so, basically, when GET offers is made, I want to have nested related object with all it's properties.
On other hand, when POST offers is made, I want to limit JobType choices.
When I've removed job_type = JobTypeSerializer() from OfferSerializer I had nice dropdown with available choices (in DRF debug). But it caused that GET on offers returned only JobOffer's ID in results.
How can I achieve desired behavior?
You can use different serializer for POST and GET requests.
Override get_serializer_class
def get_serializer_class(self):
if self.request.method == 'POST':
return SalaryPostSerializer
return SalaryFullSerializer