I need to get this endpoint /comments/int:post_id/
I can GET and POST comments and posts but I need to show all comments to specific post. I donĀ“t know how to connect it. My code look like
comment urls
urlpatterns = [
path('', views.CommentsView.as_view()),
path('<int:post_id>/', views.CreateCommentsView.as_view()),
]
comment view.py
# I get all comments/
class CommentsView(ListCreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
# Comments to specific post
class CreateCommentsView(ListCreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
lookup_url_kwarg = 'post_id'
def perform_create(self,serializer):
post = self.kwargs.get('post_id')
post =set.get_queryset().filter(id = 'post_id')
post.comments.add(comment)
post = Post.objects.filter(id=self.kwargs.get('post_id'))
serializer.save(user=self.request.user, post=post)
comment serializer
from rest_framework import serializers
from .models import Comment
from django.contrib.auth import get_user_model
User = get_user_model()
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['id', 'user', 'post', 'content', 'created']
class UserSimpleSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username']
class CommentSimpleSerializer(serializers.ModelSerializer):
user = UserSimpleSerializer()
class Meta:
model = Comment
fields = ['user', 'content', 'created']
post.view.py
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
class LikePost(generics.UpdateAPIView):
permission_classes = [IsNotPostUser]
queryset = Post.objects.all()
serializer_class = PostSerializer
you can pass the post_id in url parameters for /comments/ endpoint and call HTTP GET method. Then in CommentsView you need to override the get_queryset method. Your implementation will be like
class CommentsView(ListCreateAPIView):
serializer_class = CommentSerializer
def get_queryset(self):
query = self.request.query_params.get('post_id', None)
if query is not None:
queryset = Comment.objects.filter(post_id=post_id)
else:
queryset = Comment.objects.all()
return queryset
This /comments/ endpoint will return all the comments and this one /comments/?post_id=1 will return comments only related to specified post.
Related
I am using django rest framework. I want to include the url of an action defined in a view in its serializer.
My serializers.py:
from rest_framework import serializers
class CommentSerializer(serializers.ModelSerializer):
"""Serializer for comments."""
class Meta:
model = Comment
fields = ["id", "item", "author", "content", "date_commented", "parent"]
class ItemDetailSerializer(serializers.ModelSerializer):
"""Serializer for items (for retrieving/detail purpose)."""
category = CategorySerializer(many=True, read_only=True)
media = MediaSerializer(many=True, read_only=True)
brand = BrandSerializer(many=False, read_only=True)
specifications = serializers.SerializerMethodField(source="get_specifications")
comments = ??????????????????????????????????????????????????
class Meta:
model = Item
fields = [
"id",
"slug",
"name",
"description",
"brand",
"show_price",
"location",
"specifications",
"is_visible",
"is_blocked",
"created_at",
"updated_at",
"seller",
"category",
"media",
"comments",
"users_wishlist",
"reported_by",
]
read_only = True
editable = False
lookup_field = "slug"
def get_specifications(self, obj):
return ItemSpecificationSerializer(obj.item_specification.all(), many=True).data
My views.py:
from rest_framework import viewsets, mixins, status
from ramrobazar.inventory.models import Item, Comment
from ramrobazar.drf.serializers ItemSerializer, ItemDetailSerializer, CommentSerializer
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.filters import SearchFilter
from django_filters.rest_framework import DjangoFilterBackend
class ItemList(viewsets.GenericViewSet, mixins.ListModelMixin):
"""View for listing and retrieving all items for sale."""
queryset = Item.objects.all()
serializer_class = ItemSerializer
serializer_action_classes = {
"retrieve": ItemDetailSerializer,
}
permission_classes = [IsAuthenticatedOrReadOnly]
lookup_field = "slug"
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = [
"category__slug",
"brand__name",
]
search_fields = ["name", "description", "category__name", "brand__name", "location"]
def get_serializer_class(self):
try:
return self.serializer_action_classes[self.action]
except:
return self.serializer_class
def retrieve(self, request, slug=None):
item = self.get_object()
serializer = self.get_serializer(item)
return Response(serializer.data)
#action(detail=True, methods=['GET'])
def comments(self, request, slug=None):
item = Item.objects.get(slug=slug)
queryset = Comment.objects.filter(item=item)
serializer = CommentSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have an action named comments in the ItemList view which gives all the comments of a specific item. I can get the details of the item from the url /api/items/<slug>. I can get all the comments of the item from the url api/items/<slug>/comments. I want to include a comments field in the ItemDetailSerializer serializer which is a link to api/items/<slug>/commments. How can I accomplish this?
You can do this via SerializerMethodField and reverse:
class ItemDetailSerializer(serializers.ModelSerializer):
...
comments = serializers.SerializerMethodField(source="get_comments")
...
def get_comments(self, obj):
return reverse(YOUR_URL_NAME, kwargs={'slug': obj.slug})
try this:
class ItemDetailSerializer(serializers.ModelSerializer):
...
comments = serializers.CharField(max_length=100, required=False)
def create(self, validated_data):
validated_data['comments'] = self.context['request'].build_absolute_uri() + 'comments'
return super(ContentGalleryListSerializer, self).create(validated_data)
You can use the HyperlinkedIdentityField for this:
comments = serializers.HyperlinkedIdentityField(
view_name='item-comments',
lookup_field='slug'
)
This will then render a comments field that contains a URL to the item-comments view, with the slug as lookup parameter.
You will need to register the view with the name item-comments, for example with:
urlpatterns = [
path('items/<slug>/comments/', ItemList.as_view({'get': 'comments'}), name='item-comments')
]
Note that normally nested routes are discouraged, and you'd put the comments in a separate view. But the above should work.
I've got a time obj serialized to DRF.
model:
class Time(TimeStampedModel):
user = models.ForeignKey(
'users.User', on_delete=models.CASCADE, related_name='time_logs')
amount = models.DurationField()
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
serializer:
class TimeSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Time
fields = ('__all__')
extra_kwargs = {'date_created': {'read_only': True}, 'date_updated': {'read_only': True}}
viewset:
class UserTimeViewSet(viewsets.ModelViewSet):
serializer_class = TimeSerializer
queryset = Time.objects.all()
def get_queryset(self):
return Time.objects.filter(user=self.request.user)
urls
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users/time', UserTimeViewSet)
this gives me an endpoint: /users/time/{id} where {id} is the Time obj id. when I tried to modify the endpoint to the same url route but instead pass it a user id as the arg (to get only that user's time) by overwriting lookup_field on the serializer and ViewSet, I'm either getting response errs or {"detail":"Not found."}
class UserTimeViewSet(viewsets.ModelViewSet):
serializer_class = TimeSerializer
queryset = Time.objects.all().select_related('user')
lookup_field = 'user'
also tried:
class UserTimeViewSet(generics.ListAPIView):
serializer_class = TimeSerializer
def get_queryset(self):
user = self.kwargs['user']
return Time.objects.filter(user=user)
how can I GET Time with a relationship to a user by user's id?
If I'm understood correctly, this view class will do the magic
class UserTimeViewSet(viewsets.ModelViewSet):
serializer_class = TimeSerializer
queryset = Time.objects.all()
lookup_field = 'user' # add "lookup_field" attribute
# remove "get_queryset()" method
def get_queryset(self):
return Time.objects.filter(user=self.request.user)
JPG's other answer is a valid solution if the user only has one Time object. however, multiple objs will return a MultipleObjectsReturned get() returned more than one Time err
a solution for multiple objs was to convert the viewset into a ListAPIView:
views:
class UserTimeViewSet(generics.ListAPIView):
serializer_class = TimeSerializer
queryset = Time.objects.all()
def get_queryset(self):
return Time.objects.filter(user=self.kwargs['user'])
urls:
url(r'users/time/(?P<user>\d+)/$', UserTimeViewSet.as_view(), name='user_time'),
I have a django application. I have one part of my app that is not working as I was expecting. As of now I am using the django rest framework view sets. What I want is two basically have 2 different list views in the same viewset. 1 that returns all of the accounts in the accountviewset and another list view that returns all of the accounts associated with a user based on the user foreignkey in the account model.
The code below returns all the acccounts associated with a specific person, but I am not able to have a list view that returns all of the accounts in the database.
class AccountViewSet(viewsets.ModelViewSet):
serializer_class = AccountSerializer
queryset = Account.objects.all()
lookup_field = 'user__username'
def list(self, request):
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_object(self):
return self.queryset.get(user__username=self.kwargs.get('username'))
**UPDATE**
class AccountViewSet(viewsets.ModelViewSet):
queryset = Account.objects.all();
serializer_class = AccountSerializer
filter_backends = (filters.DjangoFilterBackend,)
filterset_fields = ('user', 'name', '_class')
lookup_field = 'user__username'
# def get_queryset(self):
# return self.queryset.filter(user__username=self.kwargs.get('username'))
it works when there is one account associated with one user.
does not work with more than 2 accounts with error:
MultipleObjectsReturned at /api/users/accounts/omarjandali/
get() returned more than one Account -- it returned 2!
other attempts doesnt return any accounts with a specific user or no user in the endpoint.
The biggest problem is that it returns an id of the user not user username.
current code:
serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'is_staff')
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('user', 'gender', 'phone', 'ip_address', 'dob_month', 'dob_day',
'dob_year', 'address_street', 'address_city', 'address_state',
'address_postal', 'address_country', 'profile_pic', 'role')
class FriendSerializer(serializers.ModelSerializer):
class Meta:
model = Friend
fields = ('requested', 'friended', 'status', 'blocked')
class AccountSerializer(serializers.ModelSerializer):
user = UserSerializer(
read_only = True,
)
class Meta:
model = Account
fields = ('user', '_id', 'name', 'balance', 'currency', 'bank_name',
'routing', '_class', 'type', 'active', 'main', 'synapse')
Views.py:
class UserViewSet(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
lookup_field = 'username'
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = Profile.objects.all()
lookup_field = 'user__username'
class FriendViewSet(viewsets.ModelViewSet):
serializer_class = FriendSerializer
querset = Friend.objects.all()
lookup_field = 'user__username'
class AccountViewSet(viewsets.ModelViewSet):
queryset = Account.objects.all();
serializer_class = AccountSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('user',)
urls:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
router.register(r'profiles', ProfileViewSet, basename='profile')
router.register(r'friends', FriendViewSet, basename='friend')
router.register(r'accounts', AccountViewSet, basename='account')
urlpatterns = router.urls
First, you have a lot of issues with your code. If you need to return a list, then you should filter in the get_object method. it is meant to only return one single instance, so do that in the get_queryset method. Secondly, you have the get_object() inside your list method which is wrong. Thirdly, assigning the serializer class in the list method doesn't make sense. It is assigned before that method is even called.
Now back to your question, 'list' is a viewset action and you can't implement it twice. Instead you can add an extra action on the list endpoint.
To do this, just add a method in the action method in the viewset.
In your particular case I would do this: I will introduce an instance variable, qs_type which is used to control the type of result returned by the get_queryset() method.
Something like this:
def __init__(self, *args, **kwargs):
self.qs_type = 'user_only'
super().__init__(*args, **kwargs)
def get_queryset(self):
if self.qs_type == 'user_only':
qs = Account.objects.filter(username=self.kwargs.get('username'))
else:
qs = Account.objects.all()
return qs)
#action(detail=False, methods=['get'])
def all(self, request, *args, **kwargs):
self.qs_type = 'all'
return self.list(request, *args, **kwargs)
This assumes that by default, username filtered version is returned while the all accounts will be returned in the new action. You can make qs_type a boolean if you are sure you won't need more similar actions but I made it a string just in case. I don't know how your url is configured so I don't really know how the username is retrieved from the url kwargs, but this code should help you solve your problem
I want to write a viewset in Django Rest Framework which allow user upload a list of new Images with these Order number together. But I don't know what field to use in DRF.
My serializer:
class ContentSerializer(ModelSerializer):
fileUpload = FileField(max_length=100000, allow_empty_file=False, use_url=False)
order_num = IntegerField(required=False)
class Meta:
model = Article
fields = [
'fileUpload',
'order_num',
]
class ArticleSerializer(ModelSerializer):
file_uploads = ListField(child=ContentSerializer(),write_only=True)
class Meta:
model = Article
fields = [
'file_uploads',
]
My Viewset:
class FeedEditAPIView(ModelViewSet):
'''
Use Form Data to PATCH
'''
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'id'
def edit_article(self, request, id):
print request.POST.getlist('file_uploads') // This return [u'{"file":{},"order_num":0}']
When I print request POST file_uploads, it return [u'{"file":{},"order_num":0}']. But I want to return [u'{"file":<files_object>,"order_num":0}'] instead of
Use multipartparser, which supports file uploads and request.data
class FeedEditAPIView(ModelViewSet):
'''
Use Form Data to PATCH
'''
parser_classes = (MultiPartParser,)
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'id'
I'm overriding get_queryset in a ModelViewSet to support "me" as filter and multiple pk search:
class UserViewSet(viewsets.ModelViewSet):
queryset = UserProfile.objects.all()
serializer_class = UserProfileSerializer
def get_queryset(self):
qs = UserProfile.objects.all()
if 'pk' in self.kwargs:
pk_user = self.kwargs['pk']
if ',' in pk_user: # Multiquery
pk_users = pk_user.split(',')
qs = qs.filter(pk__in=pk_users)
elif pk_user == "me":
qs = qs.filter(pk=self.request.user)
else:
qs = qs.filter(pk=pk_user)
# By default return all the items
return qs
I'm using the following serializer:
class UserProfileSerializer(serializers.ModelSerializer):
avatar_thumbnail_small = serializers.ImageField(read_only=True)
avatar_thumbnail_medium = serializers.ImageField(read_only=True)
id = serializers.CharField(source='user.id')
username = serializers.CharField(source='user.username')
firstname = serializers.CharField(source='user.first_name')
lastname = serializers.CharField(source='user.last_name')
class Meta:
model = UserProfile
fields = ('id', 'username', 'firstname', 'lastname', 'karma', 'avatar_thumbnail_small', 'avatar_thumbnail_medium', 'contacts', 'suggested_contacts')
and I've registered the url in urls.py
router = routers.DefaultRouter()
router.register(r'users', app.views_rest.UserViewSet, base_name="users")
but when I tried to GET the url /api/users/2,3/ or /api/users/me/ it gives a json message saying that detail is not found.
/api/users/2/, /api/users/3/ and /api/users/ works fine.
Thanks for the help.
This will not work because the retrieve action also calls the get_object after and you didn't overrode it. See https://github.com/tomchristie/django-rest-framework/blob/master/rest_framework/generics.py#L76