I want to use HyperlinkedIdentityField in my serializer, that's why I need view name. How can I get the view name if I need model_name-list for example?
Here is my serializer:
class CategorySerializer(serializers.HyperlinkedModelSerializer):
posts = serializers.HyperlinkedIdentityField(view_name='', format=None)
class Meta:
model = Category
fields = ('url', 'category_name', 'id', 'parent', 'posts')
This is my views.py:
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
And here is my urls.py:
router = routers.DefaultRouter()
router.register(r'categories', views.CategoryViewSet)
router.register(r'posts', views.PostViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
]
My models:
class Post(models.Model):
post_name = models.CharField(max_length=50)
post_text = models.CharField(max_length=1000)
pub_date = models.DateTimeField('date published')
categories = models.ManyToManyField('Category', related_name='posts')
def __str__(self):
return self.post_name
class Category(models.Model):
category_name = models.CharField(max_length=50)
parent = models.ForeignKey('self', null=True, blank=True, on_delete = models.CASCADE)
def __str__(self):
return self.category_name
I need a view_name for post-list, in order to use HyperlinkedIdentityField
As the Documentation for routers in DRF (http://www.django-rest-framework.org/api-guide/routers/) says, if the base_name argument is not passed when registering the ViewSet then the view_name is auto generated depending on the queryset attribute of the ViewSet registered while defining the router.
In your case the view_name should be 'post-list' for the list view and 'post-detail' for the detail view of the viewset unless you want to change/override it by passing it as the third parameter to router.register().
Also note that the queryset attribute should always be present if you are not initializing the base_name for the ViewSet which otherwise will throw an error.
Related
When the book owner is authenticated it's ok, but if the book owner is not authenticated this error appears ValueError: Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x000002677F18EE00>": "Book.owner" must be an instance of " CustomUser".. I think this problem is caused by HyperlinkedModelSerializer. The owner of the book is specified in the perform_create() method, which is taken from self.request.user. I want authenticated users or non-authenticated users to be able to create books.
models.py
class Book(models.Model):
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
blank=True,
null=True, # allow Anonymous user
on_delete=models.CASCADE,
related_name="books",
)
title = models.TextField()
serializers.py
class BookSerializer(serializers.HyperlinkedModelSerializer):
owner_username = serializers.ReadOnlyField(source="owner.username")
class Meta:
model = Book
fields = (
"url",
"owner",
"owner_username",
"title",
)
read_only_fields = ("owner",)
views.py
class BookViewSet(viewsets.ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
I need your help to fix this problem.
AnonymousUser is a different model, instead of using that, you can check request.user.is_authenticated and if it's False, set owner=None
Here's an example:
class BookViewSet(viewsets.ModelViewSet):
serializer_class = BookSerializer
queryset = Book.objects.all()
def perform_create(self, serializer):
user = self.request.user if self.request.user.is_authenticated else None
serializer.save(owner=user)
New to Django, this is a simple blog post app. How do i include the name of a post's author in the url?
urlpatterns = [
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
]
post model
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank="true")
content = models.CharField(max_length=400)
views.py
class PostDetailView(DetailView):
model = Post
You can add an extra parameter:
urlpatterns = [
path('post/<str:author>/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
]
in the view, you can filter on the author such that if the author's username is incorrect, it will not present any content:
class PostDetailView(DetailView):
model = Post
def get_queryset(self, *args, **kwargs):
return super().get_queryset(*args, **kwargs).filter(
author__username=self.kwargs['author']
)
We can generate a URL for a Post object with the .get_absolute_url() method [Django-doc] which includes the username of the author:
from django.urls import reverse
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=100, blank="true")
content = models.CharField(max_length=400)
def get_absolute_url(self):
return reverse('post-detail', kwargs={'id': self.pk, 'author': self.author.username})
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Im trying to create an endpoint for a post and its comments in the following format:
/posts (view all posts)
/posts/{id} (view post by id)
/posts/{id}/comments (view comments for a post)
The first 2 work, but for the last one I have /comments rather than the url i would like and I am not sure how to go about that, I think I need to change my models for it.
My current models (its using default Django User):
class Post(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
class PostComment(models.Model):
author = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
comment = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.post.title
And urls:
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'posts', views.PostViewSet)
router.register(r'comments', views.PostCommentViewSet)
Edit: this are the viewsets
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all().order_by('id')
serializer_class = UserSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().order_by('created_at')
serializer_class = PostSerializer
class PostCommentViewSet(viewsets.ModelViewSet):
queryset = PostComment.objects.all().order_by('created_at')
serializer_class = PostCommentSerializer
You can achieve this by writing the custom viewset actions--(drf doc),
from rest_framework.decorators import action
from rest_framework.response import Response
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all().order_by('created_at')
serializer_class = PostSerializer
#action(detail=True, url_path='comments', url_name='post-comments')
def comments(self, request, *args, **kwargs):
queryset = PostComment.objects.filter(post=kwargs['pk'])
serializer = PostCommentSerializer(queryset, many=True, context= {'request':request, 'view':self})
return Response(data=serializer.data)
Your view should be something similar to this -
class PostCommentViewSet(viewsets.ModelViewSet):
queryset = PostComment.objects.all().order_by('created_at')
serializer_class = PostCommentSerializer
#action(detail=True)
def comments(self, request, id=None):
....
You can refer to DRF documentation for more detail here - https://www.django-rest-framework.org/api-guide/routers/#routing-for-extra-actions
If you want to use the router, then this is probably achievable by implementing a custom router, like in this example: https://www.django-rest-framework.org/api-guide/routers/#example
I think you forgot to register the viewset route parameters with the action decorator
https://www.django-rest-framework.org/api-guide/viewsets/#marking-extra-actions-for-routing
it should work if you had in your viewset
from rest_framework.decorators import action
#actions(detail=True)
def comments(self, request, pk):
# things to do here
So I'm trying to achieve the general "Like" functionality in a social media website using Django and REST Framework, and a frontend in React.
Using a Post model to save all the posts, and I have a Many-to-Many field for storing the likes and created a through model as follows:
class PostLike(models.Model):
user = models.ForeignKey(AppUser, on_delete=models.CASCADE)
post = models.ForeignKey("Post", on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
class Post(models.Model):
user = models.ForeignKey(AppUser, on_delete=models.CASCADE)
caption = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
edited_at = models.DateTimeField(auto_now=True)
likes = models.ManyToManyField(
AppUser, related_name="post_user", blank=True, through=PostLike
)
(AppUser is a custom auth model used)
Similarly, I have created serializers and viewsets for the above models:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = "__all__"
class PostLikeSerializer(serializers.ModelSerializer):
class Meta:
model = PostLike
fields = "__all__"
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostLikeViewSet(viewsets.ModelViewSet):
queryset = PostLike.objects.all()
serializer_class = PostLikeSerializer
My question is, how do I "like" or remove an existing "like" from a post using API calls?
One method I know is to just make a POST request to the PostLike endpoint using the user PK and the post PK to create a PostLike instance, but I don't know a way to "remove" a like using the same method.
Please help!
you can use APIView instead of ViewSet like this:
from rest_framework import views
class PostLikeApiView(views.APIView):
serializer = PostLikeSerializer(data=request.data)
if serializer.is_valid():
user = serializer.data['user']
post = serializer.data['post']
post_like_obj = PostLike.objects.filter(user=user, post=post)
if post_like_obj.exists():
post_like_obj.delete()
result = 'unliked'
else:
PostLike.objects.create(user=user, post=post)
result = 'liked'
return Response(
{
'result': result,
},
status=status.HTTP_200_OK
)
else:
return Response(
serializer.errors,
status=status.HTTP_400_BAD_REQUEST
)
I have the following model class:
# Song Model
class Song(models.Model):
title = models.CharField(max_length=200)
artist = models.CharField(max_length=200)
content = models.TextField()
user = models.ForeignKey('auth.User', related_name='songs')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self): # __unicode__ on Python 2
return self.title + ' ' + self.artist
class Meta:
ordering = ('title',)
My serializers
class SongSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
fields = ('id', 'title', 'artist', 'content')
model = Song
class UserSerializer(serializers.ModelSerializer):
songs = serializers.HyperlinkedRelatedField(
many=True, read_only=True,
view_name='songs'
)
class Meta:
model = User
fields = '__all__'
and my views
class SongViewSet(viewsets.ModelViewSet):
queryset = Song.objects.all()
serializer_class = SongSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
I am trying to get the list of songs but I keep getting this error
ImproperlyConfigured: Could not resolve URL for hyperlinked relationship using view name "songs"
This has been working with PrimaryKeyRelatedField
but not as it is now.
routes file for reference:
router = DefaultRouter()
router.register(r'songs', views.SongViewSet)
router.register(r'users', views.UserViewSet)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include(router.urls)),
]
Well, I was able to make it work with
view_name='song-detail'
If anyone wants to explain the why of how it works, please feel free
I'm total newbie in Django but I've faced with the same issue. I suppose that because of router prefix param is not the view name.
Documentation for the router says:
The example above would generate the following URL patterns:
URL pattern: ^users/$ Name: 'user-list'
URL pattern: ^users/{pk}/$ Name: 'user-detail'
URL pattern: ^accounts/$ Name: 'account-list'
URL pattern: ^accounts/{pk}/$ Name: 'account-detail'
Documentation for the serializer says:
view_name - The view name that should be used as the target of the relationship. If you're using the standard router classes this will be a string with the format
<modelname>-detail. required.
If you are using DefaultRouter
you can try change basename='song'
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register('song', views.SongViewset, basename='song')