Get User objects from foreign key - python

I am using the Django Rest Framework. I have two models as shown below:
class Following(models.Model):
target = models.ForeignKey('User', related_name='followers', on_delete=models.CASCADE, null=True)
follower = models.ForeignKey('User', related_name='targets', on_delete=models.CASCADE, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '{} is followed by {}'.format(self.target, self.follower)
class User(AbstractBaseUser):
username = models.CharField(max_length=15, unique=True)
email = models.EmailField(max_length=100, unique=True)
What I'd like to have an API that will return all the followers the user has. However, I don't want it to be returned in the Following model format, I'd like for the followers to be returned as users in this format:
[
{
"username": Bob,
"email": example#example.com
},
{
"username": Charlie,
"email": example#example.com
}
]
Right now, I have something like this:
class FollowingAPIView(ListAPIView):
serializer_class = FollowingSerializer
def get_queryset(self):
return Following.objects.filter(target=3)
class FollowingSerializer(serializers.ModelSerializer):
class Meta:
model = Following
fields = ('follower', 'target')
This only shows the follower and target I don't want this. I want the actual data from User Also, I put target=3 as a placeholder just to test.

you should use a serializer for the User not following:
class UserSerializer(serializers.ModelSerializer):
is_following = serializer.IntegerField()
class Meta:
model = User
fields = ('username', 'email', 'is_following')
also, change your queryset in view as below:
from django.db import models
from rest_framework.generics import get_object_or_404
class FollowingAPIView(ListAPIView):
serializer_class = FollowingSerializer
### so add this method to get the requested user based on the username in the url
def get_requested_user(self):
##get the user based on the user name in url
filter_kwargs = {'username': self.kwargs['username']}
obj = get_object_or_404(User.objects.all(), **filter_kwargs)
return obj
def get_queryset(self):
requested_user = self.get_requested_user()
return User.objects.filter(targets__target=requested_user)\
.annotate(is_following=models.Count('followers', filter=models.Q(followers__follower=requested_user), distinct=True))
if you are using Django<2.0, your get_queryset should be like:
def get_queryset(self):
requested_user = self.get_requested_user()
return User.objects.filter(targets__target=requested_user)\
.annotate(is_following=models.Count(models.Case(models.When(models.Q(followers__follower=requested_user), 1))))
because you want to return a list of users, not Following instances. use following only to filter( targets__ in the filter above) the users that their target in Following is the currently authenticated user(at least in one of its targets).
updated
also, change your url to something like this:
path('/api/followers/<username>/', FollowingAPIView.as_view(), name='get_user_followers')

Related

Django Rest Framework how do I get the id I use in the URL

I have this serializer and I use it to get post detail of a post belonging to a user. The owner of the post is not the user that is currently logged in. I want to check if the post is bookmarked by the currently logged in user. The currently logged in user's id is passed in the request but I cannot find it in this context.
Here is the serializer:
class UserPostSerializer(serializers.ModelSerializer):
images = PostImageSerializer(many=True, read_only=True, required=False)
profile = serializers.SerializerMethodField()
bookmarked = serializers.SerializerMethodField()
class Meta:
model = Post
fields = [
"id",
"category",
"body",
"images",
"video",
"profile",
"published",
"bookmarked",
"created_at",
"updated_at",
]
depth=1
def get_profile(self, obj):
profile_obj = Profile.objects.get(id=obj.user.profile.id)
profile = ShortProfileSerializer(profile_obj)
return profile.data
def get_bookmarked(self, obj):
breakpoint()
bookmark = Bookmark.objects.filter(owner=obj.user.id, post=obj.id,marktype='post')
if bookmark:
return True
else:
return False
The problem is obj.user.id is the owner of the post. I need the logged in user whose id is passed in the url. Here is the model for the bookmark:
class Bookmark(models.Model):
marktype = models.CharField(max_length=50)
post = models.OneToOneField(Post, on_delete=models.CASCADE, null=True, blank=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True, verbose_name="created at")
updated_at = models.DateTimeField(auto_now=True, verbose_name="updated at")
class Meta:
verbose_name = "bookmark"
verbose_name_plural = "bookmarks"
ordering = ["created_at"]
db_table = "bookmarks"
def __str__(self):
return "{}'s bookmark".format(self.owner.username)
and here is the URL:
path("posts/<int:user>/home/", HomeView.as_view(), name="home"),
This self.context['request'].user returns the owner of the post and not the logged in user.
How do I get the id of the currently logged in user or the user whose id I pass in the URL please?
Maybe do you can use filters to the Viewset:
urls.py
path("posts/home/", HomeView.as_view(), name="home")
viewsets.py
from rest_framework import viewsets
from .models import Post
from .serializers import, UserPostSerializer
from .filters import OwnerFilter
class HomeView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = UserPostSerializer
filter_backends = (OwnerFilter,)
filters.py
from rest_framework.filters import BaseFilterBackend
class OwnerFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
owner = request.query_params.get('owner', None)
if not owner:
return queryset.all()
else:
try:
return queryset.filter(bookmarked__owner__id=owner)
except Exception:
return queryset.none()
Running
Then access the URL:
/posts/home/?owner=OWNER_ID_HERE
Solved it and you can get any kwargs from the view that handles the request. In my case adding the following to the get_bookmarked function gives me the id I send in the URL:
loggeduser = self.context.get('view').kwargs.get('user')

How to fetch data by foreign key in Django serializers?

I'm stuck on fetching data with a related foreign key. I am doing some kind of validation in which the create method would be allowed depending on the requesterid to be POSTed.
This is my userTable model:
class userTable(models.Model):
userid = models.UUIDField(primary_key=True, default = uuid.uuid4, editable=False)
username = models.CharField(max_length=50, null=False, unique=True)
userroleid = models.ForeignKey(roleTable, on_delete=models.CASCADE)
def __str__(self):
return self.username
Another model is the requestTable
class requestTable(models.Model):
rid = models.AutoField(primary_key=True)
requesterid = models.ForeignKey(userTable, on_delete=models.CASCADE)
(...)
This is my serializer:
class RequestCreateSerializer(serializers.ModelSerializer):
parts=PartSerializer(many=True)
class Meta:
model = requestTable
fields = ['rid','requesterid', (...)]
def create(self, instance, validated_data):
if instance.requesterid.userroleid == 3: #how can i fetch my `requesterid` data?
parts_data = validated_data.pop('parts')
request = requestTable.objects.create(**validated_data)
for part_data in parts_data:
partsTable.objects.create(request=request, **part_data)
return request
raise ValidationError("Sorry! Your role has no permission to create a request.")
Also, I'm quite confused if I should do validation in serializers, or views. If I do it in views, it just throws the ValidationError, and it doesn't seem to enter the if condition.
For reference, here's my views:
class RequestListCreateView(ListCreateAPIView):
queryset = requestTable.objects.all()
serializer_class = RequestCreateSerializer
def create(self, request, *args, **kwargs):
if request.data.get('requesterid__userroleid') == '3':
write_serializer = RequestCreateSerializer(data=request.data)
write_serializer.is_valid(raise_exception=True)
self.perform_create(write_serializer)
headers = self.get_success_headers(write_serializer.data)
return Response({"Request ID": write_serializer.instance.requestid, "Parts ID": [p.partsid for p in write_serializer.instance.parts.all()]},headers=headers)
raise ValidationError("Sorry! Your role has no permission to create a request.")
Hope someone can help me!

How to get user id from JWT

How to get user id from JWT token.
My JWT token has payload something like this:
{
"username": "Steve",
"is_admin": false,
"id": 1
}
How do I get access to user id?
I actually want to update certain fields in database as per the id, that are for a specific user.
Secondly after gaining the access how do I get update the fields?
models.py
class Profile(models.Model):
branch = models.CharField(max_length=20, null=True)
year = models.IntegerField(null=True)
image = models.ImageField(upload_to="accounts/images/", null=True, blank=True)
serializer.py
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('branch', 'year' ,'image',)
What will be the view to update these fields?
You should use the User object associated with the current request object. This assumes the cookie being present means that the user has a logged-in session when visiting the view in question,
def update_profile(request):
current_user = request.user
profile = Profile.objects.get(user=current_user.pk)
profile.update(field_a="value a", field_b="value b") # etc, example only
profile.save()
To make this possible, you also need to add the user relation as a OneToOne field within the Profile object:
from django.contrib.auth import User # assuming no customisation has been done to User model
# All your other imports and code
class Profile(models.Model):
branch = models.CharField(max_length=20, null=True)
year = models.IntegerField(null=True)
image = models.ImageField(upload_to="accounts/images/", null=True, blank=True)
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key=False,
)
serializers.py
from django.contrib.auth import get_user_model
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
exclude = ()
views.py
import base64
import json
from django.contrib.auth import get_user_model
from rest_framework.generics import RetrieveUpdateAPIView
class MyUserViewset(RetrieveUpdateAPIView):
queryset = get_user_model().objects.all()
serializer_class = MyUserSerializer
pagination_class = None
def get_object(self):
request = self.request
token = http_auth = request.META.get('HTTP_AUTHORIZATION', None)
token = token.replace("Token ", "")
user_json = json.loads(base64.b64decode(token.split(".")[1]))
user_id = user_json['id']
User = get_user_model()
user_obj = User.objects.get(id=user_id)
return user_obj

Django-rest serializer returning code instead of list of objects

I have an endpoint in my Django-rest application in which I expect to receive the following get response:
{
"my_objects": [
{
"my_object_order": 1,
"related_topics": [{"title": "my_title", "subtitle": "my_subtitle"}, {"title": "my_title2", "subtitle": "my_subtitle2"}],
"collected_at": "2016-05-02T20:52:38.989Z",
}]
}
In order to achieve that, below you can observe my serializers.py
class TopicSerializer(serializers.ModelSerializer):
class Meta:
model = MyTopic
fields = ["title", "subtitle"]
class MyObjectSerializer(serializers.ModelSerializer):
related_topics = TopicSerializer(many=True)
class Meta:
model = MyObject
fields = ("my_object_order",
"related_topics")
def create(self, validated_data):
"""
Saving serialized data
"""
related_topics_list = validated_data.pop("related_topics", [])
obj = MyObject.objects.create(**validated_data)
for topics_data in related_topics_list:
MyTopic.objects.create(trend=trend, **topics_data)
return obj
As suggested, here you can see my models.py
class MyObject(models.Model):
my_object_order = models.IntegerField()
collected_at = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.story_title
class MyTopic(models.Model):
my_obj = models.ForeignKey(MyObject, related_name="related_topics")
title = models.CharField(max_length=50, blank=False, null=True)
subtitle = models.CharField(max_length=50, blank=True, null=True)
def __unicode__(self):
return self.title
Below you have the excerpt from my views.py
def get(self, request):
params = request.QUERY_PARAMS
# Filtering data
obj_list = my_fun(MyObject, params)
response = {"my_objects": obj_list.values("my_object_order",
"collected_at",
"related_topics")}
return Response(response)
I have looked on the documentation, however I am confused/not understanding fundamentally what I should do.
Your problem is in views.py, you are not using actually the serializer at all. You are just filter some data and return whatever values you get from database (hence the ids only).
I suggest you to check Generic Class Based Views
from myapp.models import MyObject
from myapp.serializers import MyObjectSerializer
from rest_framework import generics
class MyObjectListAPIView(generics.ListAPIView):
queryset = MyObject.objects.all()
serializer_class = MyObjectSerializer
Also if you need any filtering check documentation here. Basically you can filter by fields from model with this snippet
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('field1', 'field2')
PS: You can do the view as normal function, but you have to handle yourself filtering/serialization part, the code may not look as cleaner as you get with class based views.

How can I add a car to a user with Django Rest Framework

When i try to add a Car for my user this is what's happening
"username": [
"A user with that username already exists."]
This is my models I'm using Django auth User model
There are my models
class Car(models.Model):
"""
CAR Table with a ForeingKey from USER Table
"""
user = models.ForeignKey(User, related_name='cars')
model = models.CharField(max_length=50)
color = models.CharField(max_length=20)
year = models.IntegerField()
seats = models.IntegerField()
def __unicode__(self):
return u'%d: %s: %s: %d' % (self.id, self.model, self.color, self.year)
My Serializers
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = Car
fields = (
'model',
'color',
'seats'
)
class AddCarSerializer(serializers.ModelSerializer):
car = CarSerializer()
class Meta:
model = User
fields = (
'username',
'car'
)
this is my View
class AddCarForUser(APIView):
authentication_classes = (TokenAuthentication,)
permission_classes = (IsAuthenticated,)
def put(self, request):
car = AddCarSerializer(data = request.data)
car.is_valid(raise_exception = True)
car.save()
return Response(status=status.HTTP_201_CREATED)
And this is what i'm sending in a Put Request
{
"username": "root",
"car": {
"model": "Seat Ibiza",
"color": "Verde",
"seats": "4"
}}
Your code doesn't works because you use serializer.ModelSerializer and the username attribute of theUser class must be unique, that why It can't validate it. To overcome this, follow these steps.
Update your serializer AddCarSerializer. We add a custom field username to handle username passed without a unique, just a simple CharField. And we add create function because Nested Serializer can't handle creation or update out the box:
class AddCarSerializer(serializers.Serializer):
# add username custom field
username = serializers.CharField()
car = CarSerializer()
class Meta():
fields = (
'username',
'car'
)
def create(self, validated_data):
""" Add car to an user
"""
tmp_car = validated_data.pop('car')
user = User.objects.get(username=validated_data['username'])
car = Car.objects.create(
user=user,
model=tmp_car['model'],
color=tmp_car['color'],
seats=tmp_car['seats'],
)
return car
update your view :
class AddCarForUser(APIView):
def put(self, request):
serializer = AddCarSerializer(data = request.data)
if serializer.is_valid():
serializer.save()
return Response(status=status.HTTP_201_CREATED)
return Response(status=status.HTTP_400_BAD_REQUEST)
update your Car model, because year attribute doesn't exist when we add a car to an user then, add null=True :
class Car(models.Model):
user = models.ForeignKey(User, related_name='cars')
model = models.CharField(max_length=50)
color = models.CharField(max_length=20)
year = models.IntegerField(null=True)
seats = models.IntegerField()
And it should work. Don't forget to handle error like if the username passed doesn't exist.

Categories

Resources