Get image file and fields in Django rest framework - python

I am trying to upload image via django restframe work and return a custom response
views.py
class Imageuploadviewset(viewsets.ModelViewSet):
queryset = UploadModel.objects.all()
serializer_class = UploadSerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response({'something': 'serializer'})
def list(self, request, *args, **kwargs):
return Response({'Model': 'Skin Disease Diagnosis'})
models.py
class UploadModel(models.Model):
name=models.CharField(max_length=50)
image=models.ImageField('images/')
urls.py
router = DefaultRouter()
router.register(r'UploadModel', views.Imageuploadviewset)
urlpatterns = [
path('',views.home,name='home'),
path('api/', include(router.urls)),
]
Serializer.py
class UploadSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model=UploadModel
fields='__all__'
Query is that how to play with name and image upload, run some functions on them and return a response. I am not sure how to get those.

Instead of retrieve and list i need the create function
def create(self, request):
img=request.FILES['image']
user=request.POST['name']
pred=somefunction(ai,img)
return Response({'user':user,'prediction':pred})

Related

how to pass current authenticated user to django rest framework serializer?

I have a model like this:
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
a serializer like this:
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
and a view like this one:
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
The thing is, I need to pass the current authenticated user for a given request as the user for my serializer, I'm getting an error because obviously the user field is required as stated in my model, but I can't find an elegant way to do so, how could I go about it?
Set the user as a read_only_fields in the serializer meta. This will prevent accepting the user data from the payload.
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
read_only_fields = ["user"]
Then, override the perform_create(...) method of the view class
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
You can make validate method with like validate_user and do following code inside.
self.context['view'].request.user()
class CreateProfessional(generics.CreateAPIView):
serializer_class = ProfessionalSerializer
def create(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data, context={'request': request})
serializer.is_valid(raise_exception=True)
serializer.save()
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
def create(self, validated_data):
user = self.context['request'].user
like this you can get your current user
I found a very nice way to do it, I dont wan't to return custom responses, I will let that to the framework, what I did was the following in my view:
def get_serializer(self, *args, **kwargs):
# redefine method to parameterize the serializer
# while leaving the response handling to the
# framework
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
kwargs['context']['user'] = self.request.user
return serializer_class(*args, **kwargs)
and in my serializer:
class EntitySerializer(serializers.ModelSerializer):
class Meta:
model = Entity
fields = '__all__'
extra_kwargs = {'user': {'required': False}}
def create(self, validated_data):
user = self.context['request'].user
entity = Entity.objects.create(user=user, **validated_data)
return entity
You can use ModelViewSet instead of single API Views. ModelViewSet handles all the crud operations automatically.
Let's say you have the model
class Professional(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dummy_text = models.CharField(max_length=300)
and the serializer
class ProfessionalSerializer(serializers.ModelSerializer):
class Meta:
model = Professional
fields = '__all__'
So you can add a ModelViewSet
class ProfessionalViewSet(ModelViewSet):
queryset = Professional.objects.all()
permission_classes = [IsAuthenticated]
serializer_class = ProfessionalSerializer
this will handle all the crud operations for your model. Following example class will show you the methods that a viewset has.
class UserViewSet(viewsets.ViewSet):
"""
Example empty viewset demonstrating the standard
actions that will be handled by a router class.
If you're using format suffixes, make sure to also include
the `format=None` keyword argument for each action.
"""
def list(self, request):
pass
def create(self, request):
pass
def retrieve(self, request, pk=None):
pass
def update(self, request, pk=None):
pass
def partial_update(self, request, pk=None):
pass
def destroy(self, request, pk=None):
pass
these will handle all the actions. checkout the documentation for more
you'll also have to add the router to handle all the urls automatically which is also covered in the documentation (above link). Following is an example(urls.py).
from myapp.views import UserViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls

Django RestFramework IsOwnerOrReadOnly

I create my permission class for everyone can read an object and only admin create the object. But when I log out and try to create the object, permission class allowing me. What am I missing?
models.py
class Book(models.Model):
title = models.CharField(max_length=20)
content = models.TextField()
page = models.IntegerField()
views.py
class BookAPIView(
mixins.CreateModelMixin,
generics.ListAPIView):
permission_classes = [IsAdminOrReadOnly]
serializer_class = BookSerializer
queryset = Book.objects.all()
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
permissions.py
class IsAdminOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method == 'GET':
return True
return request.user.is_staff
You need to overwrite has_permission:
class IsAdminOrReadOnly(permissions.BasePermission):
def has_permission(self, request, view):
if request.method == 'GET':
return True
return request.user.is_staff
has_object_permission lets you modify one existing object.

Is it possible to automatically create ViewSets and Serializers for each Model?

In our project we have a huge amount of models and some of them are still in the works. I would like to create our REST API using the Django Rest Framework but don't want to create a separate serializer class and ModelViewSet for each Model we have (as most of them should be accessible via API).
Currently there is a Serializer like this for each of our models:
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = '__all__'
Additionally in views.py there is the ModelViewSet:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
Is it possible to somehow reduce the amount of code and make this approach a bit more flexible? I am thinking of something like this:
for model in models:
ser = createSerializer()
createViewSet(ser)
Where with each call a separate class is created. Or is there even a class which makes this possible?
Based of #bdoubleus answer, we can use the viewset with a small modification:
from rest_framework import serializers
# Template class for serializing
class GeneralSerializer(serializers.ModelSerializer):
class Meta:
fields = '__all__'
class GenericAPIView(generics.ListAPIView):
def dispatch(self, request, *args, **kwargs):
self.model = kwargs.pop('model')
self.queryset = self.model.objects.all()
serializer = GeneralSerializer
serializer.Meta.model = self.model
self.serializer_class = serializer
return super().dispatch(request, *args, **kwargs)
To conclude this generator we need to create an URL route for each model in urls.py:
app = apps.get_app_config('testapp')
for model_name, model in app.models.items():
urlpatterns.append(path(model_name, views.GenericAPIView.as_view(), {'model': model}))
You can create a generalized class for your view then pass the model and any other info through extra url paramaters.
For example:
class URLModelAPIView(generics.ListAPIView):
def dispatch(self, request, *args, **kwargs):
self.model = kwargs.pop('model')
self.queryset = self.model.objects.all()
self.serializer = kwargs.pop('serializer')
return super().dispatch(request, *args, **kwargs)
Then in your urls.py
from django.urls import path
from . import models, serializers, views
urlpatterns = [
path(
'customer/',
views.URLModelAPIView.as_view(),
{
'model': models.Customer,
'serializer': serializers.CustomerSerializer,
}
),
path(
'customer_contact/',
views.URLModelAPIView.as_view(),
{
'model': models.CustomerContact,
'serializer': serializers.CustomerContactSerializer,
}
),
]
The same logic applies for viewsets when using a router.

PUT and Delete Request in Django rest framework

I am new to Django, I was wondering How to code PUT request and Delete method in my Django REST API Project.
I have tried to follow with many tutorials, but none working with my kind of code.
The code I tried looks like this
def put(self, request, pk):
snippet = self.get_object(pk)
serializer = UserSerializers(snippet, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
snippet = self.get_object(pk)
snippet.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
But getting message method not allowed
The following is my Models.py
from django.db import models
class Users(models.Model):
user_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=50)
profile_name = models.CharField(max_length=30)
profile_picture = models.CharField(max_length=1000)
phone = models.CharField(max_length=15)
email = models.EmailField()
password = models.CharField(max_length=25, default='')
def __str__(self):
return self.name
The following is my serializers.py
from rest_framework import serializers
from .models import Users
from .models import UserPost
class UserSerializers(serializers.ModelSerializer):
class Meta:
model = Users
fields = '__all__'
class UserDetailSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = '__all__'
class UserPostSerializers(serializers.ModelSerializer):
class Meta:
model = UserPost
fields = '__all__'
The following code contains my Views.py
from django.http import Http404
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework import status
from .models import Users
from .models import UserPost
from .serializers import UserSerializers, UserPostSerializers
class UsersList(APIView):
def get(self, request):
User_details = Users.objects.all()
serializer = UserSerializers(User_details, many=True)
return Response(serializer.data)
def post(self, request):
serializer = UserSerializers(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class UserDetail(APIView):
def get_object(self, pk):
try:
return Users.objects.get(pk=pk)
except Users.DoesNotExist:
raise Http404
def get(self, request, pk):
snippet = self.get_object(pk)
serializer = UserSerializers(snippet)
return Response(serializer.data)
class UserViewSet(viewsets.ModelViewSet):
queryset = Users.objects.all()
serializer_class = UserSerializer
#action(methods=['put'], detail=False)
def filter_by_something(self, request):
something = request.data.get('that_something')
queryset = Users.objects.filter(something__exact=something)
return Response(self.get_serializer(queryset, many=True).data)
The following code contains urls.py
from sys import path
from django.urls import include
from rest_framework.urlpatterns import format_suffix_patterns
from app import views
from django.conf.urls import url
from django.contrib import admin
from rest_framework import routers
router = routers.DefaultRouter()
router.register('user_details', UserDetailViewSet)
urlpatterns = [path('api/', include(router.urls)), ...]
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^User_details/', views.UsersList.as_view()),
url(r'^User_details/(?P<pk>[0-9]+)/', views.UserDetail.as_view()),
url(r'^userpost_details/', views.UsersPostList.as_view()),
]
urlpatterns = format_suffix_patterns(urlpatterns)
Define put & delete methods in UsersDetail class.
class UsersDetail(APIView):
def put(self, request, pk):
// code
def delete(self, request, pk):
//code
If the above does not work also add http_method_names
class UsersDetail(APIView):
http_method_names = ['GET', 'PUT', 'DELETE']
class APIView inherits from class View defined in the base file of Django. dispatch method will check http_method_names for valid method. PUT and DELETE is listed as valid methods along with others.
Reference: django/views/generic/base.py
class View(object):
def dispatch(self, request, *args, **kwargs):
# Try to dispatch to the right method; if a method doesn't exist,
# defer to the error handler. Also defer to the error handler if the
# request method isn't on the approved list.
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
def http_method_not_allowed(self, request, *args, **kwargs):
logger.warning('Method Not Allowed (%s): %s', request.method, request.path,
extra={
'status_code': 405,
'request': request
}
)
return http.HttpResponseNotAllowed(self._allowed_methods())
In urls.py you should declare a router object that maps your ViewSets to DRF:
from rest_framework import routers
router = routers.DefaultRouter()
router.register('user_details', UserDetailViewSet)
urlpatterns = [path('api/', include(router.urls)), ...]
UserDetailViewSet must be declared in api\views.py (api is the directory for DRF application)
from rest_framework.decorators import action
from rest_framework import viewsets
from rest_framework.response import Response
class UserDetailViewSet(viewsets.ModelViewSet):
queryset = UserDetail.objects.all()
serializer_class = UserDetailSerializer
#action(methods=['put'], detail=False)
def filter_by_something(self, request):
something = request.data.get('that_something')
queryset = UserDetail.objects.filter(something__exact=something)
return Response(self.get_serializer(queryset, many=True).data)
Then you will make PUT requests on http:\\your_website\api\user_details\filter_by_something
Try something like this. It should work!

Django TemplateHTMLRenderer - Rendering a HTML view

I'm trying to render some objects from my model to HTML. I initially thought this was going to be straightforward (and it likely is) but I'm coming up against numerous errors.
In this project I have built some API views that work fine (HintsListApiView and HintsRudView work). But I'd ideally like to use the API to produce a regular - read only HTML page that I can then style as I wish - my HTMLAPIView. I'm trying to use TemplateHTMLRenderer, but am coming up against errors. All I want is to get the text attribute to show up in HTML. The actual texts are just a sentence long each.
These are my files:
models.py:
from django.db import models
from rest_framework.reverse import reverse as api_reverse
class Hints(models.Model):
text = models.TextField(max_length=255)
author = models.CharField(max_length=20)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.text)
def timestamp_pretty(self):
return self.timestamp.strftime('%b %d %Y')
def get_api_url(self, request=None):
return api_reverse("api-hints1:hints-rud", kwargs={'pk': self.pk}, request=request)
views.py:
class HTMLAPIView(generics.RetrieveAPIView):
lookup_field = 'pk'
serializer_class = HTMLSerializer
renderer_classes = (TemplateHTMLRenderer,)
def get_queryset(self):
queryset = Hints.objects.value('text')
return Response({'queryset': queryset}, template_name='base.html')
class HintsListApiView(mixins.CreateModelMixin, generics.ListAPIView):
lookup_field = 'pk'
serializer_class = HintsSerializer
def get_queryset(self):
qs = Hints.objects.all()
query = self.request.GET.get("q")
if query is not None:
qs = qs.filter(
Q(text__icontains=query)|
Q(author__icontains=query)
).distinct()
return qs
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def get_serializer_context(self, *args, **kwargs):
return {"request": self.request}
class HintsRudView(generics.RetrieveUpdateDestroyAPIView):
lookup_field = 'pk'
serializer_class = HintsSerializer
def get_queryset(self):
return Hints.objects.all()
def get_serializer_context(self, *args, **kwargs):
return {"request": self.request}
serializers.py:
class HintsSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Hints
fields = [
'url',
'id',
'text',
'author',
'timestamp_pretty'
]
read_only_fields = ['timestamp_pretty', 'id']
def get_url(self, obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
class HTMLSerializer(serializers.ModelSerializer):
class Meta:
model = Hints
fields = [
'text',
]
read_only_fields = ['text',]
root urls.py:
from django.contrib import admin
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets
urlpatterns = [
url('admin/', admin.site.urls),
url(r'^api/hints/', include(('hints1.api.urls', 'api'), namespace='api-hints1')),
url(r'^api-auth/', include('rest_framework.urls')),
]
urls.py:
from .views import HintsRudView, HintsListApiView, HTMLAPIView
from . import views
from django.contrib import admin
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets
urlpatterns = [
url(r'^(?P<pk>\d+)$', HintsRudView.as_view(), name='hints-rud'),
url(r'^$', HintsListApiView.as_view(), name='hints-list'),
url(r'^html/', HTMLAPIView.as_view(), name='html' )
]
The errors I've faced were varied, currently I'm encountering AttributeError at /api/hints/html/
Manager object has no attribute 'value'.
I've tried with and without a serializer (because in the documentation it mentions TemplateHTMLRenderer doesn't need one). I think the problem lies within the view.py and the get_queryset function. I've tried various approaches but will get other errors like
TypeError context must be a dict rather than QuerySet.
Any help that can be provided will be greatly appreciated!
Thanks!
So I managed to do what I wanted. But to be honest I'm not completely sure why it worked, so if anyone can provide clarification, please do.
I changed my HTMLAPIView in views.py to a Viewset:
class HTMLAPIView(viewsets.ViewSet):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'base.html'
serializer_class = HTMLSerializer
def list(self, request):
queryset = Hints.objects.order_by('pk')
return Response({'queryset': queryset})
I then got an error in my urls.py but was able to fix that by including the dictionary portion in the .as_view()
url(r'^html/', HTMLAPIView.as_view({'get': 'list'}), name='html' )
The reason why I'm not sure why this worked is that it returns my text attribute from my model as I wanted but I don't see where I specified that that's the correct attribute. Is it just because it is the first one?
I also tried commenting out the serializer and it still worked fine, so I concluded that wasn't the reason for the results.

Categories