Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Django Forms approach in the processing of a RESTful HTTP request payload (especially for content type like JSON or MessagePack) without HTML front-end.
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:
Form
),form = Form.create_from_request(request)
),form.is_valid()
,form.clean_data
property.I wanted to keep:
So I have decided to create a simple Python package to cover all my expectations.
# Using pip
pip install django-api-forms
# Using poetry
peotry add django-api-forms
# Using setup.py
python setup.py install
Optional:
# msgpack support (for requests with Content-Type: application/x-msgpack)
peotry add msgpack
# ImageField support
peotry 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'
}
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(fields.DateTimeField())
def clean_year(self):
if self.cleaned_data['year'] == 1992:
raise ValidationError("Year 1992 is forbidden!", 'forbidden-value')
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!")
return self.cleaned_data
"""
Django view example
"""
def create_album(request):
form = AlbumForm.create_from_request(request)
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.
# install all dependencies
poetry install
# run code-style check
poetry run flake8 .
# run the tests
poetry run python runtests.py
Navicat Premium is a super awesome database development tool for cool kids in the neighborhood that allows you to simultaneously connect to MySQL, MariaDB, MongoDB, SQL Server, Oracle, PostgreSQL, and SQLite databases from a single application. Compatible with cloud databases like Amazon RDS, Amazon Aurora, Amazon Redshift, Microsoft Azure, Oracle Cloud, Google Cloud and MongoDB Atlas. You can quickly and easily build, manage and maintain your databases.
Especially, I have to recommend their database design tool. Many thanks Navicat for supporting Open Source projects 🌈.
Made with ❤️ and ☕️ by Jakub Dubec, BACKBONE s.r.o. & contributors.
mapping
field_type_strategy
and field_strategy
DictionaryField
was unable to raise validation errors for keysAnniversary release 🥳
fill
method is deprecated and replaced by populate
Settings
object introduced (form.settings
)DJANGO_API_FORMS_PARSERS
settingmime
argument in FileField
is supposed to be a tuple
FieldList
and FormFieldList
now supports optional min/max constrains using min_length
/max_length
ModelForm
class introduced (experimental, initial support - not recommended for production)fill_method
introducedImage.verify()
call in ImageField::to_python
ApiFormException('No clean data provided! Try to call is_valid() first.')
was incorrectly raised if
request payload was empty during Form::fill
method callclean_data
property is by default None
instead of empty dictionaryclean_
methods returning values resolved as False (False
, None
, ''
)ModelMultipleChoiceField
in Form::fill()
ModelChoiceField
is used in Form::fill()
DjangoApiFormsConfig
is createdpytest
in project (we don't need it)FileField.content_type
introduced (contains mime)FileField
and ImageField
introducedsetup.py
for optional Pillow
and msgpack
dependenciesForm::fill()
method for primitive data types. Introduced IgnoreFillMixin
BaseForm._request
property introduced (now it's possible to use request in clean_
methods)Content-Type
handling if charset
or boundary
is presentApiFormException
.setup.py
(failing on Windows OS).django_api_forms
to django-api-forms
(cosmetic change without effect)django_request_formatter
to django_api_forms
django_api_forms
BooleanField
introducedInvalid value
as ValidationError
not as a string
Invalid value
error message, if there is AttributeError
, TypeError
, ValueError
0.5.5
but this time for real__version__.py
to version.py
FieldList
and FormFieldList
msgpack`` dependency to
setup.py`AnyField
FormField
if value is not required and emptyEnumField
even if it's not marked as requireddjango.form.fields
if possiblekwargs
propagation from release 0.3.0
django.forms
compatible (e.g. form.validate_{key}()
-> form.clean_{key}()
)FieldList
raises ValidationError
instead of RuntimeException
if there is a type in validationFieldList
returns values instead of None
DictionaryField
Form
has no attribute self._data
NoneType
errorUUIDField
DictionaryField
kwargs
from Form.is_valid()
to Form.validate()
and Form.validate_{key}()
methodsto_python()
in FormFieldListForm.validate()
replaced by Form.is_valid()
Form.validate()
is now used as a last step of form validation and it's aimed to be overwritten if
neededForm::fill()
ValidationError
Form:errors
DeclarativeFieldsMetaclass
because of custom Field
classForm::payload
default_validators
in Field
validation_{field}
methods in Form
(initial support)EnumField
Form
classCharField
IntegerField
FloatField
DecimalField
DateField
TimeField
DateTimeField
DurationField
RegexField
EmailField
BooleanField
RegexField
FieldList
FormField
FormFieldList
FAQs
Declarative Django request validation for RESTful APIs
We found that django-api-forms demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.