# DRF NESTED FORMS
A library that parses nested json or form data to python object.
[![Build Status](https://travis-ci.com/emperorDuke/nested_formdata.svg?branch=master)](https://travis-ci.com/emperorDuke/nested_formdata)
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/emperorDuke/nested_formdata)](https://github.com/emperorDuke/nested_formdata/releases)
[![PyPI - License](https://img.shields.io/pypi/l/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)
[![PyPI - Django Version](https://img.shields.io/pypi/djversions/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)
[![PyPI](https://img.shields.io/pypi/v/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)
# Overview
SPA's, sometimes send nested form data or json as requests encoded by some javascript libraries like [json-form-data](https://github.com/hyperatom/json-form-data#readme) which can be difficult to handle due to the key naming conventions. This library helps to eliminate that difficulty, by parsing that nested requests into a more predictable python object that can be used by libraries like [drf_writable_nested](https://github.com/beda-software/drf-writable-nested#readme) or used directly in the code.
# Installation
It is available via pypi:
```
pip install drf_nested_forms
```
# Usage
The utiliy class can be used directly in any part of the code.
```python
from drf_nested_forms.utils import NestedForm
data = {
'item[attribute][0][user_type]': 'size',
'item[attribute][1][user_type]': '',
'item[verbose][]': '',
'item[variant][vendor_metric]': '[]',
'item[variant][metric_verbose_name]': 'Large',
'item[foo][baaz]': 'null',
}
options = {
'allow_blank': True,
'allow_empty': False
}
```
## Note
`.is_nested()` should be called before accessing the `.data`
```python
form = NestedForm(data, **options)
form.is_nested(raise_exception=True)
```
The parsed result will look like below:
```python
print(form.data)
data = {
'item': {
'attribute': [
{'user_type': 'size'},
{'user_type': ''}
],
'verbose': [''],
'variant': {
'vendor_metric': None,
'metric_verbose_name': 'Large'
},
'foo': { 'baaz': None }
}
}
```
# DRF Integration
The parser is used with a djangorestframework view classes.
## Parser classes supported:
- `NestedMultiPartParser`: is a default DRF multipart parser that suppport parsing nested form data.
- `NestedJSONParser`: is a default DRF JSONParser that support parsing nested json request.
Add the parser to your django settings file
```python
#...
REST_FRAMEWORK = {
DEFAULT_PARSER_CLASSES = [
# nested parser are just default DRF parsers with extended
# functionalities to support nested
'drf_nested_forms.parsers.NestedMultiPartParser',
'drf_nested_forms.parsers.NestedJSONPartParser',
'rest_framework.parsers.FormParser',
# so this settings will work in respective of a nested request
# or not
]
}
#...
```
To change default settings of the parsers, add `OPTIONS` to `NESTED_FORM_PARSER` with the new settings to your django settings file
```python
#..
NESTED_FORM_PARSER = {
'OPTIONS': {
'allow_empty': False,
'allow_blank': True
}
}
#...
```
The parsers can also be used directly in a `rest_framework` view class
```python
from rest_framework.views import APIView
from rest_framework.parsers import FormParser
from rest_framework.response import Response
from drf_nested_forms.parsers import NestedMultiPartParser, NestedJSONParser
class TestMultiPartParserView(APIView):
parser_classes = (NestedMultiPartParser, FormParser)
def post(self, request):
return Response(data=request.data, status=200)
# or
class TestJSONParserView(APIView):
parser_classes = (NestedJSONParser, FormParser)
def post(self, request):
return Response(data=request.data, status=200)
```
For example, a form or JSON data with nested params like below can be posted to any of the above drf view:
```python
data = {
'[0][attribute]': 'true',
'[0][verbose][0]': 'bazz',
'[0][verbose][1]': 'foo',
'[0][variant][vendor_metric]': 'null',
'[0][variant][metric_verbose_name]': 'Large',
'[0][foo][baaz]': 'false',
'[1][attribute]': 'size',
'[1][verbose]': '[]',
'[1][variant][vendor_metric]': '{}',
'[1][variant][metric_verbose_name][foo][baaz][]': 'Large',
'[1][foo][baaz]': '',
'[1][logo]': '235'
}
```
after being parsed, the `request.data` will look like this:
```python
print(request.data)
data = [
{
'attribute': True,
'verbose': ['bazz', 'foo'],
'variant': {
'vendor_metric': None,
'metric_verbose_name': 'Large'
},
'foo': { 'baaz': False }
},
{
'attribute': 'size',
'verbose': None,
'variant': {
'vendor_metric': None,
'metric_verbose_name': { 'foo': { 'baaz': ['Large'] } }
},
'foo': { 'baaz': '' },
'logo': 235
}
]
```
# Options
| Option | Default | Description |
| ----------- | ------- | ------------------------------------- |
| allow_blank | `True` | shows empty string `''` in the object |
| allow_empty | `False` | shows empty `list` or `dict` object |
# Running Tests
To run the current test suite, execute the following from the root of the project:
```
python runtests.py
```
# Author
@Copyright 2021, Duke Effiom
Raw data
{
"_id": null,
"home_page": "http://github.com/emperorDuke/nested_formdata",
"name": "drf-nested-forms",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "drf,nested form,html_formsdrf_nested_forms,restframework,nested json",
"author": "Duke Effiom",
"author_email": "effiomduke@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/c6/ae/ef5de4614e0f1ec8941890990cec54e580665a4382b43a2da77a1d0ab535/drf_nested_forms-1.1.8.tar.gz",
"platform": null,
"description": "# DRF NESTED FORMS\r\n\r\nA library that parses nested json or form data to python object.\r\n\r\n[![Build Status](https://travis-ci.com/emperorDuke/nested_formdata.svg?branch=master)](https://travis-ci.com/emperorDuke/nested_formdata)\r\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/emperorDuke/nested_formdata)](https://github.com/emperorDuke/nested_formdata/releases)\r\n[![PyPI - License](https://img.shields.io/pypi/l/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)\r\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)\r\n[![PyPI - Django Version](https://img.shields.io/pypi/djversions/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)\r\n[![PyPI](https://img.shields.io/pypi/v/drf_nested_forms)](https://pypi.python.org/pypi/drf-nested-forms)\r\n\r\n\r\n# Overview\r\n\r\nSPA's, sometimes send nested form data or json as requests encoded by some javascript libraries like [json-form-data](https://github.com/hyperatom/json-form-data#readme) which can be difficult to handle due to the key naming conventions. This library helps to eliminate that difficulty, by parsing that nested requests into a more predictable python object that can be used by libraries like [drf_writable_nested](https://github.com/beda-software/drf-writable-nested#readme) or used directly in the code.\r\n\r\n# Installation\r\n\r\nIt is available via pypi:\r\n\r\n```\r\npip install drf_nested_forms\r\n```\r\n\r\n# Usage\r\n\r\nThe utiliy class can be used directly in any part of the code.\r\n\r\n```python\r\n\r\nfrom drf_nested_forms.utils import NestedForm\r\n\r\ndata = {\r\n 'item[attribute][0][user_type]': 'size',\r\n 'item[attribute][1][user_type]': '',\r\n 'item[verbose][]': '',\r\n 'item[variant][vendor_metric]': '[]',\r\n 'item[variant][metric_verbose_name]': 'Large',\r\n 'item[foo][baaz]': 'null',\r\n}\r\n\r\noptions = {\r\n 'allow_blank': True,\r\n 'allow_empty': False\r\n}\r\n```\r\n\r\n## Note\r\n\r\n`.is_nested()` should be called before accessing the `.data`\r\n\r\n```python\r\nform = NestedForm(data, **options)\r\nform.is_nested(raise_exception=True)\r\n```\r\n\r\nThe parsed result will look like below:\r\n\r\n```python\r\nprint(form.data)\r\n\r\ndata = {\r\n 'item': {\r\n 'attribute': [\r\n {'user_type': 'size'},\r\n {'user_type': ''}\r\n ],\r\n 'verbose': [''],\r\n 'variant': {\r\n 'vendor_metric': None,\r\n 'metric_verbose_name': 'Large'\r\n },\r\n 'foo': { 'baaz': None }\r\n }\r\n}\r\n```\r\n\r\n# DRF Integration\r\n\r\nThe parser is used with a djangorestframework view classes.\r\n\r\n## Parser classes supported:\r\n\r\n- `NestedMultiPartParser`: is a default DRF multipart parser that suppport parsing nested form data.\r\n- `NestedJSONParser`: is a default DRF JSONParser that support parsing nested json request.\r\n\r\nAdd the parser to your django settings file\r\n\r\n```python\r\n\r\n#...\r\n\r\nREST_FRAMEWORK = {\r\n DEFAULT_PARSER_CLASSES = [\r\n # nested parser are just default DRF parsers with extended\r\n # functionalities to support nested\r\n\r\n 'drf_nested_forms.parsers.NestedMultiPartParser',\r\n 'drf_nested_forms.parsers.NestedJSONPartParser',\r\n 'rest_framework.parsers.FormParser',\r\n\r\n # so this settings will work in respective of a nested request\r\n # or not\r\n ]\r\n}\r\n\r\n#...\r\n\r\n```\r\n\r\nTo change default settings of the parsers, add `OPTIONS` to `NESTED_FORM_PARSER` with the new settings to your django settings file\r\n\r\n```python\r\n#..\r\n\r\nNESTED_FORM_PARSER = {\r\n 'OPTIONS': {\r\n 'allow_empty': False,\r\n 'allow_blank': True\r\n }\r\n}\r\n\r\n#...\r\n\r\n```\r\n\r\nThe parsers can also be used directly in a `rest_framework` view class\r\n\r\n```python\r\n\r\nfrom rest_framework.views import APIView\r\nfrom rest_framework.parsers import FormParser\r\nfrom rest_framework.response import Response\r\n\r\nfrom drf_nested_forms.parsers import NestedMultiPartParser, NestedJSONParser\r\n\r\nclass TestMultiPartParserView(APIView):\r\n parser_classes = (NestedMultiPartParser, FormParser)\r\n\r\n def post(self, request):\r\n return Response(data=request.data, status=200)\r\n\r\n# or\r\n\r\nclass TestJSONParserView(APIView):\r\n parser_classes = (NestedJSONParser, FormParser)\r\n\r\n def post(self, request):\r\n return Response(data=request.data, status=200)\r\n\r\n```\r\n\r\nFor example, a form or JSON data with nested params like below can be posted to any of the above drf view:\r\n\r\n```python\r\ndata = {\r\n '[0][attribute]': 'true',\r\n '[0][verbose][0]': 'bazz',\r\n '[0][verbose][1]': 'foo',\r\n '[0][variant][vendor_metric]': 'null',\r\n '[0][variant][metric_verbose_name]': 'Large',\r\n '[0][foo][baaz]': 'false',\r\n '[1][attribute]': 'size',\r\n '[1][verbose]': '[]',\r\n '[1][variant][vendor_metric]': '{}',\r\n '[1][variant][metric_verbose_name][foo][baaz][]': 'Large',\r\n '[1][foo][baaz]': '',\r\n '[1][logo]': '235'\r\n}\r\n```\r\n\r\nafter being parsed, the `request.data` will look like this:\r\n\r\n```python\r\nprint(request.data)\r\n\r\ndata = [\r\n {\r\n 'attribute': True,\r\n 'verbose': ['bazz', 'foo'],\r\n 'variant': {\r\n 'vendor_metric': None,\r\n 'metric_verbose_name': 'Large'\r\n },\r\n 'foo': { 'baaz': False }\r\n },\r\n {\r\n 'attribute': 'size',\r\n 'verbose': None,\r\n 'variant': {\r\n 'vendor_metric': None,\r\n 'metric_verbose_name': { 'foo': { 'baaz': ['Large'] } }\r\n },\r\n 'foo': { 'baaz': '' },\r\n 'logo': 235\r\n }\r\n]\r\n```\r\n\r\n# Options\r\n\r\n| Option | Default | Description |\r\n| ----------- | ------- | ------------------------------------- |\r\n| allow_blank | `True` | shows empty string `''` in the object |\r\n| allow_empty | `False` | shows empty `list` or `dict` object |\r\n\r\n# Running Tests\r\n\r\nTo run the current test suite, execute the following from the root of the project:\r\n\r\n```\r\npython runtests.py\r\n```\r\n\r\n# Author\r\n\r\n@Copyright 2021, Duke Effiom\r\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A library that parses nested json or form data to python object",
"version": "1.1.8",
"project_urls": {
"Download": "http://github.com/emperorDuke/nested_formdata/archive/v1.1.8.tar.gz",
"Homepage": "http://github.com/emperorDuke/nested_formdata"
},
"split_keywords": [
"drf",
"nested form",
"html_formsdrf_nested_forms",
"restframework",
"nested json"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "fc7e339ba9cf19f02202538ad51619855b48ab9d0e6d066c89641cad976e71b1",
"md5": "a8fdc585dad9e3747c5321d2a34dd610",
"sha256": "087fe5b6df32c95fcc605962a61109df08e7059580ef3adc6ba9de68d375b57b"
},
"downloads": -1,
"filename": "drf_nested_forms-1.1.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a8fdc585dad9e3747c5321d2a34dd610",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 10079,
"upload_time": "2023-10-25T18:56:25",
"upload_time_iso_8601": "2023-10-25T18:56:25.392532Z",
"url": "https://files.pythonhosted.org/packages/fc/7e/339ba9cf19f02202538ad51619855b48ab9d0e6d066c89641cad976e71b1/drf_nested_forms-1.1.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c6aeef5de4614e0f1ec8941890990cec54e580665a4382b43a2da77a1d0ab535",
"md5": "5c7c55c2ec7682c5083e344698c4dc11",
"sha256": "5d086b7e68c56915f430bd5a1beffe0c79a6ac48424732287d056c5322e65f93"
},
"downloads": -1,
"filename": "drf_nested_forms-1.1.8.tar.gz",
"has_sig": false,
"md5_digest": "5c7c55c2ec7682c5083e344698c4dc11",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 12928,
"upload_time": "2023-10-25T18:56:33",
"upload_time_iso_8601": "2023-10-25T18:56:33.053957Z",
"url": "https://files.pythonhosted.org/packages/c6/ae/ef5de4614e0f1ec8941890990cec54e580665a4382b43a2da77a1d0ab535/drf_nested_forms-1.1.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-10-25 18:56:33",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "emperorDuke",
"github_project": "nested_formdata",
"travis_ci": true,
"coveralls": false,
"github_actions": false,
"requirements": [],
"lcname": "drf-nested-forms"
}