Get username in model with extended user - django

At the moment, I get the user id when I create a new article. I would like also to have the username in the model (I'm using DRF with React, so it would save me a get request just for the username).
models.py:
class Article(models.Model):
title = models.CharField(max_length=100)
user = models.ForeignKey(
User, related_name='Article', on_delete=models.CASCADE)
serializers.py:
class ArticleSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Article
fields = ['id', 'title', 'user']
def save(self, **kwargs):
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)

The code below should get the username attribute from the user field.
getattr(self.fields['user'], self.fields['user'].USERNAME_FIELD)

Here is the solution:
class ArticleSerializer(serializers.ModelSerializer):
user = PublicProfileSerializer(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Article
fields = ['id', 'title', 'user']
def save(self, **kwargs):
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)
with PublicProfileSerializer:
from django.contrib.auth import get_user_model
UserModel = get_user_model()
class PublicProfileSerializer(serializers.ModelSerializer):
class Meta:
model = UserModel
fields = [
"id",
"first_name",
"last_name",
"username",
]

Related

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

Django Rest Framework link logged user to app model

When I add a new Article, I have a dropdown with a list of all registered users. I want to link the current user to the new Article.
models.py
from django.db import models
from django.conf import settings
User = settings.AUTH_USER_MODEL
class Article(models.Model):
title = models.CharField(max_length=100)
user = models.OneToOneField(User, related_name='Article', on_delete=models.CASCADE)
def __str__(self):
return self.title
serializers.py
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['id', 'title', 'user']
Here is the solution:
class ArticleSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(
read_only=True, default=serializers.CurrentUserDefault())
class Meta:
model = Article
fields = ['id', 'title', 'user']
def save(self, **kwargs):
kwargs["user"] = self.fields["user"].get_default()
return super().save(**kwargs)

How to include user_id (foreignKey) when posting an new record?

I'm new to python Django rest-framework, I'm facing this problem when creating a new address:
null value in column "user_id" violates not-null constraint
DETAIL: Failing row contains (21, full name, 123456789, any, any, any, any, any, any, f, null).
This is The address model:
from django.db import models
from django.contrib.auth.models import User
class UserAddress (models.Model):
user = models.ForeignKey(
User, related_name='addresses', on_delete=models.CASCADE)
full_name = models.TextField(default='')
phone = models.CharField(max_length=30, default='')
city = models.TextField()
province = models.TextField()
street = models.TextField()
description = models.TextField()
postcode = models.CharField(max_length=20)
country = models.CharField(max_length=100)
is_default = models.BooleanField(default=True)
class Meta:
db_table = 'user_addresses'
And this is the serializer:
from rest_framework import serializers
from user_action.models.address import UserAddress
class UserAddressSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(read_only=True)
class Meta:
model = UserAddress
fields = ['id', 'full_name', 'phone', 'city', 'province',
'street', 'description', 'postcode', 'country', 'is_default']
And the POST method:
#api_view(['POST'])
#permission_classes((IsAuthenticated,))
def createUserAddress(request):
user = request.user
if request.method == 'POST':
serializer = UserAddressSerializer(data=request.data)
if serializer.is_valid():
newAddress = serializer.save()
else:
return Response(serializer.errors)
return Response(serializer.data)
Thanks in advance.
Post request is unable to save User details. Try to pass user instance to serialiser.save() method.

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.

Additional field while serializing django rest framework

I am a newbie to django rest framework and have created a sample Employee model.
My models.py:
class Employees(models.Model):
created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
My serializers.py:
class EmployeeSerializer(serializers.Serializer):
class Meta:
model = Employees
fields = ('first_name','last_name')
This works fine but I want an additional field full_name, which will be first_name + last_name.
How do I define this new field full_name in my serializers.py?
I see two ways here (I prefer the first way since you can reuse it in other parts of the app):
add a calculated property to your model and add it to your serializer
by using a readonly field with source=
# models.py
class Employees(models.Model):
created = models.DateTimeField(auto_now_add=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
#property
def full_name(self):
return self.first_name + self.last_name
# serializers.py
class EmployeeSerializer(serializers.ModelSerializer):
full_name = serializers.Field(source='full_name')
class Meta:
model = Employees
fields = ('first_name','last_name', 'full_name')
by using SerializerMethodField
(your model unchanged)
class EmployeeSerializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField('get_full_name')
def get_full_name(self, obj):
return obj.first_name + obj.last_name
class Meta:
model = Employees
fields = ('first_name','last_name', 'full_name')
Provided that the Employee is a login user, then most of us will use django.auth.User, I will share how Employee can be implemented as another Profile (extension of django User). Also with the addition of full_name.read_only, first_name.write_only, and last_name.write_only
# models.py
class Employee(models.Model):
"""User Profile Model"""
user = models.OneToOneField('auth.User')
# serializers.py
class EmployeeSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.EmailField(source='user.email')
first_name = serializers.CharField(
source='user.first_name', write_only=True)
last_name = serializers.CharField(
source='user.last_name', write_only=True)
name = serializers.CharField(
source='user.get_full_name', read_only=True)
class Meta:
model = Employee
fields = (
'url', 'username', 'email',
'first_name', 'last_name', 'name')
depth = 1
SerializerMethodField works fine, and we can also store data in serializer object and let method get_field_name use that.
Example:
class MySerializer(serializers.ModelSerializer):
statistic = serializers.SerializerMethodField()
def __init__(self, instance=None, data=serializers.empty, statistic=None, **kwargs):
super(MySerializer, self).__init__(instance=instance, data=data, **kwargs)
self.statistic = statistic
def get_statistic(self, obj):
if self.statistic is None:
return serializers.empty
return self.statistic.get(obj.id, {})