Skip to content

Commit e27be39

Browse files
committed
Docs 😇
1 parent 508f3be commit e27be39

8 files changed

Lines changed: 1238 additions & 193 deletions

File tree

README.md

Lines changed: 127 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -3,56 +3,97 @@
33
[![PyPI version](https://badge.fury.io/py/django-api-forms.svg)](https://badge.fury.io/py/django-api-forms)
44
[![codecov](https://codecov.io/gh/Sibyx/django_api_forms/branch/master/graph/badge.svg)](https://codecov.io/gh/Sibyx/django_api_forms)
55

6-
[Django Forms](https://docs.djangoproject.com/en/4.1/topics/forms/) approach in the processing of a RESTful HTTP
7-
request payload (especially for content type like [JSON](https://www.json.org/) or [MessagePack](https://msgpack.org/))
8-
without HTML front-end.
6+
**Django API Forms** is a Python library that brings the [Django Forms](https://docs.djangoproject.com/en/4.1/topics/forms/) approach to processing RESTful HTTP request payloads (such as [JSON](https://www.json.org/) or [MessagePack](https://msgpack.org/)) without the HTML front-end overhead.
7+
8+
## Overview
9+
10+
Django API Forms provides a declarative way to:
11+
12+
- Define request validation schemas using a familiar Django-like syntax
13+
- Parse and validate incoming API requests
14+
- Handle nested data structures and complex validation rules
15+
- Convert validated data into Python objects
16+
- Populate Django models or other objects with validated data
17+
18+
[**📚 Read the full documentation**](https://sibyx.github.io/django_api_forms/)
19+
20+
## Key Features
21+
22+
- **Declarative Form Definition**: Define your API request schemas using a familiar Django Forms-like syntax
23+
- **Request Validation**: Validate incoming requests against your defined schemas
24+
- **Nested Data Structures**: Handle complex nested JSON objects and arrays
25+
- **Custom Field Types**: Specialized fields for common API use cases (BooleanField, EnumField, etc.)
26+
- **File Uploads**: Support for BASE64-encoded file and image uploads
27+
- **Object Population**: Easily populate Django models or other objects with validated data
28+
- **Customizable Validation**: Define custom validation rules at the field or form level
29+
- **Multiple Content Types**: Support for JSON, MessagePack, and extensible to other formats
930

1031
## Motivation
1132

12-
The main idea was to create a simple and declarative way to specify the format of expecting requests with the ability
33+
The main idea was to create a simple and declarative way to specify the format of expected requests with the ability
1334
to validate them. Firstly, I tried to use [Django Forms](https://docs.djangoproject.com/en/4.1/topics/forms/) to
14-
validate my API requests (I use pure Django in my APIs). I have encountered a problem with nesting my requests without
35+
validate my API requests (I use pure Django in my APIs). I encountered a problem with nesting my requests without
1536
a huge boilerplate. Also, the whole HTML thing was pretty useless in my RESTful APIs.
1637

1738
I wanted to:
1839

19-
- define my requests as object (`Form`),
20-
- pass the request with optional arguments to my defined object (`form = Form.create_from_request(request, param=param)`),
21-
- validate my request `form.is_valid()`,
22-
- extract data `form.clean_data` property.
40+
- Define my requests as objects (`Form`)
41+
- Pass the request to my defined object (`form = Form.create_from_request(request, param=param))`)
42+
- With the ability to pass any extra optional arguments
43+
- Validate my request (`form.is_valid()`)
44+
- Extract data (`form.cleaned_data` property)
2345

2446
I wanted to keep:
2547

26-
- friendly declarative Django syntax,
27-
([DeclarativeFieldsMetaclass](https://github.com/django/django/blob/master/django/forms/forms.py#L25) is beautiful),
28-
- [Validators](https://docs.djangoproject.com/en/4.1/ref/validators/),
29-
- [ValidationError](https://docs.djangoproject.com/en/4.1/ref/exceptions/#validationerror),
30-
- [Form fields](https://docs.djangoproject.com/en/4.1/ref/forms/fields/) (In the end, I had to "replace" some of them).
48+
- Friendly declarative Django syntax
49+
([DeclarativeFieldsMetaclass](https://github.com/django/django/blob/master/django/forms/forms.py#L22) is beautiful)
50+
- [Validators](https://docs.djangoproject.com/en/4.1/ref/validators/)
51+
- [ValidationError](https://docs.djangoproject.com/en/4.1/ref/exceptions/#validationerror)
52+
- [Form fields](https://docs.djangoproject.com/en/4.1/ref/forms/fields/) (In the end, I had to "replace" some of them)
3153

32-
So I have decided to create a simple Python package to cover all my expectations.
54+
So I created this Python package to cover all these expectations.
3355

3456
## Installation
3557

36-
```shell script
58+
```shell
3759
# Using pip
3860
pip install django-api-forms
3961

4062
# Using poetry
4163
poetry add django-api-forms
4264

43-
# Local installation
65+
# Local installation from source
4466
python -m pip install .
4567
```
4668

47-
Optional:
48-
```shell script
49-
# msgpack support (for requests with Content-Type: application/x-msgpack)
50-
poetry add msgpack
69+
### Requirements
70+
71+
- Python 3.9+
72+
- Django 2.0+
5173

52-
# ImageField support
53-
poetry add Pillow
74+
### Optional Dependencies
75+
76+
Django API Forms supports additional functionality through optional dependencies:
77+
78+
```shell
79+
# MessagePack support (for Content-Type: application/x-msgpack)
80+
pip install django-api-forms[msgpack]
81+
82+
# File and Image Fields support
83+
pip install django-api-forms[Pillow]
84+
85+
# RRule Field support
86+
pip install django-api-forms[rrule]
87+
88+
# GeoJSON Field support
89+
pip install django-api-forms[gdal]
90+
91+
# Install multiple extras at once
92+
pip install django-api-forms[Pillow,msgpack]
5493
```
5594

95+
For more detailed installation instructions, see the [Installation Guide](https://sibyx.github.io/django_api_forms/install/).
96+
5697
Install application in your Django project by adding `django_api_forms` to yours `INSTALLED_APPS`:
5798

5899
```python
@@ -88,146 +129,98 @@ DJANGO_API_FORMS_PARSERS = {
88129
}
89130
```
90131

91-
## Example
132+
## Quick Example
92133

93-
**Simple nested JSON request**
94-
95-
```json
96-
{
97-
"title": "Unknown Pleasures",
98-
"type": "vinyl",
99-
"artist": {
100-
"_name": "Joy Division",
101-
"genres": [
102-
"rock",
103-
"punk"
104-
],
105-
"members": 4
106-
},
107-
"year": 1979,
108-
"songs": [
109-
{
110-
"title": "Disorder",
111-
"duration": "3:29"
112-
},
113-
{
114-
"title": "Day of the Lords",
115-
"duration": "4:48",
116-
"metadata": {
117-
"_section": {
118-
"type": "ID3v2",
119-
"offset": 0,
120-
"byteLength": 2048
121-
},
122-
"header": {
123-
"majorVersion": 3,
124-
"minorRevision": 0,
125-
"size": 2038
126-
}
127-
}
128-
}
129-
],
130-
"metadata": {
131-
"created_at": "2019-10-21T18:57:03+0100",
132-
"updated_at": "2019-10-21T18:57:03+0100"
133-
}
134-
}
135-
```
136-
137-
**Django API Forms equivalent + validation**
134+
Here's a simple example demonstrating how to use Django API Forms:
138135

139136
```python
140-
from enum import Enum
141-
142-
from django.core.exceptions import ValidationError
143137
from django.forms import fields
138+
from django.http import JsonResponse
139+
from django_api_forms import Form, FormField, FieldList
144140

145-
from django_api_forms import FieldList, FormField, FormFieldList, DictionaryField, EnumField, AnyField, Form
146-
147-
148-
class AlbumType(Enum):
149-
CD = 'cd'
150-
VINYL = 'vinyl'
151-
152-
141+
# Define a nested form
153142
class ArtistForm(Form):
154-
class Meta:
155-
mapping = {
156-
'_name': 'name'
157-
}
158-
159143
name = fields.CharField(required=True, max_length=100)
160144
genres = FieldList(field=fields.CharField(max_length=30))
161145
members = fields.IntegerField()
162146

163-
164-
class SongForm(Form):
165-
title = fields.CharField(required=True, max_length=100)
166-
duration = fields.DurationField(required=False)
167-
metadata = AnyField(required=False)
168-
169-
147+
# Define the main form
170148
class AlbumForm(Form):
171149
title = fields.CharField(max_length=100)
172150
year = fields.IntegerField()
173151
artist = FormField(form=ArtistForm)
174-
songs = FormFieldList(form=SongForm)
175-
type = EnumField(enum=AlbumType, required=True)
176-
metadata = DictionaryField(value_field=fields.DateTimeField())
177-
178-
def clean_year(self):
179-
if self.cleaned_data['year'] == 1992:
180-
raise ValidationError("Year 1992 is forbidden!", 'forbidden-value')
181-
if 'param' not in self.extras:
182-
self.add_error(
183-
('param', ),
184-
ValidationError("You can use extra optional arguments in form validation!", code='param-where')
185-
)
186-
return self.cleaned_data['year']
187-
188-
def clean(self):
189-
if (self.cleaned_data['year'] == 1998) and (self.cleaned_data['artist']['name'] == "Nirvana"):
190-
raise ValidationError("Sounds like a bullshit", code='time-traveling')
191-
if not self._request.user.is_authenticated():
192-
raise ValidationError("You can use request in form validation!")
193-
if 'param' not in self.extras:
194-
raise ValidationError("You can use extra optional arguments in form validation!")
195-
return self.cleaned_data
196-
197-
198-
199-
"""
200-
Django view example
201-
"""
152+
153+
# In your view
202154
def create_album(request):
203-
form = AlbumForm.create_from_request(request, param=request.GET.get('param'))
155+
form = AlbumForm.create_from_request(request)
204156
if not form.is_valid():
205-
# Process your validation error
206-
print(form.errors)
157+
# Handle validation errors
158+
return JsonResponse({"errors": form.errors}, status=400)
207159

208-
# Cleaned valid payload
209-
payload = form.cleaned_data
210-
print(payload)
160+
# Access validated data
161+
album_data = form.cleaned_data
162+
# Do something with the data...
163+
164+
return JsonResponse({"status": "success"})
165+
```
166+
167+
This form can validate a JSON request like:
168+
169+
```json
170+
{
171+
"title": "Unknown Pleasures",
172+
"year": 1979,
173+
"artist": {
174+
"name": "Joy Division",
175+
"genres": ["rock", "punk"],
176+
"members": 4
177+
}
178+
}
211179
```
212180

213-
If you want example with whole Django project, check out repository created by [pawl](https://github.com/pawl)
214-
[django_api_forms_modelchoicefield_example](https://github.com/pawl/django_api_forms_modelchoicefield_example), where
215-
he uses library with
216-
[ModelChoiceField](https://docs.djangoproject.com/en/4.1/ref/forms/fields/#django.forms.ModelChoiceField).
181+
### More Examples
182+
183+
For more comprehensive examples, check out the documentation:
184+
185+
- [Basic Example with Nested Data](https://sibyx.github.io/django_api_forms/example/#basic-example-music-album-api)
186+
- [User Registration with File Upload](https://sibyx.github.io/django_api_forms/example/#example-user-registration-with-file-upload)
187+
- [API with Django Models](https://sibyx.github.io/django_api_forms/example/#example-api-with-django-models)
188+
- [ModelChoiceField Example](https://github.com/pawl/django_api_forms_modelchoicefield_example) - External repository by [pawl](https://github.com/pawl)
189+
190+
## Documentation
191+
192+
Comprehensive documentation is available at [https://sibyx.github.io/django_api_forms/](https://sibyx.github.io/django_api_forms/)
193+
194+
The documentation includes:
195+
196+
- [Installation Guide](https://sibyx.github.io/django_api_forms/install/)
197+
- [Tutorial](https://sibyx.github.io/django_api_forms/tutorial/)
198+
- [Field Reference](https://sibyx.github.io/django_api_forms/fields/)
199+
- [Examples](https://sibyx.github.io/django_api_forms/example/)
200+
- [API Reference](https://sibyx.github.io/django_api_forms/api_reference/)
201+
- [Contributing Guide](https://sibyx.github.io/django_api_forms/contributing/)
217202

218203
## Running Tests
219204

220-
```shell script
221-
# install all dependencies
205+
```shell
206+
# Install all dependencies
222207
poetry install
223208

224-
# run code-style check
209+
# Run code-style check
225210
poetry run flake8 .
226211

227-
# run the tests
212+
# Run the tests
228213
poetry run python runtests.py
214+
215+
# Run tests with coverage
216+
poetry run coverage run runtests.py
217+
poetry run coverage report
229218
```
230219

220+
## License
221+
222+
Django API Forms is released under the MIT License.
223+
231224
---
232225
Made with ❤️ and ☕️ by Jakub Dubec, [BACKBONE s.r.o.](https://www.backbone.sk/en/) &
233226
[contributors](https://github.com/Sibyx/django_api_forms/graphs/contributors).

0 commit comments

Comments
 (0)