Django Rest Framework - How to set the current_user when POST - django

I have just followed a small tutorial using DRF, but I can't figure how to like my model to his user when POSTing a new object
this is my model :
# Create your models here.
class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
#######
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
and so my serializer
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ("id", "title", "subtitle", "user_id")
so, now in the view I have access to the current_user with this :
request.user
class ListProjectsView(generics.ListCreateAPIView):
authentication_classes = [authentication.TokenAuthentication]
queryset = Project.objects.all()
serializer_class = ProjectSerializer
def list(self, request):
queryset = Project.objects.filter(user_id=request.user.id)
serializer = ProjectSerializer(queryset, many=True)
return Response(serializer.data)
[...]
def create(self, request, pk = None):
return super(ListProjectsView, self).create(request, pk = None)
I suppose there is a way to passe the request.user is the create in order to allow my Project.user_id to be filled ?
Whatever I'm doing, I can't manage to set the user, and i get the
django.db.utils.IntegrityError: null value in column "user_id" violates not-null constraint error
Any idea?
Thanks!

Try to override with following method. Everytime PUT/PATCH/CREATE operation is performed following method is called. This is the good way to pass the current user.
def perform_create(self, serializer):
serializer.save(user = self.request.user)

class Project(models.Model):
# project title
title = models.CharField(max_length=255, null=False)
# subtitle
subtitle = models.CharField(max_length=255, null=False)
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
okay so you have FK user but try to access it with the user_id = request.user.id
Just call
queryset = Project.objects.filter(user=request.user)
queryset = Project.objects.filter (user_id=request.user.id)
if you want to match the id you should put two __
like so user__id = request.user.id but I dont see any sence making it.

Related

How to retrieve data from model that current user created and list it for another model's field in django

Let us imagine that I have two models.
First model contains curse details and user that created this course
class Course(models.Model):
course_name = models.CharField(max_length=100, null=False)
description = models.CharField(max_length=255)
user_profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
and my second model is:
class Lesson(models.Model):
course = models.OneToOneField(Course, on_delete=models.CASCADE) #
# inside the course I want my APIVIEW to list only the courses that current user created.
# OnetoOne relationship does not solve the problem.
status = models.CharField(choices=STATUS, null=False, default=GOZLEMEDE,max_length=20)
tariffs = models.FloatField(max_length=5,null=False,default=0.00)
continues_off = models.CharField(max_length=2)
user_profile = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
My serializers for both Models:
class LessonSerializer(serializers.ModelSerializer):
class Meta:
model = models.Lesson
fields = ('course', 'status', 'tariffs', 'continues_off', 'user_profile')
def create(self, validated_data):
lesson = models.Lesson.objects.create(
course = validated_data['course'],
status = validated_data['status'],
tariffs=validated_data['tariffs'],
continues_off=validated_data['continues_off'],
user_profile=validated_data['user_profile']
)
return lesson
class CourseSerializer(serializers.ModelSerializer):
"""Serializers Course content"""
class Meta:
model = models.Course
fields = '__all__'
def create(self,validated_data):
course = models.Course.objects.create(
course_name = validated_data['course_name'],
description=validated_data['description'],
user_profile=validated_data['user_profile']
)
return course
My Viewset:
class LessonViewset(viewsets.ModelViewSet):
model = models.Lesson
serializer_class = serializers.LessonSerializer
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,BasePermission,)
def get_queryset(self):
user_current = self.request.user.id
return models.Lesson.objects.filter(user_profile=user_current)
How can I get the desired result. I want to get the courses for the current user and show them as a dropdown list in my API view. Just only the courses that user created should be in the dropdown list not all.
OnetoOne relationship gives all results of course table.
i think change your view code to :
def get_queryset(self,id):
return model.objects.filter(user_profile=id)
#You do not need to call it again when you put the Lesson on the model
\

Django API issue trying to create a method to add lookup data from another table

Django API Issue
I am wanting to append a data field [site_name] stored in the parent table [USERaffiliates] to a child record [USERaffylinks].
site_name method: I am doing this so that I have access to the site_name field on a Vue.js component ... where I display the link data -- –
There is a 1:Many relationship with 1 USERaffiliates having many USERaffylinks.
I would so love some help - I have tried so many things but keep getting errors in the API when I try adding a new USERaffylinks record via the API browser screen.
I tried adding this method > def site_name - but failed.
Here's the traceback
traceback image
Without the site_name method, the API works just fine.
def site_name(self): is wrong somehow.
class USERaffylinks(models.Model):
owner_link_useraffyid = models.ForeignKey(USERaffiliates, on_delete=models.CASCADE)
owner_link_short = models.CharField(max_length=27, null=True, default=None, blank=True)
def site_name(self):
if USERaffiliates.objects.filter(id=self.owner_link_useraffyid).exists():
sitename = USERaffiliates.objects.get(id=self.owner_link_useraffyid)
return sitename.site_name
else:
return "ERROR"
FULL MODELS { without the def site_name(self) } -- API works fine with just this
class USERaffiliates(models.Model):
owneruserid = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
user_category = models.BigIntegerField(null=True, default=None, blank=True )
registered_email = models.EmailField(null=True)
site_name = models.CharField(max_length=40, null=True)
... ... ....
class USERaffylinks(models.Model):
owner_link_useraffyid = models.ForeignKey(USERaffiliates, on_delete=models.CASCADE)
owner_link_short = models.CharField(max_length=27, null=True, default=None, blank=True)
... ... ...
USERaffylinks View
#action(methods=['POST'], detail=True)
class AffyPartnerLinksViewSet(viewsets.ModelViewSet):
queryset= USERaffylinks.objects.all() #need to filter on login user > owneruserid=request.user.id
serializer_class = USERaffylinksSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,) #later IsAuthentiated
Serialiser File
class USERaffiliatesSerializer(serializers.ModelSerializer):
class Meta:
model = USERaffiliates
fields = '__all__'
extra_fields = ['user_category_desc','user_status_desc','num_affy_links']
extra_kwargs = {'owneruserid': {'required':True}}
def get_field_names(self, declared_fields, info):
expanded_fields = super(USERaffiliatesSerializer, self).get_field_names(declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
class USERaffylinksSerializer(serializers.ModelSerializer):
class Meta:
model = USERaffylinks
fields = '__all__'
extra_fields = ['site_name']
extra_kwargs = {'owner_link_useraffyid': {'required':True}}
def get_field_names(self, declared_fields, info):
expanded_fields = super(USERaffylinksSerializer, self).get_field_names(declared_fields, info)
if getattr(self.Meta, 'extra_fields', None):
return expanded_fields + self.Meta.extra_fields
else:
return expanded_fields
You are facing this error due to the wrong use of the actions decorator. As rest_framework's viewsets docs suggest that you can use actions decorator for making some extra actions for routing. In your case you are trying actions decorator on class which mean, you are making your whole class a routable which only accept POST method.
so instead of using this:
#action(methods=['POST'], detail=True)
class AffyPartnerLinksViewSet(viewsets.ModelViewSet):
queryset= USERaffylinks.objects.all() #need to filter on login user > owneruserid=request.user.id
serializer_class = USERaffylinksSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,) #later IsAuthentiated
try this:
class AffyPartnerLinksViewSet(viewsets.ModelViewSet):
queryset= USERaffylinks.objects.all() #need to filter on login user > owneruserid=request.user.id
serializer_class = USERaffylinksSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = (AllowAny,) #later IsAuthentiated
Hope this will solve your issue.
USERaffylinks.owner_link_useraffyid is a ForeignKey so when accessed it will perform a DB query to access the related object. You need to fix the way you check to see if the ForeignKey contains a valid id, simply access the field and catch any DoesNotExist errors
class USERaffylinks(models.Model):
owner_link_useraffyid = models.ForeignKey(USERaffiliates, on_delete=models.CASCADE)
owner_link_short = models.CharField(max_length=27, null=True, default=None, blank=True)
def site_name(self):
try:
return self.owner_link_useraffyid.site_name
except USERaffiliates.DoesNotExist:
return "ERROR"
If the ForeignKey can be null (or at least invalid) then maybe it would make sense to make the field nullable instead of having a default value

Django Serializer defining initial object

I can't seem to figure out how to pass in an initial value to a serializer. I have a multitenant django site and I am trying to now get APIs setup. The client field exists but needs to be hidden and read only. I thought this worked like a form and a view in traditional django. I would normally pass in the get_initial in the view. I tried that first but it doesn't work. I think I need to get the value directly in the serializer but I can't seem to get it to work.
model:
class Location(ClientAwareModel):
name = models.CharField(max_length=64, blank=True)
address = models.CharField(max_length=64)
address2 = models.CharField(max_length=64, blank=True)
city = models.CharField(max_length=64)
state = USStateField()
zip_code = USZipCodeField()
class Client(models.Model):
name = models.CharField(max_length=100)
subdomain_prefix = models.CharField(max_length=100, unique=True)
def __str__(self):
return self.name
class ClientAwareModel(models.Model):
client = models.ForeignKey(Client, on_delete=models.PROTECT)
class Meta:
abstract = True
def hostname_from_request(request):
# split on `:` to remove port
return request.get_host().split(':')[0].lower()
def client_from_request(request):
hostname = hostname_from_request(request)
subdomain_prefix = hostname.split('.')[0]
return Client.objects.filter(subdomain_prefix=subdomain_prefix).first()
serializer (you can see all my failed attempts commented out:
class LocationSerializer(serializers.ModelSerializer):
def get_client(self, obj):
# return client_from_request(self.request)
return client_from_request(self.context['request'])
# client = serializers.SerializerMethodField('get_client')
# client = serializers.SerializerMethodField()
# client = serializers.Field(source='get_client', read_only=True)
# client = serializers.ReadOnlyField(source='get_client')
# client = serializers.PrimaryKeyRelatedField(read_only=True, default='get_client')
client = serializers.PrimaryKeyRelatedField(read_only=True, source='get_client')
# client = serializers.HiddenField(default=get_client(self))
class Meta:
model = Location
fields = ['name', 'address', 'address2', 'city', 'state', 'zip_code', 'client']
viewset:
class LocationViewSet(viewsets.ModelViewSet):
queryset = Location.objects.all()
serializer_class = LocationSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
client = client_from_request(self.request)
return super().get_queryset().filter(client=client)
you can see the different ways I tried to pass in the value but no matter what I do I get
IntegrityError at /locations/
null value in column "client_id" violates not-null constraint
One easy way to pass the client object to serializer is to pass it in perform_create method, something like:
from rest_framework import serializers
class LocationViewSet(viewsets.ModelViewSet):
queryset = Location.objects.all()
serializer_class = LocationSerializer
permission_classes = [permissions.IsAuthenticated]
def get_queryset(self):
client = client_from_request(self.request)
return super().get_queryset().filter(client=client)
def perform_create(self, serializer):
client = client_from_request(self.request)
if not client:
raise serializers.ValidationError("Client does not exist")
serializer.save(client=client)
And also remove the client field from your serializer:
class LocationSerializer(serializers.ModelSerializer):
class Meta:
model = Location
fields = ['name', 'address', 'address2', 'city', 'state', 'zip_code']
In your viewset you are trying to get Location by filtering Client when your Location model does not have a Client FK.
Right here:
def get_queryset(self):
client = client_from_request(self.request)
return super().get_queryset().filter(client=client) <----
As #gregory mentioned in the comment above, adding Foreign Key would solve your problem, then you can just simply add it to your serializer.

Access foreign key value in Django Rest Framework permissions

I am coding a restaurant reviews app. User can write a review and add pictures to it. I am setting up permissions to StarterPicsViewset so only the author of the review can Update it.
So I set up a custom permission but as I run it, I have a 'StarterPic' object has no attribute 'restaurant_review_review_author_id' error.
What I need is to get the review_author related to the restaurant_review_id so I can compare it to the request.user
Here is my models:
class Restaurant(models.Model):
adress = models.CharField(max_length=240)
name = models.CharField(max_length=140)
class RestaurantReview(models.Model):
review_author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE)
class StarterPic(models.Model):
restaurant_review = models.OneToOneField(RestaurantReview,
on_delete=models.CASCADE)
name_1 = models.CharField(max_length=40)
picture_1 = models.ImageField()
My view:
class StarterPicsViewset(viewsets.ModelViewSet):
queryset = models.InsidePic.objects.all()
serializer_class = serializers.InsidePicsSerializer
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnReviewOrReadOnly]
and my permissions:
class IsOwnReviewOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.restaurant_review_review_author_id == request.user
StarterPic does not have such property like restaurant_review_review_author_id .
Try obj.restaurant_review.review_author to access review's author.

perform_create saving user data

I have EndPoint that let user booking halls. But when i try to booking hall it give me error NOT NULL constraint failed. I lock at the problem 'NOT NULL constraint failed' after adding to models.py. they suggest to put null in the filed. But in my case i must save the hall id otherwise I can not know the hall that user is booking (it save null in the hall)
at the beginning it show this Error: NOT NULL constraint failed: api_bookingmodel.user_id and I solve it by putting in the function perform_create ... serializer.save(user=self.request.user)
This is my Code....
Model classes
class HallModel(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='hall_owner')
name = models.CharField(max_length=100)
price = models.FloatField()
phone = models.CharField(max_length=9, blank=True)
size = models.CharField(max_length=50)
region = models.CharField(max_length=100)
state = models.CharField(max_length=100)
image_1 = models.ImageField(upload_to='halls_image')
image_2 = models.ImageField(upload_to='halls_image')
image_3 = models.ImageField(upload_to='halls_image')
created_at = models.DateTimeField(auto_now_add=True)
class BookingModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_booking')
hall = models.ForeignKey(HallModel, on_delete=models.CASCADE, related_name='hall_owner')
time_date = models.DateTimeField(auto_now_add=True)
booking_method = models.IntegerField()
Serializer
class HallSerializer(serializers.ModelSerializer):
booking = serializers.SerializerMethodField()
class Meta:
model = HallModel
fields = '__all__'
def get_booking(self, obj):
booking = BookingSerializer(obj.hall_owner.all(),many=True).data
return booking
def perform_create(self, serializer):
serializer.save()
class BookingSerializer(serializers.ModelSerializer):
hall = serializers.SerializerMethodField()
class Meta:
model = BookingModel
fields = '__all__'
depth = 1
def get_hall(self, obj):
serializer_data = HallSerializer(obj.hall).data
return serializer_data
View Sets
class HallViewSet(viewsets.ModelViewSet):
queryset = HallModel.objects.all()
serializer_class = HallSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class BookingViewSet(viewsets.ModelViewSet):
serializer_class = BookingSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_queryset(self):
return self.request.user.user_booking.all()
it show this Error: IntegrityError at /api/booking/
NOT NULL constraint failed: api_bookingmodel.hall_id... I think i must save the hall id in the perform_create function but I don't able to do it. the stange things is when i delete the depth = 1 in the booking serializer it not show me the Error ... any one have a solution.
You can override def perform_create in BookingViewSet class, like below:
def perform_create(self, serializer):
serializer.save(
user=self.request.user,
hall=self.request.data['hall_id']
)
make sure that hall_id is ready in post request. Also def perform_create in HallSerializer is useless. read cdrf.co