When I am creating an blog post I also want to automatically save the current user without selecting the user manually as a blog author.
here is my code:
models.py:
class Blog(models.Model):
author = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, blank=True, null=True)
blog_title = models.CharField(max_length=200, unique=True)
serializers.py
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = Blog
views.py
class BlogViewSet(viewsets.ModelViewSet):
queryset = Blog.objects.all().order_by('-id')
serializer_class = BlogSerializer
pagination_class = BlogPagination
lookup_field = 'blog_slug'
def get_permissions(self):
if self.action == 'retrieve':
permission_classes = [IsOwnerOrReadOnly]
elif self.action == 'list':
permission_classes = [IsOwnerOrReadOnly]
else:
permission_classes = [IsOwnerOrReadOnly & IsAuthorGroup]
return [permission() for permission in permission_classes]
You can modify your serializer like below. It picks up the user from the request context and creates the blog.
class BlogSerializer(serializers.ModelSerializer):
class Meta:
model = Blog
fields = "__all__"
read_only_fields = ["author"]
def create(self, validated_data):
user = self.context["request"].user
blog = Blog.objects.create(**validated_data, author=user)
return blog
Related
I have the following django rest framework serializer and view for a model Post, and the models defined for the app are as following.
Now I wanted to test the API, so tried to "create" a new post from the API page, but then I got an error IntegrityError at /api/posts/ NOT NULL constraint failed: appname_post.user_id.
So then I tried to debug and checked its request.data value, it says it only has title and content but not user_id, as I could expect from the error above.
But now I have no idea how to tell the API automatically relate the user_id field to request.user.id. Isn't not enough to just add the user_id = serializers.ReadOnlyField(source='user.id') line? I could do "retrieve", "update", "patch" etc but just cannot "post" (create).
Thanks for your help.
serializer and view
class DevPerm(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return True
def has_permission(self, request, view):
return True
class PostSerializer(serializers.HyperlinkedModelSerializer):
user_id = serializers.ReadOnlyField(source='user.id')
class Meta:
model = Post
fields = ('url',
'id',
'title',
'content',
'created_at',
'voters',
'comments',
'user_id',
)
read_only_fields = (
'id',
'created_at',
"voters",
"comments",
# "user_id",
)
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated, DevPerm,]
models
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts', default="")
created_at = models.DateTimeField(auto_now_add=True)
title = models.TextField(blank=True)
content = models.TextField(blank=True)
voters = models.ManyToManyField(User, related_name='votes', default="")
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments', default="")
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments', default="")
created_at = models.DateTimeField(auto_now_add=True)
content = models.TextField(blank=True)
You can override perform_create method inside you ModelViewSet.
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [permissions.IsAuthenticated, DevPerm,]
def perform_create(self, serializer):
serializer.save(user=self.request.user)
When User tries to add an Announcement, should i pass all the informations of the user in the form ?
i'm using token authentification.
So for adding an Announcement the user must be authenticated.
Models.py
class User(AbstractUser):
username = None
email = models.EmailField(max_length=100, verbose_name='email',
unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
class Announcement(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
photo = models.ManyToManyField(Photo, blank=True)
class Photo(models.Model):
name = models.CharField(max_length=100)
content_type = models.CharField(max_length=100)
path = models.CharField(max_length=100)
class Parameter(models.Model):
name = models.CharField(max_length=100)
value = models.FloatField(blank=True, null=True)
announcement = models.ForeignKey(
Announcement,related_name='parameters', on_delete=models.CASCADE)
Serializers.py
class AnnouncementSerializer(serializers.ModelSerializer):
author = UserSerializer(required=True)
parameters = ParameterSerializer(many=True,
required=False)
photo = PhotoSerializer(many=True,
required=False)
class Meta:
model = Announcement
fields = ['id', 'name', 'author',
'parameters', 'photo']
class UserSerializer(serializers.ModelSerializer):
photo = PhotoSerializer()
class Meta:
model = User
fields = ['id', 'email','photo', ]
class ParameterSerializer(serializers.ModelSerializer):
class Meta:
model = Parameter
fields = '__all__'
class PhotoSerializer(serializers.ModelSerializer):
class Meta:
model = Photo
fields = '__all__'
Views.py
class AnnouncementCreate(CreateAPIView):
permission_classes = [IsAuthenticated]
queryset = models.Announcement.objects.all()
serializer_class = AnnouncementSerializer
When trying the browsable API. to create a new announcement i have to enter all the informations of the user. But if the user is already authenticated. is there any solution to create the announcement for only this user and show it to the other users ?
If you don't want to create a User when creating an Announcement, omit the author field from your AnnouncementSerializer, then pass the current user when saving serializer object:
serializer.py
class AnnouncementSerializer(serializers.ModelSerializer):
parameters = ParameterSerializer(many=True, required=False)
photo = PhotoSerializer(many=True, required=False)
class Meta:
model = Announcement
fields = ['id', 'name', 'parameters', 'photo']
views.py
class AnnouncementCreate(CreateAPIView):
permission_classes = [IsAuthenticated]
queryset = models.Announcement.objects.all()
serializer_class = AnnouncementSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
The below is my code about model Domain's Update:
serializer.py:
class DomainUpdateSerializer(serializers.ModelSerializer):
class Meta:
model = Domain
fields = "__all__"
models.py:
class Domain(models.Model):
domain_name = models.CharField(max_length=512, help_text='domain. eg.example.com')
cname = models.ForeignKey(
unique=True,
to=CNAMEModel,
on_delete=models.DO_NOTHING,
related_name="domains",
help_text="CNAME")
ssl_cert = models.TextField(max_length=40960, help_text="SSL cert + ca-bundle")
ssl_key = models.TextField(max_length=40960, help_text="SSL key")
ctime = models.DateTimeField(auto_now_add=True)
uptime = models.DateTimeField(auto_now=True)
def __str__(self):
return self.domain_name
def __unicode__(self):
return self.domain_name
class Meta:
verbose_name = "domain"
verbose_name_plural = "domain"
ordering = ['ctime']
class CNAMEModel(models.Model):
name = models.CharField(max_length=64, unique=True, help_text=". eg:gat.demo.com")
desc = models.CharField(max_length=5120, null=True, blank=True, help_text="desc")
desc_en = models.CharField(max_length=5120, null=True, blank=True")
user = models.OneToOneField(unique=True, to=AuthUser, on_delete=models.DO_NOTHING, help_text="belong user")
is_active = models.BooleanField(default=True)
ctime = models.DateTimeField(auto_now_add=True)
uptime = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def __unicode__(self):
return self.name
class Meta:
verbose_name = "CNAME"
verbose_name_plural = "CNAME"
ordering = ['ctime']
views.py:
class DomainUpdateAPIView(UpdateAPIView):
serializer_class = DomainUpdateSerializer
permission_classes = [IsAuthenticated, IsAdminUser]
queryset = Domain.objects.all()
You see Domain belong to CNAME, CNAME belong to a user.
I have a question, how can I make a permission for checking the Domain only can be update by the belonged users or AdminUser(IsAdminUser have solved)?
Or use other way rather than permission.
You can have a additional custom permission:
from rest_framework import permissions
from rest_framework.exceptions import PermissionDenied
class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow creator of an object to edit it.
"""
def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True
# Write permissions are only allowed to the creator of the movie
return obj.cname.user == request.user
And in the views IsOwnerOrReadOnly can be included:
from .permissions import IsOwnerOrReadOnly
class DomainUpdateAPIView(UpdateAPIView):
serializer_class = DomainUpdateSerializer
permission_classes = [IsAuthenticated, IsAdminUser, IsOwnerOrReadOnly]
queryset = Domain.objects.all()
I have a pair of parent/children relation models like:
class Post(models.Model):
title = models.TextField(null=True)
content = models.TextField(null=True)
author = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
class Comment(models.Model):
content = models.TextField(null=True)
created_time = models.DateTimeField(null=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
and the serializers are like:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = '__all__'
and finally views:
class PostView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class CommentView(viewsets.ModelViewSet):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
Now I want to created an API that returns a list of Posts, in which each Post will contain two additional fields, one be all_comments, and the other will be latest_comment. I understand this could be easily done in SQL using JOINs. I am new to Django. I wonder if there's any easy way to do it in Django. Thanks.
Hope this config works for you :)
class CommentPostSerializer(serializers.ModelSerializer): # New Serializer class
class Meta:
model = Comment
exclude = ('post',)
class PostSerializer(serializers.ModelSerializer):
all_comments = CommentPostSerializer(read_only=True, many=True, source='comment_set')
latest_comment = serializers.SerializerMethodField()
def get_latest_comment(self, post):
latest_comment = post.comment_set.last()
return CommentPostSerializer(latest_comment).data
class Meta:
model = Post
fields = '__all__'
models.py
class Nugget(TimeStampedModel):
added_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name='added_by', blank=True, null=True)
serializers.py
class NuggetSerializer(TaggitSerializer, serializers.ModelSerializer):
added_by = serializers.CreateOnlyDefault(default=serializers.CurrentUserDefault())
views.py
class NuggetList(generics.ListCreateAPIView):
queryset = Nugget.objects.all()
serializer_class = NuggetSerializer
def perform_create(self, serializer):
serializer.save(added_by=self.request.user)
What I'm trying to achieve:
added_by should:
Be set on create of a Nugget
Default to the user who created the Nugget, with no way to override this default
Be included and shown when a Nugget is retrieved
Not be shown as an option for create/POST in the browsable API
Not be editable after create
Changed added_by in serializers.py (wasn't using a field, and set to read_only) and .save() in views.py to stop overriding the default.
CurrentUserDefault() requires request within the context dict. In this case generics.ListCreateAPIView already does that.
models.py
class Nugget(TimeStampedModel):
added_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, related_name='added_by', blank=True, null=True)
serializers.py
class NuggetSerializer(TaggitSerializer, serializers.ModelSerializer):
added_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
views.py
class NuggetList(generics.ListCreateAPIView):
queryset = Nugget.objects.all()
serializer_class = NuggetSerializer
def perform_create(self, serializer):
serializer.save()
I did it the following way (no need to make the field nullable):
models.py
class Nugget(models.Model):
added_by = models.ForeignKey(to=User, related_name='added_by', on_delete=models.DO_NOTHING)
serializers.py
class NuggetSerializer(serializers.ModelSerializer):
added_by = serializers.StringRelatedField(default=serializers.CurrentUserDefault(), read_only=True)
class Meta:
model = Nugget
fields = ['added_by']
views.py
class NuggetList(viewsets.ModelViewSet):
queryset = Nugget.objects.all()
serializer_class = NuggetSerializer
def perform_create(self, serializer):
request = serializer.context["request"]
serializer.save(added_by=request.user)
I have to update views.py to make it work:
class NuggetList(generics.ListCreateAPIView):
queryset = Nugget.objects.all()
serializer_class = NuggetSerializer
permission_classes = (IsAuthenticated,)
def perform_create(self, serializer):
req = serializer.context['request']
serializer.save(added_by=req.user)