Inconsistent file paths in django rest framework - django

I have the following view:
class UserProfileView(APIView):
permissions_classes = [permissions.IsAuthenticated]
def get(self, request):
user = User.objects.get(id=request.user.id)
serializer = UserPrivateSerializer(user)
return Response(serializer.data)
The following Model:
class User(AbstractUser):
pp = models.ImageField(blank=True)
and the following serializer:
class UserPrivateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
The following urls:
urlpatterns = [
path('profile/', UserProfileView.as_view())
]
I response I get is:
{
"pp": "/media/WIN_20190423_18_50_32_Pro.jpg"
}
when I expect:
{
"pp": "localhost:8000/media/WIN_20190423_18_50_32_Pro.jpg"
}
I know it's not a model or serializer issue because I have other views that use the same model and serializer where it returns the full path.

try this:
class UserProfileView(APIView):
permissions_classes = [permissions.IsAuthenticated]
def get(self, request):
user = User.objects.get(id=request.user.id)
serializer = UserPrivateSerializer(user, context=self.get_serializer_context())
return Response(serializer.data)
the key is add context=self.get_serializer_context() to you serializer.

It turns out all I do was to add context={"request": request} to the serializer.

Related

partial update in django rest viewset(not modelviewset)

i am beginner in django and i was trying to learn work with djangorest viewset(not modelviewset)
and i cant find any resource to undrestand it.
i want to learn how to write a partial_update with viewset.
here is what i have tried:
models.py :
class UserIdentDocs(models.Model):
owner = models.ForeignKey(User,on_delete=models.CASCADE, related_name = "user_ident_Docs")
code = models.PositiveBigIntegerField(null=True)
img = models.ImageField(null=True)
video = models.FileField(null=True)
is_complete = models.BooleanField(default=False)
serializers.py:
class AdminUserIdentSerializer(serializers.ModelSerializer):
class Meta:
model = UserIdentDocs
fields = [
"owner",
"code",
"img",
"video",
"is_complete",
]
views.py:
from rest_framework.parsers import MultiPartParser, FormParser
class UserDocAdminViewSet(viewsets.ViewSet):
"""User docs admin view set"""
permission_classes = [AllowAny]
parser_classes = (MultiPartParser, FormParser)
serializer_class = AdminUserIdentSerializer
queryset = UserIdentDocs.objects.filter(is_complete=False)
def list(self, request):
serializer = self.serializer_class(self.queryset, many=True)
return Response(serializer.data)
def retrive(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object)
return Response(serializer.data)
def create(self, request ):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data,status=status.HTTP_201_CREATED)
def partial_update(self, request, pk=None):
doc_object = get_object_or_404(self.queryset, pk=pk)
serializer = self.serializer_class(doc_object,data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail":"item updated succesfuly"}, status=status.HTTP_205_RESET_CONTENT)
urls.py:
urlpatterns = [
...
# admin viewset for docs
# list of accounts with is_complete=False
path("admin/userdocs/", UserDocAdminViewSet.as_view({"get":"list", "post":"create"}), name="user-docs-list"),
path("admin/userdocs/<int:pk>", UserDocAdminViewSet.as_view({"get":"retrive","patch":"partial_update"}), name="user-docs-detail"),
]
i can create user in browsable api but when i want to use partial update i can't even see the fields in browsable api and i only see this :
media type: multipart/form-data
content:this is what i see

Adding a url field linking to an action in serializer in DRF

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.

DRF update model with many-to-many field

I have two models with many-to-many relationship and I also have nested routers between them.
When I'm trying to create a tag at the endpoint api/page/4/tags/, tag is created in the database for tags, but nothing happend in my table for many to many relationship. How can I fix it?
I want to update my M2M everytime a new tag is created. Thank you
models.py
class Tag(models.Model):
title = models.CharField(max_length=30, unique=True)
class Page(models.Model):
...
tags = models.ManyToManyField(Tag, related_name='pages')
My serializers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class PageSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Page
fields = ['id', 'title', 'uuid', 'description', 'owner', 'is_private', 'tags']
views.py
class PageViewSet(viewsets.ModelViewSet):
queryset = Page.objects.all()
serializer_class = PageSerializer
def perform_update(self, serializer):
serializer.save(owner=self.request.user)
class TagViewSet(viewsets.ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
class NestedTagViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = (IsAuthenticatedOrReadOnly,)
def get_page(self, request, page_pk=None):
page = get_object_or_404(Page.objects.all(), pk=page_pk)
self.check_object_permissions(self.request, page)
return page
def create(self, request, *args, **kwargs):
self.get_page(request, page_pk=kwargs['page_pk'])
return super().create(request, *args, **kwargs)
def get_queryset(self):
return Tag.objects.filter(pages=self.kwargs['page_pk'])
def list(self, request, *args, **kwargs):
self.get_page(request, page_pk=kwargs['page_pk'])
return super().list(request, *args, **kwargs)
my urls.py
from django.urls import path, include
from rest_framework_nested import routers
from .views import PageViewSet, TagViewSet, NestedTagViewSet
router = routers.SimpleRouter()
router.register(r'pages', PageViewSet)
pages_router = routers.NestedSimpleRouter(router, r'pages', lookup='page')
pages_router.register(r'tags', NestedTagViewSet, basename='page-tags')
app_name = 'page'
urlpatterns = [
path(r'', include(router.urls)),
path(r'', include(pages_router.urls)),
]
I'm trying to create new tags at the api/ ^pages/(?P<page_pk>[^/.]+)/tags/$ [name='page-tags-list'] host
I will assume after you create a tag you want to add it to the current page in the route.
With this in mind, you have a tag viewset, so you need to override the create method when you are creating a tag.
class TagViewSet(viewsets.ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
new_tag = Tag.objects.get(id=response["id"])
page = Page.objects.get(id=kwards["page_pk"])
page.tags.add(new_tag)
return response
The idea is to create a tag and after you create it, append it to the page M2M relationship. Hope this helps.

How do I correctly retrieve objects from a database using Django REST api?

I'm teaching my self how to use the Django REST api but I'm running into a JSONDecodeError. I'm trying to display posts depending on the user that is stored in the session. I'm storing the user username, password, and id in the session for later use.
The JSONDecodeError is targeting the line with instruction posts = posts_response.json() when I look at the HTML error page
Here's my views.py:
posts_path = "http://127.0.0.1:8000/posts/"
def index(request):
if request.method=='GET':
post_form = PostForm()
posts_response = requests.get(posts_path)
posts = posts_response.json()
context = {
'post_form': post_form,
'post_list': posts,
}
return render(request, 'UserLanding/index.html', context)
else:
return render(request, "UserLanding/index.html")
This is my viewsets:
class PostView(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
def get_queryset(self):
query_set = self.queryset.filter(user=self.request.session['id'])
return query_set
class SubscriberView(viewsets.ModelViewSet):
queryset = Subscriber.objects.all()
serializer_class = SubscriberSerializer
My registered urls:
router = routers.DefaultRouter()
router.register('posts', viewsets.PostView)
router.register('users', viewsets.SubscriberView)
And serializers:
class SubscriberSerializer(serializers.ModelSerializer):
class Meta:
model = Subscriber
fields = ('id', 'username', 'password')
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ('id', 'post_text', 'pub_date', 'user')
Please help me spot the problem. Thanks in advance
EDIT:
I was able to fix the problem by adding a function to my PostView code like this:
#action(detail=False, methods=['GET'], url_path=r'(?P<user_id>[0-9]+)')
def find_by_user_id(self, request, user_id):
user = Subscriber.objects.get(id=user_id)
posts = Post.objects.filter(user=user)
serializer = self.serializer_class(posts, many=True)
return Response(serializer.data)
and my request.get() now points to this:
posts_response = requests.get(posts_path + str(request.session['id']))

django rest api - adding users to keyword model

i want to implement a feature in my rest api that users can add specific keywords for a news feed.
so if the users make a post request with a keyword within, the user object will be added on the predefined keyword (predefined in the database).
I have tried it with this code, but always if i try to simulate the post request with postman and i have this problem:
the keyword will be added but not the provided json data, its just a empty string and the post request returns also an empty keyword...
I hope you are able to help me and maybe you could give me an advice how to just allow the static keywords which are already defined and allow user only have a keyword once (no double keywords with same value)
Made with this headers:
[{"key":"Content-Type","value":"application/json","description":""}]
[{"key":"Authorization","value":"Token xxxxxxx","description":""}]
Body:
{
"name": "keyword1"
}
Authorization works, so the user added to the empty keyword
I am very new to django and i am doing this project to improve my skills, so please be lenient to me :) So it could be that its completly wrong, please give me some advices to solve my problem
These are the snippets for the implementation:
models.py
class Keywords(models.Model):
name = models.CharField(max_length=200)
user = models.ManyToManyField(User)
def __str__(self):
return self.name
serializers.py
class KeywordSerializer(serializers.Serializer):
class Meta:
model = Keywords
fields = ('id', 'name', 'user')
def create(self, validated_data):
keyword = Keywords(**validated_data)
keyword.save()
keyword.user.add(self.context['request'].user)
return keyword
views.py
class KeywordAPI(generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
keyword = serializer.save()
return Response({
"name": KeywordSerializer(keyword, context=self.get_serializer_context()).data,
})
Try this snippet:
class KeywordSerializer(serializers.ModelSerializer):
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Keywords
fields = '__all__'
def create(self, validated_data):
user = validated_data.pop('user')
kw = Keywords.objects.create(**validated_data)
kw.user.add(user)
kw.save()
return kw
and views:
from rest_framework import viewsets, permissions
class KeywordAPI(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
queryset = Keywords.objects.all()
The input payload be as
{
"name":"kw1"
}
NOTE
Here I used ModelSerializer class, because it's very handy for CURD applications and HiddenField is something like write_only=True parameter for fields.
References:
DRF - Modelviewset
HiddenField
CurrentUserDefault
There are few things you are doing wrong
First
Your model's name is Keywords it shouldn't be plural use Keyword and user field is ManyToMany so you should pluralise it
class Keyword(models.Model):
name = models.CharField(max_length=200)
users = models.ManyToManyField(User)
def __str__(self):
return self.name
Second
You are using Serializer instead of ModelSerializer
class KeywordSerializer(serializers.ModelSerializer):
class Meta:
model = Keywords
fields = ('id', 'name', 'users')
read_only_fields = ('users',)
def create(self, validated_data):
keyword = super().create(validated_data)
keyword.users.add(self.context['request'].user)
return keyword
Third
You don't have to write creation logic yourself use existing mixins
from rest_framework import mixins
class KeywordAPI(mixins.CreateModelMixin, generics.RetrieveAPIView):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
For those who maybe have the same problem, here is the whole solution:
Thanks to Sardorbek Imomaliev and Jerin Peter George for helping me
serializer:
class KeywordSerializer(serializers.ModelSerializer):
users = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Keyword
fields = '__all__'
def create(self, validated_data):
users = validated_data.pop('users')
if Keyword.objects.filter(**validated_data).exists():
kw = Keyword.objects.filter(**validated_data).get()
kw.users.add(self.context['request'].user)
return kw
else:
raise serializers.ValidationError("This Keyword has not been set yet.")
model:
class Keyword(models.Model):
name = models.CharField(max_length=200)
users = models.ManyToManyField(User)
def __str__(self):
return self.name
view:
class KeywordAPI(viewsets.ModelViewSet):
permission_classes = [permissions.IsAuthenticated, ]
serializer_class = KeywordSerializer
queryset = Keyword.objects.all()