Skip to main content

Worf (Wade's Own Rest Framework): A more djangonic approach

Project description

Worf

build-status-image pypi-version

Worf is a small Django API framework for building out REST APIs simply using class-based views and serializers.

Worf

Full documentation for the project is available at https://memory-alpha.fandom.com/wiki/Worf.

Table of contents

Installation

pip install worf

Requirements

  • Python (3.7, 3.8, 3.9)
  • Django (3.0, 3.1, 3.2)

Roadmap

  • Abstracting serializers away from model methods
  • Declarative marshmallow-based serialization
  • More support for different HTTP methods
  • File upload support
  • Support for user-generated validators
  • Better test coverage
  • Browsable API docs

Usage

The following examples provides you with an API that does the following:

  • Only allows authenticated users to access the endpoints
  • Provides a list of books, with POST support to create a new book
  • Provides an endpoint for each book's detail endpoint, with PATCH support

A more complete example will demonstrate the additional built-in capabilities, including search, pagination, ordering, and the other things Worf can do.

# models.py
class Book(models.Model):
    title = models.CharField(max_length=128)
    author_name = models.CharField(max_length=128)
    published_at = models.DateField()
# serializers.py
from worf.serializers import Serializer

class BookSerializer(Serializer):
    class Meta:
        fields = [
            "id",
            "title",
            "author_name",
            "published_at",
        ]
# views.py
from worf.permissions import Authenticated
from worf.views import DetailAPI, ListAPI, UpdateAPI

class BookList(CreateAPI, ListAPI):
  model = Book
  serializer = BookSerializer
  permissions = [Authenticated]

class BookDetail(UpdateAPI, DetailAPI):
  model = Book
  serializer = BookSerializer
  permissions = [Authenticated]
# urls.py
path("api/", include([
    path("books/", BookList.as_view()),
    path("books/<int:id>/", BookDetail.as_view()),
])),

Serializers

Worf serializers are basically marshmallow schemas with some tweaks to improve support for Django models, and supply extra defaults.

from worf.serializers import fields, Serializer

class BookSerializer(Serializer):
    image_url = fields.Function(lambda obj: obj.get_image_url())
    author = fields.Nested(AuthorSerializer)
    tags = fields.Nested(TagSerializer, many=True)

    class Meta:
        fields = [
            "id",
            "title",
            "content",
            "image_url",
            "url",
            "author",
            "tags",
        ]

Worf serializers build on top of marshmallow to make them a little easier to use in Django, primarily, we add support for using the Nested field with related managers, and setting default serializer options via settings:

WORF_SERIALIZER_DEFAULT_OPTIONS = {
    "dump_only": [
        "id",
        "created_at",
        "deleted_at",
        "updated_at",
    ]
}

Permissions

Permissions functions can be found in worf.permissions.

These functions extend the API View, so they require self to be defined as a parameter. This is done in order to allow access to self.request during permission testing.

If permissions should be granted, functions should return int(200).

If permissions fail, they should return an HTTPException

Validators

Validation handling can be found in worf.validators.

The basics come from ValidationMixin which AbstractBaseAPI inherits from, it performs some coercion on self.bundle, potentially resulting in a different bundle than what was originally passed to the view.

Views

AbstractBaseAPI

Provides the basic functionality of both List and Detail APIs. It is not recommended to use this abstract view directly.

Name Type Default Description
model class None An uninstantiated django.db.models.model class.
permissions list [] Pass a list of permissions classes.

ListAPI

Name Type Default Description
filters dict {} Pass key/value pairs that you wish to further filter the queryset beyond the lookup_url_kwarg
lookup_field str None Use these two settings in tandem in order to filter get_queryset based on a URL field. lookup_url_kwarg is required if this is set.
lookup_url_kwarg str None Use these two settings in tandem in order to filter get_queryset based on a URL field. lookup_field is required if this is set.
payload_key str verbose_name_plural Use in order to rename the key for the results array
ordering list [] Pass a list of fields to default the queryset order by.
filter_fields list [] Pass a list of fields to support filtering via query params.
search_fields list [] Pass a list of fields to full text search via the q query param.
sort_fields list [] Pass a list of fields to support sorting via the sort query param.
per_page int 25 Sets the number of results returned for each page.
max_per_page int per_page Sets the max number of results to allow when passing the perPage query param.

The get_queryset method will use lookup_url_kwarg and lookup_field to filter results. You should not need to override get_queryset. Instead, set the optional variables listed above to configure the queryset.

Filtering

Parameters in the URL must be camelCase and exactly match the snake_case model field.

To allow full text search, set to a list of fields for django filter lookups.

Pagination

All ListAPI views are paginated and include a pagination json object.

Use per_page to set custom limit for pagination. Default 25.

DetailAPI

Name Type Default Description
lookup_field str id Override with the lookup field used to filter the model. Defaults to id
lookup_url_kwarg str id Override with the name of the parameter passed to the view by the URL route. Defaults to id

This get_instance() method uses lookup_field and lookup_url_kwargs to return a model instance.

You may prefer to override this method, for example in a case when you are using request.user to return an instance.

CreateAPI

Adds a post method to handle creation, mix this into a ListAPI view:

class BookListAPI(CreateAPI, ListAPI):
    model = Book
    serializer = BookSerializer

Validation of creates is kind of sketchy right now, but the idea is that you'd use the same serializer as you would for an update, unless you have create-only fields, in which case, you may want to create a BookCreateSerializer.

UpdateAPI

Adds patch and put methods to handle updates, mix this into a DetailAPI.

class BookDetailAPI(UpdateAPI, DetailAPI):
    model = Book
    serializer = BookSerializer

Validation of update fields is delegated to the serializer, any fields that are writeable should be within the fields definition of the serializer, and not marked as dump_only (read-only).

Bundle loading

The dispatch method is run by Django when the view is called. In our version of dispatch, we interpret any request.body as JSON, and convert all values from camel to snake case at that time. You'll always be able to access bundle attributes by their snake case variable name, and these attributes will exactly match the model fields.

self.bundle is set on a class level, it is available to all methods inside the view. We perform type coercion during validation, so self.bundle will be changed during processing. You may also append or remove attributes to the bundle before saving the object via post, patch, or other methods.

Field casing

Worf expects all your model fields to be defined in snake case 🐍, and JSON objects to be camel case 🐪 and that conversion is handled in worf.casing.

We interpret camelCase, strictly, based on the database model. This means that inappropriate naming of database fields will result in confusion.

A quick example:

freelance_fulltime = models.CharField(...)
freelancer_id = models.UUIDField(...)
API_strict = ...

This will be strictly translated by the API, and acronyms are not considered:

  • freelance_fulltime == freelanceFulltime
  • freelancer_id == freelancerId
  • API_strict == apiStrict

File uploads

File uploads are supported via multipart/form-data requests.

Internal naming

We refer to the json object that is sent and received by the API differently in this codebase for clarity:

  • bundle is what we send to the backend.
  • payload is what the backend returns.

Credits

Wanted dead or alive Made with 🥃 at Gun.io

Contributors

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

worf-0.4.7.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

worf-0.4.7-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file worf-0.4.7.tar.gz.

File metadata

  • Download URL: worf-0.4.7.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.9

File hashes

Hashes for worf-0.4.7.tar.gz
Algorithm Hash digest
SHA256 359f07c41871dfb05b1d7d339466b01c94ea5e7887d71f28f2b50118cb83c56f
MD5 78a8b35c51f16d5b8849593f7c1db317
BLAKE2b-256 17248454e841a75e978f7c2a7f6510fe2fa83d9aa095d42a9f9e5d2352923e3a

See more details on using hashes here.

File details

Details for the file worf-0.4.7-py3-none-any.whl.

File metadata

  • Download URL: worf-0.4.7-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.9

File hashes

Hashes for worf-0.4.7-py3-none-any.whl
Algorithm Hash digest
SHA256 3c40ae50ec7635661b8f97bdc8d319360366ec668b43615b842a9955a152cf3f
MD5 ce5e8f41cf9527d650e41cb76a9201ce
BLAKE2b-256 867e0b1bb19a359d58788cd207f5c70fbd98982afbf9fac2b909829792846707

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page