Update foreign key object's fields in drf - django

I've two models:
class User(models.Model):
name = models.CharField(max_length=255)
email = models.EmailFeild()
password = models.CharField(max_length=255)
class Profile(models.Model):
user = models.ForeignKeyField(User, on_delete=models.CASCADE)
image = models.ForeignKeyField(to='media.images', on_delete=models.CASCADE)
mobile = models.IntegerField()
address = models.CharField(max_length=255)
Now I'm creating a patch API for updating profile. Now, a user can also update email and his name.
class ProfileUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = '__all__'
def update(self, instance, validated_data):
.........
How would I accept the User model's detail (email and name) in the profile payload and update both the models in my serializer?

At it simplest form, you can add two custom fields in the serializer:
class ProfileUpdateSerializer(serializers.ModelSerializer):
name = serializers.CharField()
email = serializers.EmailField()
And rewrite the serializer like this:
def update(self, instance, validated_data):
name = validated_data.pop('name', '')
email = validated_data.pop('email', '')
instance = super().update(instance, validated_data)
instance.user.email = email
instance.user.name = name
instance.user.save()
return instance

Related

django rest framework nested serializer crud operations . what required is to create a api which should be called only once to perform crud operations

models.py
class User(models.Model):
ROLE_CHOICES = (
("admin","ADMIN"),
("staff","STAFF")
)
username = models.CharField(max_length=256)
password = models.CharField(max_length=256)
firstname = models.CharField(max_length=256)
lastname = models.CharField(max_length=256)
email = models.CharField(max_length=256)
role = models.CharField(max_length=256,choices=ROLE_CHOICES)
class Profile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,primary_key=True,)
phone_number = models.IntegerField()
company_name = models.CharField(max_length=256)
serializer.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
class ProfileSerializer(serializers.ModelSerializer):
user = UserSerializer(many=True)
class Meta:
model = Profile
fields = '__all__'
def create(self, validated_data):
users_data = validated_data.pop('user')
user = User.objects.create(**user_data)
profile = Profile.objects.create(user=user,**validated_data)
# for user_data in users_data:
# User.objects.create(**user_data)
return profile
I want to use nested serializer here to do crud operations.I have written a code for post method i am calling the profile serializer in my views.

How to add a foreign key to the username. Django REST framework

During serialization, i noticed that the post_author Foreign Key of the Post model is referencing the id of the creator and thus, i can't display the username of the creator in the REST API, only the post_author id.
How can i add the username of the post_creator, so that it is readable to other users, when i fetch the data on the frontend?
models.py // CustomUser = the Creator of the post.
class CustomUser(AbstractUser):
fav_color = models.CharField(blank=True, max_length=120)
class Post(models.Model):
post_author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='posts')
post_title = models.CharField(max_length=200)
post_body = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def __str__(self):
return self.post_title
views.py
#api_view(['GET'])
def post_list(request):
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data)
serializers.py user model and post model serialization
class CustomUserSerializer(serializers.ModelSerializer):
"""
Currently unused in preference of the below.
"""
email = serializers.EmailField(required=True)
username = serializers.CharField()
password = serializers.CharField(min_length=8, write_only=True)
class Meta:
model = CustomUser
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password', None)
# as long as the fields are the same, we can just use this
instance = self.Meta.model(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
single post from the API
{
"id": 1,
"post_title": "first_post",
"post_body": "qwe1",
"created_date": "2020-11-17T19:30:55Z",
"published_date": null,
"post_author": 1
},
You need to override the serializer:
class PostSerializer(serializers.ModelSerializer):
post_author_username = serializers.ReadOnlyField(source="post_author.username")
class Meta:
model = Post
fields = [post_author_username, post_title, post_body, created_data, published_data]
You can to specify the post_author serializer in your PostSerializer:
class PostSerializer(serializers.ModelSerializer):
post_author=CustomUserSerializer(read_only=True)
class Meta:
model = Post
fields = '__all__'
You can see the documentation here

How to update Foreign key field using nested serializer in django

I am facing one issue for updating models using django serializer.
Here is my models:
class User(models.Model):
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
designation = models.CharField(max_length=200, blank=True)
contact_number = models.CharField(max_length=20, blank=True)
team = models.CharField(max_length=200, blank=True)
manager = models.CharField(max_length=200, blank=True)
joining_date = models.DateField(default=datetime.now)
I need to create a serializer for editing profile details of the current user. In this User details like designation, contact_number , team , manager, joining_date are in UserProfile model and te first_name and last_name are in User model .... At one edit both the models needs to get update
This is my serializer.
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name','last_name')
class UserProfileSerializer(ModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('id', 'designation', 'contact_number', 'team', 'manager',
'joining_date','user')
def update(self, instance, validated_data):
user = validated_data.get('user')
instance.user.first_name = user.get('first_name')
instance.user.save()
return instance
I am getting an error {
"user": [
"This field is required."
]
}
Change user field to DictField. In this way, it will not be treated as foreignkey.
In to_representation, serialize user object and you will get data in format in which you want to get.
class UserSerializer(ModelSerializer):
class Meta:
model = User
fields = ('id', 'first_name','last_name')
class UserProfileSerializer(ModelSerializer):
user = serializer.DictField(required=False, write_only=True, default={})
def to_representation(self, instance):
data = super().to_representation(instance)
data.update({'user': UserSerializer(instance.user).data})
return data
class Meta:
model = UserProfile
fields = ('id', 'designation', 'contact_number', 'team', 'manager',
'joining_date','user')
def update(self, instance, validated_data):
user = validated_data.get('user')
instance.user.first_name = user.get('first_name')
instance.user.save()
return instance

Cannot query "object": Must be "User" instance

I am creating sample-api which have posts and followers. Post should visible to followers only
My models.py
from django.contrib.auth.models import User
class Post(models.Model):
creator = models.ForeignKey(User, related_name='creator_post_set', null=True, on_delete=models.CASCADE)
title = models.CharField(max_length=25)
created_date = models.DateTimeField(auto_now_add=True)
content = models.TextField()
likes = models.BigIntegerField(null=True)
comments = models.BigIntegerField(null=True)
class Follow(models.Model):
follower = models.ForeignKey(User, related_name='following', null=True, on_delete=models.CASCADE)
followed_on = models.DateTimeField(auto_now_add=True)
following = models.ForeignKey(User, related_name='follower',null=True, on_delete=models.CASCADE)
My serializers.py for the models:
class UserSerializer(ModelSerializer):
password = serializers.CharField(write_only=True)
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
password=validated_data['password'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
)
return user
class Meta:
model = User
fields = ('password', 'username', 'first_name', 'last_name',)
class PostListSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['title', 'content', 'created_date',]
class FollowSerializer(serializers.ModelSerializer):
class Meta:
model = Follow
fields = '__all__'
My views.py:
class PostList(generics.ListCreateAPIView):
serializer_class = PostListSerializer
follow_model = FollowSerializer.Meta.model
post_model = PostSerializer.Meta.model
def get_queryset(self):
try:
followers = self.follow_model.objects.get(follower_id =
self.request.user.id)
queryset = self.post_model.objects.get(creator__in = followers)
except self.follow_model.DoesNotExist:
queryset = None
return queryset
When I call this view it returns the following error:
Cannot query "Follow object (1)": Must be "User" instance.
I need help Thanks in Advance.
As I can see, Post model's creator is FKed to User model. So you need to query using User model instance, not Follower model.
You can use the following code:
following = self.request.user.following.all().values_list('follower', flat=True) # because of related name
queryset = self.post_model.objects.filter(creator_id__in = list(following))
Here I have first retrieved the user ids using self.request.following.all() by reverse relationship. Then I have extracted the user ids using values_list. After that, I have used it in Post.objects.filter(...) method.

how to post multiple model data through one serializer in django rest api

I have two models, Contact and User:
class Contact(models.Model):
name = models.CharField(max_length=50, blank=True)
status = models.BooleanField(default=False)
class User(models.Model):
username = models.CharField(max_length=50, blank=True)
password = models.CharField(max_length=50, blank=True)
contact_id = models.ForeignKey(Contact, on_delete=models.CASCADE, blank=True, null=True)
For these two models I have two serializer classes:
class ContactSerializerModel(serializers.ModelSerializer):
class Meta:
model = Contact
fields = ('name', 'status')
class UserSerializerModel(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password','contact_id')
I want to design a serializer class which takes name, username, password, and status fields. I want to post them as key-value pairs. First name and status value saved in the Contact model then with Contact model id saves username and password in User table. How to design the serializer class in Django rest API?
You need to override create method inside UserSerializerModel:
class UserSerializerModel(serializers.ModelSerializer):
contact_id = ContactSerializerModel()
class Meta:
model = User
fields = (
'username',
'password',
'contact_id'
)
def create(self, validated_data):
contact_data = validated_data.pop('contact_id')
contact = Contact.objects.create(**contact_data)
user = User.objects.create(contact_id=contact, **validated_data)
return user
See details here.
What you're looking for is writeable nested serializers, and there's a section of this in the DRF docs