Skip to main content

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

Project description

Worf

CI

A more Djangonic approach to Django REST APIs.

Worf

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

Overview

Worf is a small Django API framework that lets you quickly build out an api using simple model methods. Contributions welcome.

This project is stable but will change drastically.

Roadmap

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

API Method

Views execute the api_method in order to return data.

Example API Method:

def api(self):
  return [
    'camelCaseField': self.camel_case_field,
    ...
  ]

Serializer

Views append _update_fields to the api_method property. This model method will be executed on PATCH requests. PATCH requests are supported in DetailUpdateAPI. If you do not want to support PATCH on a particular endpoint, use DetailAPI.

Only fields included in the serializer method will be used to PATCH models.

If a PATCH request is made with fields that are not in the serializer, the response will be HTTP 400.

Example Serializer:

def api_update_fields(self):
  return [
    'field_for_update_1',
    'field_for_update_2'
    ...
  ]

Example

The following example 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.

Model Example

class Book(models.Model):
    title = models.CharField(max_length=128)
    author_name = models.CharField(max_length=128)
    published_at = models.DateField()

    def api_update_fields(self):
        return [
            "title",
            "author_name",
        ]

    def api(self):
        return dict(
            title=self.title,
            authorName=self.author_name,
            published_at=self.published_at,
        )

View Book Example

from worf.views import ListAPI, DetailPatchAPI

class BookList(ListCreateAPI):
  permissions = [Authenticated]
  model = Book

class BookDetail(DetailPatchAPI):
  permissions = [Authenticated]
  model = Book

URLs

...
path("api/v1/", include([
      path("books/", BookList.as_view()),
      path("books/<int:id>/", BookDetail.as_view()),
  ])
),
...

View Reference

worf.permissions

Permissions functions. 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 HTTPExcption

worf.validators

Provides ValidationMixin which AbstractBaseAPI inherits from. Will perform some coercion on self.bundle, potentially resulting in a different bundle than what was originally passed to the view.

worf.views

AbstractBaseAPI

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

Class Attributes

name required type description
bundle_name no str Default: None If set, the returned data will use this name. I.e., {bundle_name: return_data}
model yes class An uninstantiated django.db.models.model class.
permissions yes list of permissions classes Will return appropriate HTTP status code based on the definition of the permission class.
HTTP Methods
  • GET is always supported.

ListAPI

name required type description
api_method no str Default: api. Must refer to a model method. This method is used to return data for GET requests.
payload_key no str Default: model._meta.verbose_name_plural. Use in order to rename the key for the results array
filters no dict Default: {}. Pass key/value pairs that you wish to further filter the queryset beyond the lookup_url_kwarg
lookup_field no str 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 no str 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.
ordering no list Default: []. Pass a list of valid fields to order the queryset by.
search_fields no dict or bool Default: False.
results_per_page no int Default: 25. Sets the number of results returned for each page.
Search

Setting search_fields to True will enable search based on url parameters. Parameters in the URL must be camelCase and exactly match the snake_case model field.

To allow full text search, set to a dictionary of two lists: 'or' & 'and'. Each list will be assembled using either Q.OR or Q.AND. Fields in each list must be composed of django filter lookup argument names.

Pagination

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

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

get_queryset() method

This 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.

Return
  1. A list of the ListAPI.{model}.{instance}.api() method by default.
  2. If lookup_field and lookup_url_kwarg fields are set, it will return a filtered list.
{
  model_verbose_name: [
    instance1.api(),
    ...
  ]
}

ListCreateAPI

Adds post method to handle creation. Otherwise identical to ListAPI. In most cases, you will need to override the post method in order to create objects properly. Do this by performing whatever logic you need to do, then update the self.bundle with whatever properties you need to set on the object. Then call return super().post(request, *args, **kwargs).

DetailAPI

name default type description
api_method api str Must refer to a model method. This method is used to return data for GET requests. Additionally, {api_method}_update_fields will be called to execute PATCH requests.
lookup_field id str Override with the lookup field used to filter the model. Defaults to id
lookup_url_kwarg id str Override with the name of the parameter passed to the view by the URL route. Defaults to id
get_instance() method

This 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.

DetailUpdateAPI

Adds patch method to handle updates. Otherwise identical to DetailAPI. Runs validators automatically. You should not need to override update. If the api_update_fields model method returns an empty list, HTTP 422 will be raised.

Additional Information

Converting 🐍 & 🐪

Worf expects all your model fields to be defined in snake case, and your api's json objects to be camel case. This conversion is handled in worf.casing.

Limitations With 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:

# profiles.models.freelancers.Freelancer
freelance_fulltime = models.CharField(...)
freelancer_id = UUIDField(...)
API_strict = ...

This will be strictly translated by the API. Acronyms are not considered:

  • freelance_fulltime == freelaneFulltime
  • freelancer_id == freelancerId
  • API_strict == apiStrict

At this time, we still manually define the camelCase names in the model api() method. This will eventually be removed in favor of automated case conversion, but it has and will continue to present an opportunity for error, for now.

self.bundle is loaded from json

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.

Naming Things

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.

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.2.0.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

worf-0.2.0-py3-none-any.whl (23.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: worf-0.2.0.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.10.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.8

File hashes

Hashes for worf-0.2.0.tar.gz
Algorithm Hash digest
SHA256 2493176057c8bac4a4df428476b48bc1877e2d3b79483429b39b9a26debae8e6
MD5 f96ca85ebc1d9274755e4cc6cd695ecf
BLAKE2b-256 1cbe59000618f3181dfb260526131474a9cf5e675fbba084fbc99b541f2eb10d

See more details on using hashes here.

File details

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

File metadata

  • Download URL: worf-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 23.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/3.10.0 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.59.0 CPython/3.8.8

File hashes

Hashes for worf-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c83e46db65b8d786a3fcd2f7621970f0391def77316b478698fa9e943ef8c623
MD5 e55dcad229c2bd8759d3b0494eff1ca8
BLAKE2b-256 6581f590a3874756d4daa1f45b255c7529f3ca4375cbf1183a12f833738eb655

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