Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

Django API Forms

PyPI version codecov

Django Forms approach in the processing of a RESTful HTTP request payload (especially for content type like JSON or MessagePack) without HTML front-end.

Motivation

The main idea was to create a simple and declarative way to specify the format of expecting requests with the ability to validate them. Firstly, I tried to use Django Forms to validate my API requests (I use pure Django in my APIs). I have encountered a problem with nesting my requests without a huge boilerplate. Also, the whole HTML thing was pretty useless in my RESTful APIs.

I wanted to:

  • define my requests as object (Form),
  • pass the request with optional arguments to my defined object (form = Form.create_from_request(request, param=param)),
  • validate my request form.is_valid(),
  • extract data form.clean_data property.

I wanted to keep:

So I have decided to create a simple Python package to cover all my expectations.

Installation

# Using pip
pip install django-api-forms

# Using poetry
poetry add django-api-forms

# Local installation
python -m pip install .

Optional:

# msgpack support (for requests with Content-Type: application/x-msgpack)
poetry add msgpack

# ImageField support
poetry add Pillow

Install application in your Django project by adding django_api_forms to yours INSTALLED_APPS:

INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django_api_forms'
)

You can change the default behavior of population strategies or parsers using these settings (listed with default values). Keep in mind, that dictionaries are not replaced by your settings they are merged with defaults.

For more information about the parsers and the population strategies check the documentation.

DJANGO_API_FORMS_POPULATION_STRATEGIES = {
    'django_api_forms.fields.FormFieldList': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.FileField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.ImageField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django_api_forms.fields.FormField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django.forms.models.ModelMultipleChoiceField': 'django_api_forms.population_strategies.IgnoreStrategy',
    'django.forms.models.ModelChoiceField': 'django_api_forms.population_strategies.ModelChoiceFieldStrategy'
}

DJANGO_API_FORMS_DEFAULT_POPULATION_STRATEGY = 'django_api_forms.population_strategies.BaseStrategy'

DJANGO_API_FORMS_PARSERS = {
    'application/json': 'json.loads',
    'application/x-msgpack': 'msgpack.loads'
}

Example

Simple nested JSON request

{
  "title": "Unknown Pleasures",
  "type": "vinyl",
  "artist": {
    "_name": "Joy Division",
    "genres": [
      "rock",
      "punk"
    ],
    "members": 4
  },
  "year": 1979,
  "songs": [
    {
      "title": "Disorder",
      "duration": "3:29"
    },
    {
      "title": "Day of the Lords",
      "duration": "4:48",
      "metadata": {
        "_section": {
          "type": "ID3v2",
          "offset": 0,
          "byteLength": 2048
        },
        "header": {
          "majorVersion": 3,
          "minorRevision": 0,
          "size": 2038
        }
      }
    }
  ],
  "metadata": {
    "created_at": "2019-10-21T18:57:03+0100",
    "updated_at": "2019-10-21T18:57:03+0100"
  }
}

Django API Forms equivalent + validation

from enum import Enum

from django.core.exceptions import ValidationError
from django.forms import fields

from django_api_forms import FieldList, FormField, FormFieldList, DictionaryField, EnumField, AnyField, Form


class AlbumType(Enum):
    CD = 'cd'
    VINYL = 'vinyl'


class ArtistForm(Form):
    class Meta:
        mapping = {
            '_name': 'name'
        }

    name = fields.CharField(required=True, max_length=100)
    genres = FieldList(field=fields.CharField(max_length=30))
    members = fields.IntegerField()


class SongForm(Form):
    title = fields.CharField(required=True, max_length=100)
    duration = fields.DurationField(required=False)
    metadata = AnyField(required=False)


class AlbumForm(Form):
    title = fields.CharField(max_length=100)
    year = fields.IntegerField()
    artist = FormField(form=ArtistForm)
    songs = FormFieldList(form=SongForm)
    type = EnumField(enum=AlbumType, required=True)
    metadata = DictionaryField(value_field=fields.DateTimeField())

    def clean_year(self):
        if self.cleaned_data['year'] == 1992:
            raise ValidationError("Year 1992 is forbidden!", 'forbidden-value')
        if 'param' not in self.extras:
            self.add_error(
                ('param', ),
                ValidationError("You can use extra optional arguments in form validation!", code='param-where')
            )
        return self.cleaned_data['year']

    def clean(self):
        if (self.cleaned_data['year'] == 1998) and (self.cleaned_data['artist']['name'] == "Nirvana"):
            raise ValidationError("Sounds like a bullshit", code='time-traveling')
        if not self._request.user.is_authenticated():
            raise ValidationError("You can use request in form validation!")
        if 'param' not in self.extras:
            raise ValidationError("You can use extra optional arguments in form validation!")
        return self.cleaned_data



"""
Django view example
"""
def create_album(request):
    form = AlbumForm.create_from_request(request, param=request.GET.get('param'))
    if not form.is_valid():
        # Process your validation error
        print(form.errors)

    # Cleaned valid payload
    payload = form.cleaned_data
    print(payload)

If you want example with whole Django project, check out repository created by pawl django_api_forms_modelchoicefield_example, where he uses library with ModelChoiceField.

Running Tests

# install all dependencies
poetry install

# run code-style check
poetry run flake8 .

# run the tests
poetry run python runtests.py

Made with ❤️ and ☕️ by Jakub Dubec, BACKBONE s.r.o. & contributors.

Download files

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

Source Distribution

django_api_forms-1.0.0rc9.tar.gz (13.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

django_api_forms-1.0.0rc9-py3-none-any.whl (13.6 kB view details)

Uploaded Python 3

File details

Details for the file django_api_forms-1.0.0rc9.tar.gz.

File metadata

  • Download URL: django_api_forms-1.0.0rc9.tar.gz
  • Upload date:
  • Size: 13.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.4.0 CPython/3.11.2 Darwin/22.3.0

File hashes

Hashes for django_api_forms-1.0.0rc9.tar.gz
Algorithm Hash digest
SHA256 e7c8ea28bc3a4f10f91e13bd1a19b3bc8d3ddc1b0094f312011d1965cf1bc131
MD5 b5f382a892423b57000b14437e8b16b7
BLAKE2b-256 0c5c293bd8f975d1e5d83d27bf05b0ae251373b0c606816290807c7a15e02220

See more details on using hashes here.

File details

Details for the file django_api_forms-1.0.0rc9-py3-none-any.whl.

File metadata

File hashes

Hashes for django_api_forms-1.0.0rc9-py3-none-any.whl
Algorithm Hash digest
SHA256 f41492c9d9c77f103a698bbe2afca639a554abb8f32c3023663442cbfc203bd6
MD5 355576cf05f4882302a4e1d3dcf429e9
BLAKE2b-256 6191d6cd1cc376f625fbf605ceed800b675d26878a3e2acf95903e6037d1c980

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.0rc9 This release

2 files

0.21.0

2 files

0.20.1

2 files

0.20.0

2 files

0.19.1

2 files

0.19.0

2 files

0.18.0

2 files

0.17.0

2 files

0.16.4

2 files

0.16.3

2 files

0.16.2

2 files

0.16.1

2 files

0.16.0

2 files

0.15.1

2 files

0.15.0

2 files

0.14.0

2 files

0.13.0

2 files

0.12.0

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.0

2 files

0.7.1

2 files

0.7.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page