it was necessary to combine several models(4) in one serializer, but there were problems with the implementation.
urls.py
from django.urls import path
from .views import FiltersView
urlpatterns = [
path('filters/' FiltersView.as_view(), name='Filters')
]
views.py
from rest_framework import views
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK
from .serializers import FiltersSerializers
class FiltersView(views.APIView):
def get(self, request, *args, **kwargs):
filters = {}
filters['model_1'] = Model1.objects.all()
filters['model_2'] = Model2.objects.all()
filters['model_3'] = Model3.objects.all()
serializer = FiltersSerializers(filters, many=True)
return Response (serializer.data, status=HTTP_200_OK)
serializers.py
from rest_framework import serializers
class FiltersSerializers(serializers.Serializer):
model_1 = Model1Serializers(read_only=True, many=True)
model_2 = Model2Serializers(read_only=True)
model_3 = Model3Serializers(read_only=True)
But on the output I get:
HTTP 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept
[
{},
{},
{}
]
What could be the problem?
The way you are providing data to your serializer, many=True is not correct argument for it. Its a single object that you are passing to your serializer. Your view should be like this.
class FiltersView(views.APIView):
def get(self, request, *args, **kwargs):
filters = {}
filters['model_1'] = Model1.objects.all()
filters['model_2'] = Model2.objects.all()
filters['model_3'] = Model3.objects.all()
serializer = FiltersSerializers(filters)
return Response (serializer.data, status=HTTP_200_OK)
Related
I have the below simple rest api set up in Django. Calling the url http://127.0.0.1:8000/listheros/ returns
TypeError: Object of type Hero is not JSON serializable
for a reason I can't seem to figure out.
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import HeroSerializer
from .models import Hero
class ListHeros(APIView):
def get(self, request, format=None):
"""
Return a list of all users.
"""
queryset = Hero.objects.all().order_by('name')
serializer_class = HeroSerializer
print('get')
return Response(queryset)
# urls.py
from django.urls import include, path
from applications.api.views import ListHeros
urlpatterns = [
path('listheros/', ListHeros.as_view()),
path('api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]
# serializers.py
from rest_framework import serializers
from .models import Hero
class HeroSerializer(serializers.ModelSerializer):
class Meta:
model = Hero
fields = ('name', 'alias')
# models.py
from django.db import models
class Hero(models.Model):
name = models.CharField(max_length=60)
alias = models.CharField(max_length=60)
def __str__(self):
return self.name
You don't serialize your queryset of heroes. Your ListHeroes api view should looks like below:
class ListHeros(APIView):
def get(self, request, format=None):
"""
Return a list of all users.
"""
queryset = Hero.objects.all().order_by('name')
serializer = HeroSerializer(queryset, many=True)
return Response(serializer.data)
You can also use generics ListApiView instead of APIView:
from rest_framework.generics import ListAPIView
class ListHeros(ListAPIView):
queryset = Hero.objects.all().order_by('name')
serializer_class = HeroSerializer
In my urls file, I have specified the path to views -
urlpatterns = [
path('users/userdetails', PersonalUserDetailView.as_view(), name='hello'),
path('stores/<int:store_id>', StoreDetailView.as_view(), name='hello2'),
]
Howvere, I want to make the store_id optional. How do I do it?
Set default arguments in view methods
class StoreDetailView(...):
def get(self, request, store_id=None):
...
def post(self, request, store_id=None):
...
Also, you need to update the URL patterns a bit,
urlpatterns = [
path('users/userdetails', PersonalUserDetailView.as_view(), name='hello'),
path('stores/<int:store_id>', StoreDetailView.as_view(), name='hello2'),
path('stores/', StoreDetailView.as_view(), name='hello-default'),
]
You can use regex non-capturing parenthesis, similar question here
from django.urls import re_path
urlpatterns = [
...
re_path(r'stores/(?:(?P<store_id>\d+)/)?$',StoreDetailView.as_view(), name="hello2"), # non-capture
]
class StoreAPIView(APIView):
def get(self, request, store_id=None):
if store_id is None:
qs = Store.objects.all()
return Reponse(.......)
else:
try:
store_obj = Store.objects.get(id=store_id)
serilized_data = StoreSerializer(store_obj).data
return Response(....)
except Exception as e:
return Response(...)
URLS
path('stores/<int:store_id>', StoreAPIView.as_view(), name='hello2')
You can also use viewsets which is much better:
from django.shortcuts import get_object_or_404
from myapps.serializers import StoreSerializer
from rest_framework import viewsets
from rest_framework.response import Response
class StoreViewSet(viewsets.ViewSet):
def list(self, request):
queryset = Store.objects.all()
serializer = StoreSerializer(queryset, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
queryset = Store.objects.all()
store = get_object_or_404(queryset, pk=pk)
serializer = StoreSerializer(store)
return Response(serializer.data)
urls.py
from myapp.views import StoreViewSet
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'stores', UserViewSet, basename='store')
urlpatterns = router.urls
I want to create an endpoint, in this endpoint its possible to send POST request, if the POST request
is validated, then the page download a csv
I created the serializer form to make a easy validation of the data received
My problem is that the csv its easy downloaded in a HttpResponse, but i need neccesary to make a endpoint and a validation of the data in a post request.
My principal problem is that i canĀ“t return the export function next of the validation
This are my files
#urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^hello-view/', views.HelloApiView.as_view()),
]
Serializers
#serializers.py
from rest_framework import serializers
class HelloSerializer(serializers.Serializer):
"""Serializes a name field """
name = serializers.CharField(max_length=100)
seller_id = serializers.CharField(max_length=100)
def validate_name(self, dob):
UnitOfMeasureName = ["Each", "Grams", "Ounces", "Pounds", "Kilograms", "Metric Tons"]
if dob in UnitOfMeasureName:
return dob
else:
raise serializers.ValidationError('Wrong username')
And the views files
In this file i created the export function to try to export the csv data, but doesnt works
import csv
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import serializers
class HelloApiView(APIView):
def export(self, request):
response = HttpResponse(content_type='text/csv')
writer = csv.writer(response)
writer.writerow(['First name', 'Last name', 'ID'])
response['Content-Disposition'] = 'attachment; filename="one.csv"'
return response
serializer_class = serializers.HelloSerializer
def get(self, request, format=None):
an_apiview = [
'Uses HTTP METHOD as function',
'Its is similar to a traditional django view'
]
return Response({'message': 'Hello', 'anapi': an_apiview})
def post(self, request):
serializer = serializers.HelloSerializer(data = request.data)
if serializer.is_valid():
in this place i want to return the file
else:
return Response(
serializer.errors, status = status.HTTP_400_BAD_REQUEST)
Add a new endpoint to your urls.py file
#urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^hello-view/', views.HelloApiView.as_view()),
url(r'^csv_download/$', views.csv_download, name="csv_download"),
]
Then in your views.py file, add a function called csv_download and move all of your stuff there (This might be unnecessary, but it sure makes for cleaner, more readable code)
# views.py
import csv
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import serializers
class HelloApiView(APIView):
def get(self, request, format=None):
an_apiview = [
'Uses HTTP METHOD as function',
'Its is similar to a traditional django view'
]
return Response({'message': 'Hello', 'anapi': an_apiview})
def csv_download(request):
if request.method == 'POST':
serializer_class = serializers.HelloSerializer
serializer = serializers.HelloSerializer(data = request.data)
if serializer.is_valid():
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="one.csv"'
writer = csv.writer(response,delimiter=',') # I always like to specify the delimeter
writer.writerow(['First name', 'Last name', 'ID'])
#Then you may actually want to write some data to the CSV file, currently, you've only defined the headers (first row). An example would be like:
for value in list_of_objects:
writer.writerow([
value.first_name,
value.last_name,
value.id
])
return response
else:
return Response(
serializer.errors, status = status.HTTP_400_BAD_REQUEST)
else:
# redirect them or do something here if they hit this URL without a POST request
I made simple api with Django Rest framework.
[models.py]
from django.db import models
class Menu(models.Model):
place = models.CharField(max_length=20, primary_key=True)
mon = models.CharField(max_length=3000)
tue = models.CharField(max_length=3000)
wed = models.CharField(max_length=3000)
thu = models.CharField(max_length=3000)
fri = models.CharField(max_length=3000)
sat = models.CharField(max_length=3000)
sun = models.CharField(max_length=3000)
[serializers.py]
from rest_framework import serializers
from . import models
class MenuSerializer(serializers.ModelSerializer):
class Meta:
model = models.Menu
fields = '__all__'
[urls.py]
from django.conf.urls import url
from django.urls import path
from . import views
urlpatterns = [
path('', views.Menu.as_view(), name='menu')
]
[views.py]
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import models
class Menu(APIView):
def get(self, request, format=None):
all_menu = models.Menu.objects.all()
return Response(status=status.HTTP_200_OK, data=all_menu)
When I connect to /menu, It throws
TypeError at /menu/
Object of type 'Menu' is not JSON serializable
How can I solve this issue?
You are trying to pass as response queryset direcly. But queryset is not serializable object. And this is why you need serializers.
Just serialize data before return it as response with serializer class, like this:
class Menu(APIView):
def get(self, request, format=None):
all_menu = models.Menu.objects.all()
serializer = MenuSerializer(all_menu, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)
I have this view
from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.response import Response
from rest_framework.views import APIView
from .serializers import EmailUserSerializer
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
#method_decorator(csrf_exempt, name='post')
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
user_serializer = EmailUserSerializer(user)
return Response({'token': token.key, 'user': user_serializer.data})
obtain_auth_token = ObtainAuthToken.as_view()
and this url
urlpatterns = [
url(r'^login/$',views.obtain_auth_token, name='get_auth_token'),
url(r'^login2/$',ObtainAuthToken, name='get_auth_token'),
]
i'm trying posting with postman like this:
127.0.0.1:8000/api/login2/
but i can only receive this error
Forbidden (CSRF cookie not set.): /api/login2/
[02/Jul/2017 22:49:11] "POST /api/login2/ HTTP/1.1" 403 2891
I know there are hundreds of post like this, I searched for a long time a solution but nothing seems working
tryied like this
urlpatterns = patterns('',
url('^login2/$', csrf_exempt(ObtainAuthToken)),
...
)
this
from django.utils.decorators import method_decorator
class LoginView(APIView):
#method_decorator(csfr_exempt)
def dispatch(self, *args, **kwargs):
...
and also this
from django.utils.decorators import method_decorator
#method_decorator(csrf_exempt, name='dispatch')
class LoginView(APIView):
...
and this
#method_decorator(csrf_exempt, name='post')
class ObtainAuthToken(APIView):
throttle_classes = ()
...
#csrf_exempt
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data)
You need to use ObtainAuthToken.as_view(). Any APIView automatically uses csrf_exempt() (and explicitly checks the CSRF token if you're using SessionAuthentication), but that won't work if you're not using .as_view(). You don't have to explicitly use csrf_exempt on top of what APIView does.
I'm not sure why you're not using the first url, /login/, but if you're having issues with that url, you're going the wrong way fixing them.
On a side note: csrf_exempt sets an attribute on the function. As such, using it on post() has absolutely no effect, since the middleware won't check the attributes on the post() method. You need to use it on the dispatch() method or as csrf_exempt(ObtainAuthToken.as_view()).