Prepare for 1.1.0 (#30)
* refactor the view inspection process to be more modular and allow recursive customization * add operation_id argument to @swagger_auto_ * add inspections for min/max validators * add support for URLPathVersioning and NamespaceVersioning * integrate with djangorestframework-camel-case * fix bugs, improve tests and documentation
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
from rest_framework import serializers
|
||||
|
||||
from articles.models import Article
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
||||
class ArticleSerializer(serializers.ModelSerializer):
|
||||
references = serializers.DictField(
|
||||
help_text="this is a really bad example",
|
||||
help_text=_("this is a really bad example"),
|
||||
child=serializers.URLField(help_text="but i needed to test these 2 fields somehow"),
|
||||
read_only=True,
|
||||
)
|
||||
@@ -23,8 +24,8 @@ class ArticleSerializer(serializers.ModelSerializer):
|
||||
'body': {'help_text': 'body serializer help_text'},
|
||||
'author': {
|
||||
'default': serializers.CurrentUserDefault(),
|
||||
'help_text': "The ID of the user that created this article; if none is provided, "
|
||||
"defaults to the currently logged in user."
|
||||
'help_text': _("The ID of the user that created this article; if none is provided, "
|
||||
"defaults to the currently logged in user.")
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,45 @@ from rest_framework.response import Response
|
||||
|
||||
from articles import serializers
|
||||
from articles.models import Article
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.app_settings import swagger_settings
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema, FieldInspector, CoreAPICompatInspector, NotHandled
|
||||
from drf_yasg.utils import swagger_auto_schema
|
||||
|
||||
|
||||
class NoPagingAutoSchema(SwaggerAutoSchema):
|
||||
class DjangoFilterDescriptionInspector(CoreAPICompatInspector):
|
||||
def get_filter_parameters(self, filter_backend):
|
||||
if isinstance(filter_backend, DjangoFilterBackend):
|
||||
result = super(DjangoFilterDescriptionInspector, self).get_filter_parameters(filter_backend)
|
||||
for param in result:
|
||||
if not param.get('description', ''):
|
||||
param.description = "Filter the returned list by {field_name}".format(field_name=param.name)
|
||||
|
||||
return result
|
||||
|
||||
return NotHandled
|
||||
|
||||
|
||||
class NoSchemaTitleInspector(FieldInspector):
|
||||
def process_result(self, result, method_name, obj, **kwargs):
|
||||
# remove the `title` attribute of all Schema objects
|
||||
if isinstance(result, openapi.Schema.OR_REF):
|
||||
# traverse any references and alter the Schema object in place
|
||||
schema = openapi.resolve_ref(result, self.components)
|
||||
schema.pop('title', None)
|
||||
|
||||
# no ``return schema`` here, because it would mean we always generate
|
||||
# an inline `object` instead of a definition reference
|
||||
|
||||
# return back the same object that we got - i.e. a reference if we got a reference
|
||||
return result
|
||||
|
||||
|
||||
class NoTitleAutoSchema(SwaggerAutoSchema):
|
||||
field_inspectors = [NoSchemaTitleInspector] + swagger_settings.DEFAULT_FIELD_INSPECTORS
|
||||
|
||||
|
||||
class NoPagingAutoSchema(NoTitleAutoSchema):
|
||||
def should_page(self):
|
||||
return False
|
||||
|
||||
@@ -26,7 +60,8 @@ class ArticlePagination(LimitOffsetPagination):
|
||||
|
||||
|
||||
@method_decorator(name='list', decorator=swagger_auto_schema(
|
||||
operation_description="description from swagger_auto_schema via method_decorator"
|
||||
operation_description="description from swagger_auto_schema via method_decorator",
|
||||
filter_inspectors=[DjangoFilterDescriptionInspector]
|
||||
))
|
||||
class ArticleViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
@@ -52,7 +87,9 @@ class ArticleViewSet(viewsets.ModelViewSet):
|
||||
ordering_fields = ('date_modified', 'date_created')
|
||||
ordering = ('date_created',)
|
||||
|
||||
@swagger_auto_schema(auto_schema=NoPagingAutoSchema)
|
||||
swagger_schema = NoTitleAutoSchema
|
||||
|
||||
@swagger_auto_schema(auto_schema=NoPagingAutoSchema, filter_inspectors=[DjangoFilterDescriptionInspector])
|
||||
@list_route(methods=['get'])
|
||||
def today(self, request):
|
||||
today_min = datetime.datetime.combine(datetime.date.today(), datetime.time.min)
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
User.objects.filter(username='admin').delete()
|
||||
User.objects.create_superuser('admin', 'admin@admin.admin', 'passwordadmin')
|
||||
Binary file not shown.
@@ -5,18 +5,22 @@ from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
|
||||
|
||||
|
||||
class LanguageSerializer(serializers.Serializer):
|
||||
__ref_name__ = None
|
||||
|
||||
name = serializers.ChoiceField(
|
||||
choices=LANGUAGE_CHOICES, default='python', help_text='The name of the programming language')
|
||||
|
||||
class Meta:
|
||||
ref_name = None
|
||||
|
||||
|
||||
class ExampleProjectSerializer(serializers.Serializer):
|
||||
__ref_name__ = 'Project'
|
||||
|
||||
project_name = serializers.CharField(help_text='Name of the project')
|
||||
github_repo = serializers.CharField(required=True, help_text='Github repository of the project')
|
||||
|
||||
class Meta:
|
||||
ref_name = 'Project'
|
||||
|
||||
|
||||
class SnippetSerializer(serializers.Serializer):
|
||||
"""SnippetSerializer classdoc
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
from djangorestframework_camel_case.parser import CamelCaseJSONParser
|
||||
from djangorestframework_camel_case.render import CamelCaseJSONRenderer
|
||||
from inflection import camelize
|
||||
from rest_framework import generics
|
||||
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||
from snippets.models import Snippet
|
||||
from snippets.serializers import SnippetSerializer
|
||||
|
||||
|
||||
class CamelCaseOperationIDAutoSchema(SwaggerAutoSchema):
|
||||
def get_operation_id(self, operation_keys):
|
||||
operation_id = super(CamelCaseOperationIDAutoSchema, self).get_operation_id(operation_keys)
|
||||
return camelize(operation_id, uppercase_first_letter=False)
|
||||
|
||||
|
||||
class SnippetList(generics.ListCreateAPIView):
|
||||
"""SnippetList classdoc"""
|
||||
queryset = Snippet.objects.all()
|
||||
serializer_class = SnippetSerializer
|
||||
|
||||
parser_classes = (CamelCaseJSONParser,)
|
||||
renderer_classes = (CamelCaseJSONRenderer,)
|
||||
swagger_schema = CamelCaseOperationIDAutoSchema
|
||||
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(owner=self.request.user)
|
||||
|
||||
@@ -31,6 +45,10 @@ class SnippetDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
serializer_class = SnippetSerializer
|
||||
pagination_class = None
|
||||
|
||||
parser_classes = (CamelCaseJSONParser,)
|
||||
renderer_classes = (CamelCaseJSONRenderer,)
|
||||
swagger_schema = CamelCaseOperationIDAutoSchema
|
||||
|
||||
def patch(self, request, *args, **kwargs):
|
||||
"""patch method docstring"""
|
||||
return super(SnippetDetail, self).patch(request, *args, **kwargs)
|
||||
|
||||
@@ -128,3 +128,52 @@ USE_TZ = True
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
TEST_RUNNER = 'testproj.runner.PytestTestRunner'
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': True,
|
||||
'formatters': {
|
||||
'pipe_separated': {
|
||||
'format': '%(asctime)s | %(levelname)s | %(name)s | %(message)s'
|
||||
}
|
||||
},
|
||||
'handlers': {
|
||||
'console_log': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'stream': 'ext://sys.stdout',
|
||||
'formatter': 'pipe_separated',
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'drf_yasg': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': False,
|
||||
},
|
||||
'django': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'DEBUG',
|
||||
'propagate': False,
|
||||
},
|
||||
'django.db.backends': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'django.template': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
},
|
||||
'swagger_spec_validator': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'INFO',
|
||||
'propagate': False,
|
||||
}
|
||||
},
|
||||
'root': {
|
||||
'handlers': ['console_log'],
|
||||
'level': 'INFO',
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ from rest_framework.decorators import api_view
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.views import get_schema_view
|
||||
|
||||
schema_view = get_schema_view(
|
||||
SchemaView = get_schema_view(
|
||||
openapi.Info(
|
||||
title="Snippets API",
|
||||
default_version='v1',
|
||||
@@ -27,12 +27,12 @@ def plain_view(request):
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^swagger(?P<format>.json|.yaml)$', schema_view.without_ui(cache_timeout=0), name='schema-json'),
|
||||
url(r'^swagger/$', schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
url(r'^redoc/$', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
url(r'^cached/swagger(?P<format>.json|.yaml)$', schema_view.without_ui(cache_timeout=None), name='schema-json'),
|
||||
url(r'^cached/swagger/$', schema_view.with_ui('swagger', cache_timeout=None), name='schema-swagger-ui'),
|
||||
url(r'^cached/redoc/$', schema_view.with_ui('redoc', cache_timeout=None), name='schema-redoc'),
|
||||
url(r'^swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=0), name='schema-json'),
|
||||
url(r'^swagger/$', SchemaView.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'),
|
||||
url(r'^redoc/$', SchemaView.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
|
||||
url(r'^cached/swagger(?P<format>.json|.yaml)$', SchemaView.without_ui(cache_timeout=None), name='cschema-json'),
|
||||
url(r'^cached/swagger/$', SchemaView.with_ui('swagger', cache_timeout=None), name='cschema-swagger-ui'),
|
||||
url(r'^cached/redoc/$', SchemaView.with_ui('redoc', cache_timeout=None), name='cschema-redoc'),
|
||||
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'^snippets/', include('snippets.urls')),
|
||||
|
||||
@@ -6,7 +6,7 @@ from snippets.models import Snippet
|
||||
|
||||
class UserSerializerrr(serializers.ModelSerializer):
|
||||
snippets = serializers.PrimaryKeyRelatedField(many=True, queryset=Snippet.objects.all())
|
||||
article_slugs = serializers.SlugRelatedField(read_only=True, slug_field='slug', many=True, source='articlessss')
|
||||
article_slugs = serializers.SlugRelatedField(read_only=True, slug_field='slug', many=True, source='articles')
|
||||
last_connected_ip = serializers.IPAddressField(help_text="i'm out of ideas", protocol='ipv4', read_only=True)
|
||||
last_connected_at = serializers.DateField(help_text="really?", read_only=True)
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ class UserList(APIView):
|
||||
serializer.save()
|
||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||
|
||||
@swagger_auto_schema(request_body=no_body, operation_description="dummy operation")
|
||||
@swagger_auto_schema(request_body=no_body, operation_id="users_dummy", operation_description="dummy operation")
|
||||
def patch(self, request):
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user