I have a Django REST Framework project which uses a ModelViewSet to create APIs for a model containing a FileField.
I've shared a full example of a Django project demonstrating this issue here. But to summarise the key components:
models.py
from django.db import models
class Profile(models.Model):
image = models.FileField(upload_to='uploads/%Y/%m/%d/')
views.py
from rest_framework import (
viewsets,
serializers,
parsers,
)
from sample import models
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.Profile
fields = ['id', 'image']
read_only_fields = ['id']
class ProfileViewSet(viewsets.ModelViewSet):
serializer_class = ProfileSerializer
queryset = models.Profile.objects.all()
urls.py
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
)
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
from rest_framework.routers import DefaultRouter
from sample import views
router = DefaultRouter()
router.register('profile', views.ProfileViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
path('api/schema/', SpectacularAPIView.as_view(), name='api-schema'),
path(
'api/docs/',
SpectacularSwaggerView.as_view(url_name='api-schema'),
name='api-docs',
),
path('', include(router.urls)),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
settings.py (REST_FRAMEWORK configuration only):
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
}
requirements.txt
Django==3.2.3
djangorestframework==3.12.4
drf-spectacular==0.16.0
I am generating browsable Swagger-UI docs using drf-spectacular to document the API.
The problem is that the FileField input in Swagger-UI is a string input, and doesn't give the option to set a file:
I would like to have a file input where I can choose a file which will be sent to the API.
My question is: How can I configure DRF or drf-spectacular to show this field?
After some digging through the drf-spectacular docs and GitHub issues, I found that the FileField can be set to binary by adding the following to in settings.py:
SPECTACULAR_SETTINGS = {
'COMPONENT_SPLIT_REQUEST': True
}
Also in Swagger UI, make sure you change the content-type from application/json to multipart/form-data and click Try it out. The upload button will apppear.
Related
I want to make a swagger document of my project, so I tried to generate a dynamic schema for my APIs, using django rest automatic dynamic generated schema, as said in its document.
This is my urls.py:
from django.urls import path, include
from rest_framework.schemas import get_schema_view
schema_view = get_schema_view(title="Example API")
urlpatterns = [
path("schema/", schema_view, name="schema"),
...
]
And this is one of my view serializers:
class BookSerializer(serializers.ModelSerializer):
summary_publisher_name = serializers.ReadOnlyField(source="published_by.name")
authors = AuthorSerializer(many=True)
chapters = ChapterSerializer(many=True, read_only=True)
class Meta:
model = Book
fields = ["title", "summary_publisher_name", "authors", "chapters"]
...
The problem is that in generated schema, there is none of these API parameters.
How can I add them to schema? Is there any automation way, or I need to add all fields for any of my views, by myself?
I need to combine external json using request and Internal Database, But the condition is when someone call API we need to get empidlong from Model for call external API from specific URL, Then external API will return JSON and i need to merge this JSON with my json that create by Django REST API Framework
Here is my code
models.py :
from django.db import models
import requests
from django.core.files import File
from urllib import request
import os
# Create your models here.
class getData(models.Model):
company = models.CharField(max_length=150)
description = models.CharField(max_length=150)
period = models.CharField(max_length=150)
plate_no = models.CharField(max_length=150, primary_key=True)
project_code = models.CharField(max_length=150)
costcenter = models.CharField(max_length=150)
empidlong = models.CharField(max_length=150)
class Meta:
db_table = 'car_information'
serializers.py
from rest_framework import serializers
from .models import getData
class CarSerializer(serializers.ModelSerializer):
class Meta:
model = getData
fields = "__all__"
views.py
from django.shortcuts import render
from rest_framework import viewsets, filters
from .models import getData
from .serializers import CarSerializer
import requests
class CarViewSet(viewsets.ModelViewSet):
queryset = getData.objects.all()
serializer_class = CarSerializer
filter_backends = (filters.SearchFilter,)
#search_fields = ('plate_no')
__basic_fields = ('plate_no',)
search_fields = __basic_fields
serializer = CarSerializer(queryset, many=True)
data = serializer.data
for a in data:
empid= a['empidlong']
r = requests.get('http://192.168.10.32/Employees/'+empid)
def get_queryset(self):
queryset = getData.objects.all()
emp = self.request.query_params.get('emp', None)
if emp is not None:
queryset = queryset.filter(empidlong=emp)
return queryset
I have no idea how to do it. I stuck this for week.
Thank in advance.
Where are you expecting the json from? if from a user, you can access the json from the request. Then you can use it in your orm query. You CarViewSet seem to be doing way more than it should be. What is your goal exactly?
I am trying to query from django i.e http://localhost:8000/search/Tesco/apples to get a query of json list, the one below.
[
{
"id": 12,
"Date": "2018-08-02",
"Title": "Rosedene Farms Small Sweet Apple 520G",
"Price": 0.96,
"PricePerWeight": "1.85/kg",
"FinalOffer": "undefined undefined",
"ProductID": 292249576
},
My urls.py:
from django.conf.urls import url, include
from . import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register('Tesco', views.TescoView)
urlpatterns = [
url('', include(router.urls), name='search'),
url(r'^search/', include(router.urls), name='searchTwo')
my views.py
from __future__ import unicode_literals
from django.shortcuts import render
from rest_framework import viewsets
from .models import Tesco
from .serializers import TescoSerializers
from django.core.urlresolvers import reverse_lazy, reverse
class TescoView(viewsets.ModelViewSet):
queryset = Tesco.objects.filter(Title__icontains='apple')
serializer_class = TescoSerializers
how would I get the URL http://localhost:8000/search/tesco/ to query through the database for the list of json?
I don't think searching like:
http://localhost:8000/search/Tesco/apples
is a normal pattern in Django Rest Framework (DRF), so I think you're going to hit a lot of resistance trying to make this pattern work with DRF. If I may suggest, I believe you're in the X-Y Problem space - "That is, you are trying to solve problem X, and you think solution Y would work, but instead of asking about X when you run into trouble, you ask about Y."
Normally, filtering parameters are specified on the listing view of the model. For your model, your complete Tesco instance listing can be found at:
http://localhost:8000/Tesco/
If you want to filter this down, you append query parameters after a ? like:
http://localhost:8000/Tesco/?title__icontains=apple
or
http://localhost:8000/Tesco/?ProductID=292249576
or multiple searching filters like:
http://localhost:8000/Tesco/?ProductID=292249576&title__icontains=apple
To use this pattern, you need to modify your viewset and add a FilterSet. This is what your views.py file would look like:
from __future__ import unicode_literals
from django.shortcuts import render
from rest_framework import viewsets
from .models import Tesco
from .serializers import TescoSerializers
from django.core.urlresolvers import reverse_lazy, reverse
# Import filtering libraries
import django_filters
from rest_framework import filters
class TescoFilterSet(django_filter.FilterSet):
title__icontains = django_filter.Filter('Title', lookup_expr='icontains')
class Meta:
model = Tesco
fields = ('title__icontains', 'ProductID')
class TescoView(viewsets.ModelViewSet):
queryset = Tesco.objects.filter(Title__icontains='apple')
serializer_class = TescoSerializers
# Hook up filterset
filter_backends = (django_filters.rest_framework.DjangoFilterBackend, filters.OrderingFilter,)
filter_class = TescoFilterSet
# allow ordering on any field
ordering_fields = '__all__'
In my experience the nginx and apache webservers seem to work well with this pattern when you get to content caching.
For more on filtering, see the DRF guide on Filtering.
Ordering
Per your comment, you can order by specifying order_fields as seen above. Then you can add the ordering parameter.
vvvvvvvvvvvvvv
http://localhost:8000/Tesco/?title__icontains=apple&ordering=price
This will be ascending price.
Add a - before price and the order is reversed or descending:
http://localhost:8000/Tesco/?title__icontains=apple&ordering=-price
^
I used Django restframework to implement api server.
Also I used djangorestframework-jwt for token authentication.
[urls.py]
from django.contrib import admin
from django.urls import path, include
from rest_framework_jwt.views import refresh_jwt_token
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('rest_auth.urls')),
path('registration/', include('rest_auth.registration.urls')),
path('refresh-token/', refresh_jwt_token),
]
Everything works fine. But I want to know that How can I extract payload from token?
For example, There is article table.
[/article/serializers.py]
from rest_framework import serializers
from . import models
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = models.Article
fields = '__all__'
[models.py]
from django.db import models
class Article(models.Model):
subject = models.CharField(max_length=20)
content = models.CharField(max_length=100)
[views.py]
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from . import models, serializers
class Article(APIView):
def get(self, request, format=None):
all_article = models.Article.objects.all()
serializer = serializers.ArticleSerializer(all_article, many=True)
return Response(data=serializer.data, status=status.HTTP_200_OK)
In this case, I want to return correct response only payload['userid'] == article's userid.
How can I extract username from jwt token?
Previous, I just use jwt not djangorestframework-jwt, So just decode request data and use it.
But now I use djangorestframework-jwt, I confused How can I do it.
Is there any solution about this?
Thanks.
I solved this issue after checking the library's documentation here
https://jpadilla.github.io/django-rest-framework-jwt/
I needed to Override all the functions to return custom response as required in my application.
Also any additional settings we need to specify in JWT_AUTH in settings.py of my application.
i.e.
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER':
'myapp.utils.jwt_response_payload_handler',
}
And needed to define jwt_response_payload_handler function in a util folder in my app to override the default function.
models.py:
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.formatters.html import HtmlFormatter
from pygments import highlight
LEXERS = [item for item in get_all_lexers() if item[1]]
class Classname(models.Model):
class_name = models.CharField(max_length=5)
owner = models.ForeignKey('auth.User', related_name='fab')
highlighted = models.TextField()
def save(self, *args, **kwargs):
lexer = get_lexer_by_name(self.class_name)
options = self.class_name and {'class': self.class_name} or {}
formatter = HtmlFormatter(full=True, **options)
self.highlighted = highlight(self.class_name, lexer, formatter)
super(Classname, self).save(*args, **kwargs)
serializers.py:
from django.forms import widgets
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Classname
class ClassSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
highlight = serializers.HyperlinkedIdentityField(view_name='fab-highlight', format='html')
class Meta:
model = Classname
fields = ('id', 'owner', 'class_name', 'highlight',)
class UserSerializer(serializers.ModelSerializer):
fab = serializers.HyperlinkedRelatedField(many=True, view_name='user-detail', read_only=True)
class Meta:
model = User
fields = ('id', 'username', 'fab',)
urls.py:
from django.conf.urls import url, patterns
from rest_framework.urlpatterns import format_suffix_patterns
from . import views
urlpatterns = [
url(r'^class/$', views.ClassList.as_view()),
url(r'^class/(?P<pk>[0-9]+)/$', views.ClassDetail.as_view()),
url(r'^section/$', views.SectionList.as_view()),
url(r'^section/(?P<pk>\d+)/$', views.SectionDetail.as_view()),
url(r'^teacher/$', views.TeacherList.as_view()),
url(r'^teacher/(?P<pk>[0-9]+)/$', views.TeacherDetail.as_view()),
url(r'^attend/$', views.AttendanceList.as_view()),
url(r'^attend/(?P<pk>[0-9]+)/$', views.AttendanceDetail.as_view()),
]
When I changed my serializers.py file and try to add HyperLinked as per the tutorial then I got the above error, I'm not using ViewSets and Routers. I don't know what's the problem b'coz I've checked my url's and views everything seems to be fine.
I'm confused, Please help me to find and fix the issue....
Thanks in advance.....
The exact issue is that I'm using namespace in urls and I've used it in my project level urls.py file also. So in the views file I have to follow django-namespacing like this:
'users': reverse('student:user-list', request=request, format=format),
while 'student' is the namespace that I've used in my project level urls like:
url(r'^stu/', include('app.urls', namespace = 'student'),