Package: django-dataclasses
This library provides a decorator that when applied to a Django views does JSON serialization and enforces
schemas using thedataclasses-jsonschemalibrary.It also enables you to export your API definition to anOpenAPIspec file. This enables you to
autogenerate client libraries.UsageTo use it, decorate your view functions with the appropriate HTTP method provided bydjango-dataclasses(post,get,patch, orput). Annotate the return values of your functions with the appropriate
dataclass type.Views that take input require an additionalbodyparameter with the appropriate dataclass type annotation:from django import http
import django_dataclasses


@django_dataclasses.dataclass
class StringLengthRequest:
    input: str


@django_dataclasses.dataclass
class StringLengthResponse:
    length: int


@django_dataclasses.login_required
@django_dataclasses.post
def string_length(request: http.HttpRequest, body: StringLengthRequest) -> StringLengthResponse:
    """Calculate the length of a string"""
    return StringLengthResponse(length=len(body.input))Returning errorsTo return an error response in your views, raisedjango_dataclasses.ErrorResponse.
For example, in this instance we raise anErrorResponseif the login credentials are invalid:if user is not None:
    login(request, user)
    return UserResponse(user.username)
else:
    raise django_dataclasses.ErrorResponse("Incorrect username or password", 401)The exception will becaughtby thedjango-dataclassesdecorator and an HTTP response with a text body and status
code you provide will be returned.Query ParametersViews that use query params require an additionalqueryparameter with the appropriate
dataclass as a type annotation:from django import http
import django_dataclasses


@django_dataclasses.dataclass
class QueryParams:
    contains: str = ""


@django_dataclasses.dataclass
class FruitsResponse:
    """Fruits with the given string"""
    fruits: typing.List[str]


@django_dataclasses.get
def fruit_medley(request: http.HttpRequest, query: QueryParams) -> FruitsResponse:
    """Fruits that contain a given string"""
    fruits = ["apple", "pear", "orange", "plum"]
    return FruitsResponse([fruit for fruit in fruits if query.contains in fruit])Iterables and paginated resultsdjango-dataclassesincludes theiterable_factoryutility for creating a dataclass that represents an
iterable list of results, alleviating the need to redefine the same schema for list-based endpoints.For example, if one wants to return a list of the following results:@django_dataclasses.dataclass
class MyObject:
    val: intUse theiterable_factoryto create a wrapper around an iterable of items. For example:MyIterableObject = django_dataclasses.iterable_factory(MyObject)


@django_dataclasses.post
def iterable_get(request: http.HttpRequest) -> MyIterableObject:
    """Return an iterable of results"""
    return MyIterableObject(items=[MyObject(val=0), MyObject(val=1)])The returned payload will include anitemskey with the serialized results.The payload also includes attributes for pagination, including the totalcountof items
returned and the currentpagenumber. By default, no pagination occurs. To enable pagination,
passpaginated=Truetoiterable_factorylike so.:@django_dataclasses.dataclass
class QueryParams:
    """All query params must be strings as they are encoded in the URL"""
    page: str = "1"
    page_size: str = "10"


MyPaginatedObject = django_dataclasses.iterable_factory(MyObject, paginated=True)


@django_dataclasses.get
def paginated_get(request: http.HttpRequest, query: QueryParams) -> MyPaginatedObject:
    """Return an paginated iterable of results"""
    page = MyPaginatedObject(
        items=models.MyModel.objects.all(),
        page_num=int(query.page),
        page_size=int(query.page_size)
    )
    page.items = [IterableObject(row.a) for row in page.items]
    return pageBy default, the page size is 10. This can be changed by passingpage_sizeto the paginator.
An example response payload with a page size of2will look like this:{
    'page': 1,
    'count': 20,
    'items': [
        {'val': 0},
        {'val': 1}
    ]
}Schema exportThis library was inspired by the convenience of using autogenerated gRPC client libraries. Not everyone
can adopt gRPC, anddjango-dataclassesaims to provides a similar toolkit for JSON APIs.Export your schemas in the OpenAPI format using this management command. You need to adddjango_dataclassesto yourINSTALLED_APPSfor it to be available:./manage.py openapi_export > api.jsonThen, use another library to autogenerate client libraries. For example, you can useopenapi-typescript-codegento generate a JavaScript client complete with typed methods by running:yarn add --dev openapi-typescript-codegen
yarn run openapi --input api.json --output api/LicenseContributions are made under the terms of the Apache License, Version 2.0.
SeeLICENSE.ContributingThis project usespyenv, please install it first.
When running on a Mac, theMakefileuses Homebrew to install dependencies. On other
platforms please install them manually.Clone the repo and setup your development environment by running:git clone https://gitlab.com/roivant/oss/django-dataclasses.git
make setupRun the tests and code validations:make test
make validateYou can test exporting the openapi schema and compiling it to JavaScript usingopenapi-typescript-codegenby running this command. The output files are put in a folderapi/:make apiContributors must adhere to the Contributor CovenantCode of Conduct.
Please report suspected violations tovant.tech.eng@roivant.com.About the templateThis repository was created from thetemple-pythontemplate. If you have access to that repository, apply updates from the template by running:temple updateWhat tools are included?A Makefile for convenience; usemake setupto setup your dev environmentA build configuration in.gitlab-ci.ymlA test framework using pytest and requiring 100% coverage; usemake testto runPython auto-formatting usingblack,flake8, andisortvia a git pre-commit hookAutomatic versioning and ChangeLog usingPBRVersioning using PBRThePBRlibrary will automatically
increment the version when you commit to the main branch by merging your pull request.
The commit message that you enter on GitHub when merging will determine what version
number is assigned, usingSemantic Versioning.Messages starting withSem-Ver:bugfix,or with no matching message will bump thePATCHnumberMessages starting withSem-Ver:feature,orSem-Ver:deprecation,will bump theMINORnumber.Messages starting withSem-Ver:api-break,will bump theMAJORnumber.