So, I have two simple models:
class User(AbstractUser, models.Model):
username = models.CharField(
'Username', max_length=255,
db_index=True, unique=True
)
email = models.EmailField(
'Email', max_length=255, db_index=True,
blank=True, null=True, unique=True
)
objects = UserManager()
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ('email', 'password',)
class Book(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(User, on_delete=models.CASCADE)
and a serializer like this:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['title', 'author']
def validate_author(self, author): # < --- it doesn't work
if author != serializers.CurrentUserDefault():
raise serializers.ValidationError(
'You cant update other authors book'
)
author = serializers.PrimaryKeyRelatedField(
default=serializers.CurrentUserDefault(),
queryset=models.User.objects.all()
)
and a view with some permissions:
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
class BookViewSet(viewsets.ModelViewSet):
queryset = models.Book.objects.all()
serializer_class = serializers.BookSerializer
permission_classes = [IsAuthenticatedOrReadOnly & IsAuthorOrReadOnly]
So, how can I ensure that a user can get all of the books but can't create or update the books which don't belong to him?
This code may be useful if you want to permit Authenticated users to have SAFE_METHODS requests and only the Authors to have extra permission to PUT and DELETE. Unauthenticated users don't have any permission even GET. Of course you should use an inherited class from generics.RetrieveUpdateDestroyAPIView in view.
class IsAuthorOrIsAuthenticated(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return bool(request.user and request.user.is_authenticated)
return obj.author == request.user
Here the interesting part for me is that an anonymous user doesn't have any permission but the GET OPTIONS PUT and DELETE buttons are available! (However these buttons does not work for him/her!)
I would appreciate it if someone tell me what should I do to remove the buttons for anonymous users.
I will attach my online project here to see the result ...
Try this,
# serializers.py
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['title', 'author']
read_only_fields = ('author',)
# views.py
from rest_framework.permissions import IsAuthenticated
class BookViewSet(viewsets.ModelViewSet):
serializer_class = serializers.BookSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
if self.request.method == 'GET':
return models.Book.objects.all()
else:
return models.Book.objects.filter(author=self.request.user)
def perform_create(self, serializer):
serializer.save(author=self.request.user)
References
1. Specifying read only fields - DRF doc
2. perform_create() method of APIView
Related
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')
I am not very professional in django rest...
I wrote a blog with django rest framework and There is no problem when I want to get all the records related to the Article model or get a specific article, for example
But what I want to do is to send an user id(or an user name) to the view when I click on the user's name.
and as a result display all the records of the Article model related to the request.user and all the records of the Article model related to the user whose name was clicked.
In fact, I want to click on the name of each user, in addition to getting the Articles of that user, the Articles related to the request.user will also be taken
This is what I have done so far...
#models.py
class Article(models.Model):
title = models.CharField(max_length=255)
slug = models.SlugField
author = models.ForeignKey(User , on_delete = models.CASCADE)
content = models.TextField(null = True)
publish = models.DateTimeField(default = timezone.now)
created = models.DateTimeField(auto_now_add = True)
updated = models.DateTimeField(auto_now = True)
status = models.BooleanField(default = False)
def __str__(self):
return self.title
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
pic = models.ImageField(upload_to="img", blank=True, null=True)
def __str__(self):
return self.user.username
#views.py
class ArticleCreate(CreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class ArticleDetail(RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
class UserDetail(RetrieveUpdateDestroyAPIView):
queryset = get_user_model().objects.all()
serializer_class = UserSerializer
class UserProfile(RetrieveUpdateDestroyAPIView):
queryset = Profile.objects.all()
serializer_class = ProfileSerializer
#serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = get_user_model()
fields = "__all__"
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = "__all__"
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
exclude = ['updated' , 'created']
You should directly make several modifications in get_queryset() method by using Q objects so:
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_queryset(self):
user_id = self.kwargs.get('user_id')
if user_id:
articles = Article.objects.filter(Q(author_id=user_id) | Q(author=self.request.user))
return articles
return self.queryset
You'll also need to modify your urls.py file to include the user_id parameter in the URL so:
from django.urls import path
from .views import ArticleList
urlpatterns = [
path('articles/<int:user_id>/', ArticleList.as_view(), name='article_list'),
# ... Other routes.
]
example URL: http://example.com/api/purchases?username=denvercoder9
class ArticleList(ListAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_queryset(self):
username = self.request.query_params.get('username')
if username:
return User.objects.filter(username=username).article_set.all()
user = self.request.user
return Article.objects.filter(author=user)
I have 2 models ( Users and Posts )
class Users(models.Model):
email = models.CharField(max_length=225)
class Posts(models.Model):
user = models.ForeignKey(Users, on_delete=models.CASCADE, default=1)
type = models.TextField()
I want to include the user email when getting all posts.
I have done the following but am only getting the user id.
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
def get_queryset(self):
queryset = Posts.objects.all()
return queryset
How can I achieve to get the user email within the queryset ?
use https://docs.djangoproject.com/en/4.1/ref/models/querysets/#prefetch-related (not strictly needed, but a good habit) to grab it from the database, use post.user.email to grab the value in code. i.e:
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
queryset = Posts.objects.all().prefetch_related("user")
class PostsSerializer(serializers.ModelSerializer):
email = serializers.EmailField(source="user.email")
...
You can annotate the field in the queryset:
from django.db.models import F
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
def get_queryset(self):
queryset = Posts.objects.annotate(user_email=F('user__email'))
return queryset
Use all power of serializers. https://www.django-rest-framework.org/api-guide/serializers/
class PostsViewSet(viewsets.ModelViewSet):
serializer_class = PostsSerializer
queryset = Posts.objects.all().prefetch_related("user")
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('email')
class PostsSerializer(serializers.ModelSerializer):
user = UserSerializer()
class Meta:
model = Post
fields = ('type', 'user')
depth = 1
I am trying to save the user automatically when saving new data. I mean I want to save the user id for the user who is posting a new thing (submitting a form) without asking that user to choose his account from the droplist (the droplist from the User ForeignKey)
Try 1 :
models.py
class Feedback(models.Model):
.
.
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
editable=False,
)
def save(self, *args, **kwargs):
self.author = get_user_model().objects.get(id=2) #############
return super().save(*args, **kwargs)
It's working if I hard-coded the user-id get(id=2). How can I make the id dynamic?
Edit:
Try 2 :
I tried also doing it with the view:
models.py
class Feedback(models.Model):
.
.
author = models.ForeignKey(
get_user_model(),
on_delete=models.CASCADE,
editable=False,
null=True
blank=True
)
# without save()
views.py
class FeedbackForm(ModelForm):
class Meta:
model = Feedback
fields = "__all__"
class FeedbackListView(generics.ListCreateAPIView):
queryset = Feedback.objects.all()
serializer_class = FeedbackSerializer
from_class = FeedbackForm
permission_classes = [FeedbackPermission,] # I also tried AllowAny
def form_valid(self, form, FeedbackForm):
obj = form.save(commit=False) # I also tried self.obj = ...
obj.author = self.request.user # I also tried self.obj.author = ...
obj.save() # I also tried self.obj.save()
return super().form_valid(form)
But on the second try (with forms), the author field remains null.
ListCreateAPIView don't have method form_valid, look at this
In your class FeedbackListView you need to do something like this:
class FeedbackListView(generics.ListCreateAPIView):
queryset = Feedback.objects.all()
serializer_class = FeedbackSerializer
def perform_create(self, serializer):
# The request user is set as author automatically.
serializer.save(author=self.request.user)
Error message
Cannot assign "<SimpleLazyObject: <User: dip7777>>": "ImageTarget.uploaderclient" must be a "UploaderClient" instance.
I'm working on Python 3.4.3 and Django 1.8 and using Django Rest Framework 3.
I'm using the DRF browsable API for testing.
What I'm trying to do is to attach the currently logged in user to the file that he uploads.
Whenever I try to do a POST and upload a file with a logged in user, it throws up the above error.
I have a couple of UploaderClient created and the user dip7777 is one such user tied to a UploaderClient. I have logged in using that user.
What I am able to accomplish with the current code is edit the currently uploaded file ie. upload a new file in place of the current file using the ImageTargetDetail view.
(I had uploaded a couple of files and tied them to UploaderClients using the admin interface)
What I want to do is upload a new file using the ImageTargetList view and not replace a preexisting one.
But the error shows up and I do not understand how to assign the current(logged in) uploaderclient instance to the ImageTarget's uploaderclient
I have two models 1)UploaderClient
class UploaderClient(models.Model):
user = models.OneToOneField(User)
company_name = models.CharField(_('company name'), max_length=100)
class Meta:
verbose_name = _('uploaderclient')
verbose_name_plural = _('uploaderclients')
def __str__(self):
return 'UploaderClient: {}'.format(self.user.username)
and 2) ImageTarget
class ImageTarget(models.Model):
uploaderclient = models.ForeignKey('authenticateclients.UploaderClient', related_name='imagetargets')
targetName = models.CharField(max_length=50, unique=True)
imageWidth = models.IntegerField()
targetFile = models.FileField(upload_to=get_upload_imagetargetfile_name)
in two different apps authenticateclients and clientupload
My serializers are:
class UploaderClientSerializer(serializers.HyperlinkedModelSerializer):
username = serializers.CharField(source='user.username')
email = serializers.CharField(source='user.email', required=False, allow_null=True)
password = serializers.CharField(source='user.password', write_only=True, required=False)
date_joined = serializers.DateTimeField(source='user.date_joined', read_only=True)
last_login = serializers.DateTimeField(source='user.last_login', read_only=True)
company_name = serializers.CharField()
imagetargets = serializers.HyperlinkedRelatedField(many=True, view_name='imagetargetsdetail', read_only=True)
url = serializers.HyperlinkedIdentityField(view_name='uploaderclientsdetail')
class Meta:
model = UploaderClient
fields = ('url', 'email', 'username', 'company_name', 'date_joined', 'last_login', 'password','imagetargets',)
read_only_fields = ('user.date_joined', 'user.last_login',)
and
class ImageTargetSerializer(serializers.HyperlinkedModelSerializer):
uploaderclient = UploaderClientSerializer(read_only=True,required=False)
targetFile = serializers.FileField(label='TargetFile')
url = serializers.HyperlinkedIdentityField(view_name='imagetargetsdetail')
class Meta:
model = ImageTarget
fields = ('url','uploaderclient','targetName','imageWidth','targetFile',)
def get_validation_exclusions(self):
exclusions = super(ImageTargetSerializer, self).get_validation_exclusions()
return exclusions + ['uploaderclient']
My views are
class UploaderClientList(generics.ListAPIView):
queryset = UploaderClient.objects.all()
serializer_class = UploaderClientSerializer
class UploaderClientDetail(generics.RetrieveAPIView):
queryset = UploaderClient.objects.all()
serializer_class = UploaderClientSerializer
and
class ImageTargetList(generics.ListCreateAPIView):
queryset = ImageTarget.objects.all()
serializer_class = ImageTargetSerializer
def perform_create(self, serializer):
instance = serializer.save(uploaderclient=self.request.user)
return super(ImageTargetList, self).perform_create(serializer)
permission_classes = (permissions.IsAuthenticated,IsOwnerOrNothing,)
class ImageTargetDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = ImageTarget.objects.all()
serializer_class = ImageTargetSerializer
permission_classes = (permissions.IsAuthenticated,IsOwnerOrNothing,)
#api_view(('GET',))
def api_root(request, format=None):
return Response({
'uploaderclients': reverse('uploaderclients', request=request, format=format),
'imagetargets': reverse('imagetargets', request=request, format=format),
})
In ImageTargetList.perform_create:
instance = serializer.save(uploaderclient=self.request.user)
However uploaderclient should not be a user.