Extension Blueprints¶
Blueprints are a collection of schema fixes for Django and REST Framework apps. Some libraries/apps do not play well with drf-spectacular’s automatic introspection. With extensions you can manually provide the necessary information to generate a better schema.
There is no blueprint for the app you are looking for? No problem, you can easily write extensions yourself. Take the blueprints here as examples and have a look at Workflow & schema customization. Feel free to contribute new ones or fixes with a PR. Blueprint files can be found here.
Note
Simply copy&paste the snippets into your codebase. The extensions register
themselves automatically. Just be sure that the python interpreter sees them at least once.
To that end, we suggest creating a PROJECT/schema.py
file and importing it in your
PROJECT/__init__.py
(same directory as settings.py
and urls.py
)
with import PROJECT.schema
. Please do not import the file in
settings.py
as this may potentially lead to cyclic import issues. Now you are all set.
dj-stripe¶
Stripe Models for Django: dj-stripe
from djstripe.contrib.rest_framework.serializers import (
CreateSubscriptionSerializer, SubscriptionSerializer
)
from drf_spectacular.extensions import OpenApiViewExtension
from drf_spectacular.utils import extend_schema
class FixDjstripeSubscriptionRestView(OpenApiViewExtension):
target_class = 'djstripe.contrib.rest_framework.views.SubscriptionRestView'
def view_replacement(self):
class Fixed(self.target_class):
serializer_class = SubscriptionSerializer
@extend_schema(
request=CreateSubscriptionSerializer,
responses=CreateSubscriptionSerializer
)
def post(self, request, *args, **kwargs):
pass
return Fixed
django-oscar-api¶
RESTful API for django-oscar: django-oscar-api
from rest_framework import serializers
from drf_spectacular.extensions import (
OpenApiSerializerExtension, OpenApiSerializerFieldExtension, OpenApiViewExtension
)
from drf_spectacular.plumbing import build_basic_type
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_field
class Fix1(OpenApiViewExtension):
target_class = 'oscarapi.views.root.api_root'
def view_replacement(self):
return extend_schema(responses=OpenApiTypes.OBJECT)(self.target_class)
class Fix2(OpenApiViewExtension):
target_class = 'oscarapi.views.product.ProductAvailability'
def view_replacement(self):
from oscarapi.serializers.product import AvailabilitySerializer
class Fixed(self.target_class):
serializer_class = AvailabilitySerializer
return Fixed
class Fix3(OpenApiViewExtension):
target_class = 'oscarapi.views.product.ProductPrice'
def view_replacement(self):
from oscarapi.serializers.checkout import PriceSerializer
class Fixed(self.target_class):
serializer_class = PriceSerializer
return Fixed
class Fix4(OpenApiViewExtension):
target_class = 'oscarapi.views.checkout.UserAddressDetail'
def view_replacement(self):
from oscar.apps.address.models import UserAddress
class Fixed(self.target_class):
queryset = UserAddress.objects.none()
return Fixed
class Fix5(OpenApiViewExtension):
target_class = 'oscarapi.views.product.CategoryList'
def view_replacement(self):
class Fixed(self.target_class):
@extend_schema(parameters=[
OpenApiParameter(name='breadcrumbs', type=OpenApiTypes.STR, location=OpenApiParameter.PATH)
])
def get(self, request, *args, **kwargs):
pass
return Fixed
class Fix6(OpenApiSerializerExtension):
target_class = 'oscarapi.serializers.checkout.OrderSerializer'
def map_serializer(self, auto_schema, direction):
from oscarapi.serializers.checkout import OrderOfferDiscountSerializer, OrderVoucherOfferSerializer
class Fixed(self.target_class):
@extend_schema_field(OrderOfferDiscountSerializer(many=True))
def get_offer_discounts(self):
pass
@extend_schema_field(OpenApiTypes.URI)
def get_payment_url(self):
pass
@extend_schema_field(OrderVoucherOfferSerializer(many=True))
def get_voucher_discounts(self):
pass
return auto_schema._map_serializer(Fixed, direction)
class Fix7(OpenApiSerializerFieldExtension):
target_class = 'oscarapi.serializers.fields.CategoryField'
def map_serializer_field(self, auto_schema, direction):
return build_basic_type(OpenApiTypes.STR)
class Fix8(OpenApiSerializerFieldExtension):
target_class = 'oscarapi.serializers.fields.AttributeValueField'
def map_serializer_field(self, auto_schema, direction):
return {
'oneOf': [
build_basic_type(OpenApiTypes.STR),
]
}
class Fix9(OpenApiSerializerExtension):
target_class = 'oscarapi.serializers.basket.BasketSerializer'
def map_serializer(self, auto_schema, direction):
class Fixed(self.target_class):
is_tax_known = serializers.SerializerMethodField()
def get_is_tax_known(self) -> bool:
pass
return auto_schema._map_serializer(Fixed, direction)
class Fix10(Fix9):
target_class = 'oscarapi.serializers.basket.BasketLineSerializer'
djangorestframework-api-key¶
Since djangorestframework-api-key has
no entry in authentication_classes
, drf-spectacular cannot pick up this library. To alleviate
this shortcoming, you can manually add the appropriate security scheme.
Note
Usage of the SECURITY
setting is discouraged, unless there are special circumstances
like here for example. For almost all cases OpenApiAuthenticationExtension
is strongly preferred,
because SECURITY
will get appended to every endpoint in the schema regardless of effectiveness.
SPECTACULAR_SETTINGS = {
"APPEND_COMPONENTS": {
"securitySchemes": {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
}
}
},
"SECURITY": [{"ApiKeyAuth": [], }],
...
}