jsonapi-transformer 1.0.0

Creator: bradpython12

Last updated:

Add to Cart

Description:

jsonapitransformer 1.0.0

jsonapi-transformer
jsonapi-transformer is a Python library for producing, consuming, and manipulating JSON:API data. It makes developing with JSON:API more manageable by converting from and to JSON:API-formatted data.
This library follows the JSON:API v1.1 release candidate specification.
Quick Start
The following example is based on Example 7.2.2.4 of the JSON:API v1.1 specification.
import json
from transformers import JSONAPITransformer, from_jsonapi_generic

Data can be manually set on a JSONAPITransformer instance...
transformer = JSONAPITransformer(
type_name="articles",
id="1",
attributes={
"title": "Rails is Omakase",
},
relationships={
"author": JSONAPITransformer(
type_name="people",
id="9",
)
}
)

... and then converted to jsonapi.
>>> jsonapi = transformer.to_jsonapi()
>>> print(json.dumps(jsonapi, indent=4))
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"data": {
"type": "people",
"id": "9"
}
}
}
}
}

If you already have jsonapi data, you can load that into a JSONAPITransformer instance as well.
>>> transformer = from_jsonapi_generic(jsonapi)
>>> print(transformer.id)
1

>>> print(transformer.type_name)
articles

Installation
jsonapi-transformer is available on PyPI.
pip install --upgrade pip
pip install jsonapi-transformer

jsonapi-transformer supports Python 3.7+.
Notable Features
Code snippets in this section use the Quick Start example above as a starting point.

Support for both id and lid.
The included list is generated automatically from items in relationships when to_jsonapi() is called -- there's no need to manipulate an item in both the included list and an object's relationships.
Convenience method to get an attribute or relationship or a default if the key is not found.
# First, try `transformer.attributes["greeting"]` -- greeting is not found!
# Next, try `transformer.relationships["greeting"]` -- greeting is not found!
# Finally, the default is returned.
>>> default = "hello"
>>> greeting = transformer.get("greeting", default)
>>> print(greeting)
hello


Convenience accessor for getting attributes and relationships using [] notation.
# First, try `transformer.attributes["title"]` -- title is found!
>>> print(transformer["title"])
Rails is Omakase

# First, try `transformer.attributes["author"]` -- author is not found!
# Next, try `transformer.relationships["author"]` -- author is found!
>>> author = transformer["author"]
>>> print(author.to_jsonapi())
{"data": {"type": "people", "id": "9"}}


Convenience accessor for setting attributes using [] notation.
>>> transformer["my_new_attribute"] = "hello"


Convenience accessor for deleting attributes using [] notation.
>>> del transformer["my_new_attribute"]


Convenience accessor for contains in attributes and relationships using the in keyword.
>>> "title" in transformer
True

>>> "author" in transformer
True

>>> "book" in transformer
False


Deep equality test between transformers, comparing all of:

type_name
id
lid
attributes
relationships
included

>>> book = JSONAPITransformer(type_name="book", id="1")
>>> article = JSONAPITransformer(type_name="article", id="1")
>>> book == article
False


Derived transformer classes can contain business logic.
from transformers import JSONAPITransformer, JSONAPITransformerFactory


class People(JSONAPITransformer):
type_name = "people"

@property
def full_name(self):
"""Custom business logic for this class, keyed on `type_name`."""
return f"{self['last_name'], self['first_name']}"


jsonapi = {
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"data": {
"type": "people",
"id": "9"
}
}
}
},
"included": [
{
"type": "people",
"id": "9",
"attributes": {
"first_name": "Jon",
"last_name": "George"
}
}
]
}

# By using a factory instead of `from_jsonapi_generic(...)`, we can provide a list
# of classes that are instantiated based on the `type` field in the jsonapi data.
# Since we didn't provide a class for the "articles" type, setting `allow_generic`
# to True allows the "articles" object to load as a generic JSONAPITransformer
factory = JSONAPITransformerFactory([People], allow_generic=True)

>>> transformer = factory.from_jsonapi(jsonapi)
>>> print(type(transformer))
<class 'JSONAPITransformer'>

>>> author = transformer["author"]
>>> print(type(author))
<class 'People'>

# Access business logic unique to the `People` class.
>>> print(author.full_name)
George, Jon


Lists of JSON:API objects can reside in the same document, and any shared relationships are de-duplicated in the included list.
from transformers import JSONAPIListTransformer, JSONAPITransformer, from_jsonapi_generic


article1 = JSONAPITransformer(
type_name="articles",
id="1",
attributes={
"title": "Rails is Omakase",
},
relationships={
"author": JSONAPITransformer(
type_name="people",
id="9",
attributes={
"first_name": "Jon",
"last_name": "George"
}
)
}
)

article2 = JSONAPITransformer(
type_name="articles",
id="2",
attributes={
"title": "Now is better than never.",
},
relationships={
"author": JSONAPITransformer(
type_name="people",
id="9",
attributes={
"first_name": "Jon",
"last_name": "George"
}
)
}
)

>>> transformer = JSONAPIListTransformer([article1, article2])
>>> print(type(transformer))
<class 'JSONAPIListTransformer'>

# The `included` section is deduplicated.
>>> jsonapi = transformer.to_jsonapi()
>>> print(json.dumps(jsonapi, indent=4))
{
"data": [
{
"type": "articles",
"id": "1",
"attributes": {
"title": "Rails is Omakase"
},
"relationships": {
"author": {
"data": {
"type": "people",
"id": "9"
}
}
}
},
{
"type": "articles",
"id": "2",
"attributes": {
"title": "Now is better than never."
},
"relationships": {
"author": {
"data": {
"type": "people",
"id": "9"
}
}
}
}
],
"included": [
{
"type": "people",
"id": "9",
"attributes": {
"first_name": "Jon",
"last_name": "George"
}
}
]
}

# Convert back to transformers.
>>> transformers = from_jsonapi_generic(jsonapi)
>>> print(type(transformers))
<class 'list'>

>>> for x in transformers:
... print(type(x))
<class 'JSONAPITransformer'>
<class 'JSONAPITransformer'>



Unsupported
These top-level members of the JSON:API specification are unsupported:

errors
jsonapi
links
meta

Development Locally
Contributors to jsonapi-transformer can install an editable version of this library after cloning the repository:
pip install --upgrade pip
pip install -e .[tests,dev]

Development with Docker
Contributors to jsonapi-transformer can do all development tasks within a Docker container:
Build
Don't forget the trailing .!!!
docker build -f Dockerfile.testing \
--build-arg PYTHON_VERSION=3.10 \
-t jsonapi-transformer:latest \
.

Run Tests with Coverage
docker run --rm -it \
--entrypoint pytest \
jsonapi-transformer:latest \
--cov --cov-report=term-missing

Run Type Checking
docker run --rm -it \
--entrypoint mypy \
jsonapi-transformer:latest \
--show-error-context \
--show-error-codes \
--strict \
src

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.