Use a related field in lookup_field in Django REST framework - python

I have a user model and a profile model that is created automatically when a user is registered. In my view for retrieving the user profile, I am trying to get the user profile using the username but DRF uses pk by default. So I added a lookup field with the username but DRF can't seem to resolve it:
my profile model:
class Profile(TimeStampedModel):
user = models.OneToOneField('User', on_delete=models.CASCADE, related_name='customer_profile')
bio = models.TextField(blank=True)
image = models.URLField(blank=True)
def __str__(self):
return self.user.username
my profile serializer:
class ProfileSerializer(serializers.ModelSerializer):
username = serializers.StringRelatedField(source='user.username', read_only=True)
bio = serializers.CharField(allow_blank=True, allow_null=True)
image = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = ['username', 'bio', 'image']
def get_image(self, obj):
if obj.image:
return obj.image
return 'https://static.productionready.io/images/smiley-cyrus.jpg'
my profile retrieving view:
class ProfileRetrieveAPIView(generics.RetrieveAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'username'
def retrive(self, request, username, *args, **kwargs):
try:
profile = Profile.objects.select_related('user').get(
user__username = username
)
except Profile.DoesNotExist:
ProfileDoesNotExist
serializer = self.serializer_class(profile)
return Response(serializer.data, status=status.HTTP_200_OK)
my profile endpoint:
urlpatterns = [
# ....
path('profiles/<str:username>', views.ProfileRetrieveAPIView.as_view()),
]
I am getting the error:
Cannot resolve keyword 'username' into field. Choices are: bio, created_at, id, image, updated_at, user, user_id
What am I doing wrong?

Use nested serialzer.
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username',)
And in your profile serializer:
username = UserSerializer(many=False, read_only=True)
Or use SerializerMethodField()
username = SerializerMethodField()
And define whats in in:
def username(self,obj):
//code

You can use this
class ProfileSerializer(serializers.ModelSerializer):
username = serializers.CharField(source='user.username', read_only=True)
bio = serializers.CharField(allow_blank=True, allow_null=True)
image = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = ['username', 'bio', 'image']
def get_image(self, obj):
if obj.image:
return obj.image
return 'https://static.productionready.io/images/smiley-cyrus.jpg'

Related

Incorrect type. Expected pk value, received str in DRF.(one to one field)

When I send post request with data in profile model at that time this error show.
Error
{
"user_name": [
"Incorrect type. Expected pk value, received str."
] }
I saw this answer but don't know how to implement SlugRelatedField in serializers(OneToOneField)
models.py:
class CustomUser(AbstractUser):
# username_validator = UnicodeUsernameValidator()
username = models.CharField(
max_length=80,
unique=True,
)
email = models.EmailField(
unique=True,
blank=True,
)
.....
def __str__(self):
return '%s' %(self.username)
class Profile(models.Model):
user_name = models.OneToOneField(to = CustomUser,on_delete=models.CASCADE)
full_name = models.CharField(null=True,max_length=15, blank=True)
public_name = models.CharField(null=True,max_length=15, blank=True)
....
serializers.py:
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ['user_name','full_name','public_name']
views.py:
class ProfileApiview(CreateAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
#Pradip - Have a look at this.
class ProfileSerializer(serializers.ModelSerializer):
user_name = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = Profile
fields = ['user_name','full_name','public_name']
def create(self, validated_data):
user = self.context['request'].user
profile = Profile.objects.create(
user_name=user, full_name=validated_data['full_name'].............)
return profile

Update a field value in model Django Rest

serializers.SerializerMethodField() is only meant to read value ie
retrieve, it will not update the lang value.
I want to create an API that can update a field value. Mainly, I want to change the field lang in the User model. Here is my code
models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
member_since = models.DateTimeField(default=timezone.now)
lang = models.CharField(max_length = 5, default = 'en')
def get_lang(self):
return self.lang
serializers.py
from django.contrib.auth import get_user_model
User = get_user_model()
class ProfileRetrieveUpdateSerializer(serializers.ModelSerializer):
member_since = serializers.SerializerMethodField()
lang = serializers.SerializerMethodField()
class Meta:
model = User
fields = [
'member_since','lang'
]
def get_member_since(self, obj):
return obj.profile.member_since.date()
def get_lang(self, obj):
return obj.profile.get_lang()
def update(self, instance, validated_data):
print(validated_data)
setattr(instance.profile, "lang", validated_data.get("lang",instance.profile.lang))
instance.save()
return instance
views.py
from django.contrib.auth import get_user_model
from .serializers import (
CurrentUserDetailSerializer, ProfileRetrieveUpdateSerializer,
UserLoginSerializer, UserSerializerWithToken,
)
User = get_user_model()
class ProfileRetrieveUpdateAPIView(RetrieveUpdateAPIView):
"""
View that returns user profile data.
"""
permission_classes = [AllowAny]
queryset = User.objects.all()
serializer_class = ProfileRetrieveUpdateSerializer
lookup_field = 'username'
lookup_url_kwarg = 'username'
axios.patch(url, {'lang': 'fr') (additional, I don't think it's wrong here)
When I call API, I get that the validated_data value is {}!
I think the problem is in the serializers.py module.
lang = serializers.CharField(source = 'get_lang')
def update(self, instance, validated_data):
setattr(instance.profile, "lang", validated_data.get("get_lang"))
instance.save()
return instance
reason: serializers.SerializerMethodField() is only meant to read value ie retrieve, it will not update lang value.

Failing to lookup a field with a foreign key subfield in Django Rest Framework

I am using the django framework to serialize a django object. I have a profile model and the first field in the model is a foreignkey of the user object. I want to be able to pass a usename is the url patters for the profile. In the Views, I want to be able to take that username and go through the user foreignkey and find the profile with that username user. THis is what I have right now in my code:
Views.py
class ProfileListView(ListAPIView):
query_set = Profile.objects.all()
serializer_class = ProfileSerializer
class ProfileDetailView(RetrieveAPIView):
query_set = Profile.objects.all()
serializer_class = ProfileSerializer
lookup_field = 'user__username'
urls.py
path('profile/', ProfileListView.as_view()),
path('profile/<username>', ProfileDetailView.as_view()),
serializers.py
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('user', 'gender', 'phone', 'ip_address', 'dob_month', 'dob_day',
'dob_year', 'address_street', 'address_city', 'address_state',
'address_postal', 'address_country', 'profile_pic', 'role')
models.py
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
gender = models.CharField(max_length=12, choices=GENDER_CHOICES) # entity type
phone = models.CharField(max_length=22)
ip_address = models.CharField(max_length=32)
dob_month = models.CharField(max_length=22, choices=MONTH_CHOICES)
dob_day = models.SmallIntegerField()
dob_year = models.SmallIntegerField()
address_street = models.CharField(max_length=140)
address_city = models.CharField(max_length=22)
address_state = USStateField()
address_postal = models.IntegerField()
address_country = models.CharField(max_length=22)
profile_pic = models.ImageField(upload_to='profile_pics/')
role = models.CharField(max_length=12, choices=ROLE_CHOICES)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.user.username + ' - ' + self.type
One way of doing is to override get_object method. Try like this:
class ProfileDetailView(RetrieveAPIView):
queryset = Profile.objects.all() # it should be queryset not query_set
serializer_class = ProfileSerializer
def get_object(self):
return self.queryset.get(user__username=self.kwargs.get('username')).first()
There are some alternate solutions as well. For that please check this answer in SO.

django rest PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=App.objects.all()) is not JSON serializabl

I have some nested serializers, and I'm going to get the users objects in a JSON format through http requests, using following snippet:
import requests
r = requests.get('http://127.0.0.1:8000/accounts/users',
auth=("admin", "mat"))
But here is what it returns:
PrimaryKeyRelatedField(allow_empty=False, many=True, queryset=App.objects.all()) is not JSON serializabl
I tried a lot of approached include using to_representation method and inheriting from serializers.RelatedField. But always I get the same result.
It seems that I'm doing something wrong. I'd be appreciate if you have any suggestion regarding this?
Here are serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name',
'popularity')
class CategorySerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
class Meta:
model = Category
fields = ('en_name',
'fa_name',
'tags')
class CpSerializer(serializers.ModelSerializer):
class Meta:
model = CP
fields = ('en_name',
'fa_name',
'id_number')
class AppSerializer(serializers.ModelSerializer):
category = CategorySerializer()
cp = CpSerializer()
class Meta:
model = App
fields = ('en_name',
'fa_name',
'package_name',
'build_number',
'downloads',
'cp',
'category')
class UserAppSerializer(serializers.ModelSerializer):
class Meta:
model = UserApps
app = AppSerializer() # or even serializers.StringRelatedField()
fields = ('status', 'user_rate', 'comment', 'app')
def to_representation(self, instance):
return None
class UserSerializer(serializers.HyperlinkedModelSerializer):
id_number = serializers.CharField(read_only=True, source='userprofile.id_number')
apps = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('username',
'password',
'first_name',
'last_name',
'email',
'id_number',
'apps')
def get_apps(self, obj):
if obj.username != "admin":
return
else:
apps = UserAppSerializer(read_only=True,
many=True)
return apps
class UserProfileSerializer(serializers.HyperlinkedModelSerializer):
user = UserSerializer()
class Meta:
model = UserProfile
fields = ('user', 'id_number')
And here is the get method of my view:
def get(self, request):
print("call get method on {}".format(self.__class__.__name__))
if request.user.username == 'admin':
users = User.objects.all()
lookup_field = 'username'
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
I've defined the apps field in UserProfile as following:
apps = models.ManyToManyField('UserApps')
And in UserApps the app as following:
app = models.ManyToManyField(Application)
And in Application the cp and category as:
cp = models.ForeignKey('cp.CP')
category = models.ForeignKey(Category)
And in category the tags has been defined as:
tags = models.ManyToManyField('Tag')
def get_apps(self, obj):
if obj.username != "admin":
return
else:
apps = UserAppSerializer(read_only=True,
many=True)
return apps
This function is incorrect.
that function is getting user instance as parameter obj and is supposed to return value for app field.
However the function doesn't do anything with the user instance and instead of returning value return an empty instance of UserAppSerializer.
also
app = models.ManyToManyField(Application)
This should've been called apps for clarity
apps = models.ManyToManyField(Application)
Then your serializer function can be re-written like this
def get_apps(self, obj):
if obj.username != "admin":
return
else:
return UserAppSerializer(instance=obj.apps,
read_only=True, many=True).data

django rest framework ModelSerializer post data error

first, in models.py
class UserComment(models.Model):
user = models.ForeignKey(User)
rate = models.IntegerField()
description = models.CharField(max_length=512)
createTime = models.DateTimeField(auto_now=True)
def __unicode__(self):
return '<UserComment {%s %d}>' % (self.user.username, self.rate)
then, serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', )
class UserCommentSerializer(serializers.ModelSerializer):
user = UserSerializer(required=False)
class Meta:
model = UserComment
views.py
class UserCommentViewSet(viewsets.ModelViewSet):
queryset = UserComment.objects.all()
serializer_class = UserCommentSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, )
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.DATA, files=request.FILES)
serializer.is_valid()
print serializer.errors
print serializer.data
return super(UserCommentViewSet, self).create(request, *args, **kwargs)
then i post json data
{"user":{"id":"1","username":"watsy"},"rate":"5","description":"hello"}
i think,it will work. and insert it to db, but i get errors.
{"user": [{"username": ["User with this Username already exists."]}]}
>_<, I have no idea.
You need to make few changes to your serializer:
class UserCommentSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(read_only=True)
class Meta:
model = UserComment
depth = 1
Now pass this JSON dict in your request:
{"user":"1", "rate":"5", "description":"hello"}

Categories