I have this URL and I send the currently logged in user's id (viewer (33)) and the id of the user being looked at (user (1)) in the URL:
path("users/<int:viewer>/<int:user>/profile/", OtherProfile.as_view()),
Here is the view handling that URL:
class OtherProfile(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = ProfileSerializer
name = "other-profile"
lookup_field = "user"
def get_queryset(self):
breakpoint()
viewer = self.kwargs["viewer"]
user = self.kwargs["user"]
return Profile.objects.all().filter(user_id=user)
There is a breakpoint here and the result of
print(self.kwargs["viewer"]) gives 33 which is correct
print(self.kwargs["user"]) gives 1 which is also correct
Here is the profile serializer as specified in the serializer_class:
class ProfileSerializer(serializers.ModelSerializer):
location = serializers.SerializerMethodField()
user = UserSerializer()
followers = serializers.SerializerMethodField()
class Meta:
model = Profile
fields = [
"id",
"background",
"photo",
"first_name",
"middle_name",
"last_name",
"birthdate",
"gender",
"bio",
"occupation",
"is_verified",
"verification",
"website",
"location",
"user",
"followers",
"created_at",
"updated_at",
]
def get_location(self, obj):
location_obj = Location.objects.filter(profile=obj.id)
if location_obj:
location_obj = Location.objects.get(profile=obj.id)
location = LocationSerializer(location_obj)
return location.data
def get_followers(self, obj):
breakpoint()
followers = Follow.objects.filter(followee=obj.user.id)
return followers.count()
I put a breakpoint on the get_followers method so I can look at the data.
print(self.context["view"].kwargs) - prints ["viewer", "user"]
My question:
Why does print(self.context["view"].kwargs["user"]) print 33 when on the view the kwargs user should be 1?
Why does it give me a key error when I try and print(self.context["view"].kwargs["viewer"])?
Related
I am new to Django Rest Framework and checked some tutorials. Now I am trying to create my own user profile with more fields like: company name, phone, ....I created OneToOneField (one-to-one relationship) table with more info for my extend user. Now i want to create new user with all fields in post method, but i got error. How can i fix this?
models.py
class MoreInfo(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
compName = models.CharField(max_length=100)
title = models.CharField(null=True,max_length=128)
birthday = models.DateField(null=True, blank=True)
phone = models.CharField(max_length=20,blank=True)
api/serializer.py
class MoreInforSerializer(serializers.ModelSerializer):
class Meta:
model = MoreInfo
fields = '__all__'
class CreateUserSerializer(serializers.ModelSerializer):
moreInfoUser = MoreInforSerializer()
class Meta:
model = User
fields = '__all__'
extra_kwargs = {'password':{'write_only':True}}
def create(self,validated_data):
user = User.objects.create(
email=validated_data['email'],
username = validated_data['username'],
password = make_password(validated_data['password'])
)
info_data = validated_data.pop('moreInfoUser')
moreInfo = MoreInfo.objects.create(
user = user,
compName = info_data['compName'],
title = info_data['title'],
birthday = info_data['birthday'],
phone = info_data['phone']
)
# user.save()
return user
views.py
class ListCreateUser(ListCreateAPIView):
serializer_class = CreateUserSerializer
def post(self, request, *args, **kwargs):
serializer = CreateUserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return JsonResponse({
'message': 'Create a new Info successful!'
}, status=status.HTTP_201_CREATED)
return JsonResponse({
'message': 'Create a new Info unsuccessful!'
}, status=status.HTTP_400_BAD_REQUEST)
urls.py
path('createUser',views.ListCreateUser.as_view()),
POST:
{
"username":"user5",
"password":"12345aA#",
"email":"user5#gmail.com",
"compName":"A",
"title":"test",
"birthday":"1997-05-04",
"phone":"01234567"
}
Table for create User
enter image description here
Errors:
Can't create new User
Bad Request: /createUser
"POST /createUser HTTP/1.1" 400 46
You have to upload moreInfoUser also because you set that in the serializer.
{
"username":"user5",
"password":"12345aA#",
"email":"user5#gmail.com",
"compName":"A",
"title":"test",
"birthday":"1997-05-04",
"phone":"01234567",
"moreInfoUser": {
"compName": "...",
"title": "...",
"birthday": "...",
"phone": "..."
}
}
I have a nested serializes, which content I need to return as Null in case of the parent-serializer field "is_profile_private" (a boolean) is True.
I tried using get_queryset in order to filter the User Profile but no progress was made.
Tried using SerializerMethordField() and get_profile() but Django complained about UserProfileSerializer type of object not being allowed to be serialized.
serializers.py
class UserProfileSerializer(UserSerializer):
height = serializers.SerializerMethodField()
class Meta:
model = UserProfile
fields = (
"bio",
"gender",
"custom_gender",
"non_binary_list",
"birthdate",
"avatar",
"height",
"hometown",
"zodiac_sign",
"language",
)
#staticmethod
def get_height(obj):
return {"value": obj.height, "unit": obj.height_unit}
class SimpleUserSerializer(UserSerializer):
profile = UserProfileSerializer(source="user", required=False)
class Meta:
model = User
fields = (
"id",
"name",
"username",
"is_profile_private",
"date_joined",
"profile",
)
views.py
class UserProfileAPIView(RetrieveModelMixin, UpdateModelMixin, GenericViewSet):
lookup_field = "id"
queryset = User.objects.all()
serializer_class = SimpleUserSerializer
http_method_names = ["get"]
#staticmethod
def get(request, *args, **kwargs):
return User.objects.get(id=str(request.data))
You can use SerializerMethodField:
class SimpleUserSerializer(UserSerializer):
profile = serializers.SerializerMethodField()
class Meta:
model = User
fields = (
"id",
"name",
"username",
"is_profile_private",
"date_joined",
"profile",
)
def get_profile(self, obj):
if obj.is_profile_private:
return None
return UserProfileSerializer(obj.user).data
please note that you should return serializer's data, not serializere itself.
I have spent already a couple of days researching about this issue in similar questions and I am not able to get a solution. This should be something simple, I have a model:
model.py
class Item(models.Model):
"""Class to represent an item..."""
label = models.TextField(null=True)
name = models.TextField()
category = models.ForeignKey( "Category", on_delete=models.SET_NULL,
null=True, default=DEFAULT_CATEGORY_ID)
class Category(models.Model):
"""Class to represent the category of an Item. Like plants, bikes..."""
name = models.TextField()
description = models.TextField(null=True)
view.py
class ItemViewset(viewsets.ModelViewSet): # pylint: disable=too-many-ancestors
"""API Endpoint to return the list of items"""
queryset = Item.objects.all()
serializer_class = ItemSerializer
serializer.py
class ItemSerializer(serializers.ModelSerializer):
"""Serializer for Item."""
category = CategorySerializer(read_only=True)
class Meta: # pylint: disable=too-few-public-methods
"""Class to represent metadata of the object."""
model = Item
fields = [ 'id', 'label', 'name', 'category']
read_only_fields = ['id']
# def to_representation(self, instance):
# ret = super().to_representation(instance)
# ret['category'] = CategorySerializer(instance.category).data
# return ret
def create(self, request):
# Look up objects by arbitrary attributes.
# You can check here if your students are participating
# the classes and have taken the subjects they sign up for.
category = get_object_or_404(Category(), id=request.data.get('category'))
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(category=category)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
If I have the line category = CategorySerializer(read_only=True) commented the nested get won't work, for example for a curl to an object. I will get just de id of the structure. If I uncomment it like it is in the example I get the right response:
{
"id": 60,
"label": null,
"name": "Delete me",
"category": {
"id": 1,
"name": "IT"
}
}
But then the post with id will not work, the category will be set to null.
How can I get both working at the same time?
Update: This is the post I want to do:
{
"label": "00000003",
"name": "Delete me",
"category": 1,
}
You can override the to_represention method of your serializer
class ItemSerializer(serializers.ModelSerializer):
"""Serializer for Item."""
class Meta: # pylint: disable=too-few-public-methods
"""Class to represent metadata of the object."""
model = Item
fields = [ 'id', 'label', 'name', 'category']
read_only_fields = ['id']
def to_representation(self, instance):
data = super().to_representation(instance)
data["category"] = CategorySerializer(instance.category).data
return data
So I am creating follower system but there is a problem. Everything is working properly. but in follower serializer I want the username's and other details of users.
models.py
class Follow(models.Model):
user = models.OneToOneField(User,related_name="user" ,on_delete=models.CASCADE)
''' to obtain user ' eeee = User.objects.first() , eeee.user' '''
following = models.ManyToManyField(User,related_name='following_user',blank=True)
''' to obtain followers ' eeee.following_user.all()' '''
''' to obtain following ' eeee.user.following.all()' '''
def __str__(self):
return self.user.username
In field following , user.following.all() is used to get the user in manytomany field of request.user and
following_user.all() is used get all the users who has added request.user in their following field.
serializers.py
class FollowerSerializer(ModelSerializer):
user = UserSerializer(many=False)
follower = SerializerMethodField()
class Meta:
model = Follow
fields = ('user','follower')
def get_follower(self, obj):
context = self.context
request = context.get("request")
return request.user.following_user.all().values()
Here I am serializing all the user who has added request.user in their following field
views.py
class FollowerView(RetrieveAPIView):
queryset = Follow.objects.all()
serializer_class = FollowerSerializer
permission_classes = [IsAuthenticated]
lookup_field = 'id'
api
{
"user": {
"name": "eeee"
},
"is_follower": [
{
"id": 2,
"user_id": 9
},
{
"id": 5,
"user_id": 16
},
{
"id": 3,
"user_id": 10
}
]
}
These is the api I am getting of all the user who has added request.user in their following list.
the problem here is I am getting the pk of the user from the key user_id. But I want there username and other information like email, full name of the user who has that primary key. So how can I achieve that?
Modify the get_follower method in serializers.py as follows:
def get_follower(self, obj):
context = self.context
request = context.get("request")
qs = request.user.following_user.all()
data = [{'id': obj.pk, 'user_id': obj.user_id, 'name': obj.req_field} for obj in qs]
return data
I try to send the following data to my django application:
{
"hashtags": ["hashtag"],
"title": "title",
"message": "message"
}
and i get this response:
{
"hashtags": [
{
"non_field_errors": [
"Invalid data. Expected a dictionary, but got int."
]
}
]
}
I have the following view defined in views.py
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = IsAuthorizedOwnerOrReadOnly,
the models are defined like this:
class Post(models.Model):
ambassador = models.OneToOneField("User")
publish_date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
hashtags = models.ManyToManyField("PostHashtags", related_query_name="posts")
title = models.CharField(max_length=TEXT_SHORT, null=True, blank=True)
message = models.CharField(max_length=TEXT_MIDDLE, null=True, blank=True)
class PostHashtags(models.Model):
hashtag = models.CharField(max_length=TEXT_SHORT, null=False)
def __unicode__(self):
return self.hashtag
and i define the serializers like this:
class PostHashtagSerializer(serializers.ModelSerializer):
class Meta:
model = PostHashtags
fields = ("hashtag",)
class PostSerializer(serializers.ModelSerializer):
hashtags = PostHashtagSerializer(many=True)
class Meta:
model = Post
fields = ("id", "hashtags", "title", "message",)
read_only_fields = ("id", 'account_name',)
It seems like the hashtags are not created automatically using my current serialisation config. Is there a way to have my hashtags created if they do not yet exist, and if they do exist, have the Post use that same hashtag? In that case, how should my serialisers be defined?
EDIT 1:
After GwynBleidD's suggestions I now get the following error:
The `.create()` method does not support writable nestedfields by default.
Write an explicit `.create()` method for serializer PostSerializer , or set `read_only=True` on nested serializer fields.
Does anyone have a suggestion for such a create method?
EDIT 2: solved it using the following serialisers
class PostHashtagSerializer(serializers.ModelSerializer):
hashtag = serializers.CharField()
class Meta:
model = PostHashtags
fields = ("hashtag",)
class PostSerializer(serializers.ModelSerializer):
hashtags = PostHashtagSerializer(many=True,)
class Meta:
model = Post
fields = ("ambassador", "hashtags", "title", "message",)
def create(self, validated_data):
hashtags_data = validated_data.pop('hashtags')
post = Post.objects.create(**validated_data)
for hashtag in hashtags_data:
ht = PostHashtags.objects.create()
ht.hashtag = hashtag.get("hashtag")
ht.save()
post.hashtags.add(ht)
post.save()
return post
Hashtags are not string, but dict in that example. You have to submit:
{
"hashtags": [{"hashtag": "hashtag"}],
"title": "title",
"message": "message"
}