Skip to content
This repository was archived by the owner on Dec 16, 2025. It is now read-only.

Commit b783d87

Browse files
committed
enhance documentation
1 parent 46a1478 commit b783d87

15 files changed

+880
-148
lines changed

README.rst

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ Install
1818

1919
pip install Flask-REST-JSONAPI
2020

21-
Quickstart
22-
==========
21+
A minimal api
22+
=============
2323

2424
.. code-block:: python
2525
@@ -37,36 +37,31 @@ Quickstart
3737
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
3838
db = SQLAlchemy(app)
3939
40-
4140
# Create model
4241
class Person(db.Model):
4342
id = db.Column(db.Integer, primary_key=True)
44-
name = db.Column(db.Unicode, unique=True)
45-
birth_date = db.Column(db.Date)
43+
name = db.Column(db.String)
4644
4745
# Create the database.
4846
db.create_all()
4947
50-
5148
# Create schema
5249
class PersonSchema(Schema):
5350
class Meta:
5451
type_ = 'person'
5552
self_view = 'person_detail'
5653
self_view_kwargs = {'id': '<id>'}
54+
self_view_many = 'person_list'
5755
5856
id = fields.Str(dump_only=True)
5957
name = fields.Str()
60-
birth_date = fields.Date()
6158
62-
63-
# Create resource
59+
# Create resource managers
6460
class PersonList(ResourceList):
6561
schema = PersonSchema
6662
data_layer = {'session': db.session,
6763
'model': Person}
6864
69-
7065
class PersonDetail(ResourceDetail):
7166
schema = PersonSchema
7267
data_layer = {'session': db.session,

docs/data_layer.rst

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,118 @@ Data layer
55

66
.. currentmodule:: flask_rest_jsonapi
77

8-
Coming soon. If you want to learn let's see tests or api reference.
8+
| The data layer is a CRUD interface between resource manager and data. It is a very flexible system to use any ORM or data storage. You can even create a data layer that use multiple ORMs and data storage to manage your own objects. The data layer implements a CRUD interface for objects and relationships. It also manage pagination, filtering and sorting.
9+
|
10+
| Flask-REST-JSONAPI got a full featured data layer that use the popular ORM `SQLAlchemy <https://www.sqlalchemy.org/>`_.
11+
12+
.. note::
13+
14+
The default data layer used by a resource manager is the SQLAlchemy one. So if you want to use it, you don't have to specify the class of the data layer in the resource manager
15+
16+
To configure the data layer you have to set his required parameters in the resource manager.
17+
18+
Example:
19+
20+
.. code-block:: python
21+
22+
from flask_rest_jsonapi import ResourceList
23+
from your_project.schemas import PersonSchema
24+
from your_project.models import Person
25+
26+
class PersonList(ResourceList):
27+
schema = PersonSchema
28+
data_layer = {'session': db.session,
29+
'model': Person}
30+
31+
You can also plug additional methods to your data layer in the resource manager. There is 2 kind of additional methods:
32+
* query: the "query" additional method takes view_kwargs as parameter and return an alternative query to retrieve the collection of objects in the get method of the ResourceList manager.
33+
* pre / post process methods: all CRUD and relationship(s) operations have a pre / post process methods. Thanks to it you can make additional work before and after each operations of the data layer. Parameters of each pre / post process methods are available in the `flask_rest_jsonapi.data_layers.base.Base <https://github.com/miLibris/flask-rest-jsonapi/blob/master/flask_rest_jsonapi/data_layers/base.py>`_ base class.
34+
35+
Example:
36+
37+
.. code-block:: python
38+
39+
from sqlalchemy.orm.exc import NoResultFound
40+
from flask_rest_jsonapi import ResourceList
41+
from flask_rest_jsonapi.exceptions import ObjectNotFound
42+
from your_project.models import Computer, Person
43+
44+
class ComputerList(ResourceList):
45+
def query(self, view_kwargs):
46+
query_ = self.session.query(Computer)
47+
if view_kwargs.get('id') is not None:
48+
try:
49+
self.session.query(Person).filter_by(id=view_kwargs['id']).one()
50+
except NoResultFound:
51+
raise ObjectNotFound({'parameter': 'id'}, "Person: {} not found".format(view_kwargs['id']))
52+
else:
53+
query_ = query_.join(Person).filter(Person.id == view_kwargs['id'])
54+
return query_
55+
56+
def before_create_object(self, data, view_kwargs):
57+
if view_kwargs.get('id') is not None:
58+
person = self.session.query(Person).filter_by(id=view_kwargs['id']).one()
59+
data['person_id'] = person.id
60+
61+
schema = ComputerSchema
62+
data_layer = {'session': db.session,
63+
'model': Computer,
64+
'methods': {'query': query,
65+
'before_create_object': before_create_object}}
66+
67+
.. note::
68+
69+
You don't have to declare additonals data layer methods in the resource manager. You can declare them in a dedicated module or in the declaration of the model.
70+
71+
Example:
72+
73+
.. code-block:: python
74+
75+
from sqlalchemy.orm.exc import NoResultFound
76+
from flask_rest_jsonapi import ResourceList
77+
from flask_rest_jsonapi.exceptions import ObjectNotFound
78+
from your_project.models import Computer, Person
79+
from your_project.additional_methods.computer import before_create_object
80+
81+
class ComputerList(ResourceList):
82+
schema = ComputerSchema
83+
data_layer = {'session': db.session,
84+
'model': Computer,
85+
'methods': {'query': Computer.query,
86+
'before_create_object': before_create_object}}
87+
88+
SQLAlchemy
89+
----------
90+
91+
Required parameters:
92+
93+
:session: the session used by the data layer
94+
:model: the model used by the data layer
95+
96+
Optional parameters:
97+
98+
:id_field: the field used as identifier field instead of the primary key of the model
99+
:url_field: the name of the parameter in the route to get value to filter with. Instead "id" is used.
100+
101+
Custom data layer
102+
-----------------
103+
104+
Like I said previously you can create and use your own data layer. A custom data layer must inherit from `flask_rest_jsonapi.data_layers.base.Base <https://github.com/miLibris/flask-rest-jsonapi/blob/master/flask_rest_jsonapi/data_layers/base.py>`_. You can see the full scope of possibilities of a data layer in this base class.
105+
106+
Usage example:
107+
108+
.. code-block:: python
109+
110+
from flask_rest_jsonapi import ResourceList
111+
from your_project.schemas import PersonSchema
112+
from your_project_data_layers import MyCustomDataLayer
113+
114+
class PersonList(ResourceList):
115+
schema = PersonSchema
116+
data_layer = {'class': MyCustomDataLayer,
117+
'param_1': value_1,
118+
'param_2': value_2}
119+
120+
.. note::
121+
122+
All items except "class" in the data_layer dict of the resource manager will be plugged as instance attributes of the data layer. It is easier to use in the data layer.

docs/errors.rst

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,77 @@ Errors
55

66
.. currentmodule:: flask_rest_jsonapi
77

8-
Coming soon. If you want to learn let's see tests or api reference.
8+
JSONAPI 1.0 specification recommand to return errors like that:
9+
10+
.. sourcecode:: http
11+
12+
HTTP/1.1 422 Unprocessable Entity
13+
Content-Type: application/vnd.api+json
14+
15+
{
16+
"errors": [
17+
{
18+
"status": "422",
19+
"source": {
20+
"pointer":"/data/attributes/first-name"
21+
},
22+
"title": "Invalid Attribute",
23+
"detail": "First name must contain at least three characters."
24+
}
25+
],
26+
"jsonapi": {
27+
"version": "1.0"
28+
}
29+
}
30+
31+
The "source" field gives information about the error if it is located in data provided or in querystring parameter.
32+
33+
The previous example displays error located in data provided instead of this next example displays error located in querystring parameter "include":
34+
35+
.. sourcecode:: http
36+
37+
HTTP/1.1 400 Bad Request
38+
Content-Type: application/vnd.api+json
39+
40+
{
41+
"errors": [
42+
{
43+
"status": "400",
44+
"source": {
45+
"parameter": "include"
46+
},
47+
"title": "BadRequest",
48+
"detail": "Include parameter is invalid"
49+
}
50+
],
51+
"jsonapi": {
52+
"version": "1.0"
53+
}
54+
}
55+
56+
Flask-REST-JSONAPI provides two kind of helpers to achieve error displaying:
57+
58+
| * **the errors module**: you can import jsonapi_errors from the errors module to create the structure of a list of errors according to JSONAPI 1.0 specification
59+
|
60+
| * **the exceptions module**: you can import lot of exceptions from this module that helps you to raise exceptions that will be well formatted according to JSONAPI 1.0 specification
61+
62+
When you create custom code for your api I recommand to use exceptions from exceptions module of Flask-REST-JSONAPI to raise errors because JsonApiException based exceptions are catched and rendered according to JSONAPI 1.0 specification.
63+
64+
Example:
65+
66+
.. code-block:: python
67+
68+
# all required imports are not displayed in this example
69+
from flask_rest_jsonapi.exceptions import ObjectNotFound
70+
71+
class ComputerList(ResourceList):
72+
def query(self, view_kwargs):
73+
query_ = self.session.query(Computer)
74+
if view_kwargs.get('id') is not None:
75+
try:
76+
self.session.query(Person).filter_by(id=view_kwargs['id']).one()
77+
except NoResultFound:
78+
raise ObjectNotFound({'parameter': 'id'}, "Person: {} not found".format(view_kwargs['id']))
79+
else:
80+
query_ = query_.join(Person).filter(Person.id == view_kwargs['id'])
81+
return query_

docs/filtering.rst

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ Filtering
55

66
.. currentmodule:: flask_rest_jsonapi
77

8-
Flask-REST-JSONAPI as a very flexible filtering system. The filtering system is completely related to the data layer used by the resource. I will explain the filtering interface for SQLAlchemy data layer but you can use the same interface to your filtering implementation of your custom data layer. The only requirement is that you have to use the filter querystring parameter to make filtering according to JSONAPI 1.0 specification.
8+
Flask-REST-JSONAPI as a very flexible filtering system. The filtering system is completely related to the data layer used by the ResourceList manager. I will explain the filtering interface for SQLAlchemy data layer but you can use the same interface to your filtering implementation of your custom data layer. The only requirement is that you have to use the "filter" querystring parameter to make filtering according to JSONAPI 1.0 specification.
99

1010
.. note::
1111

12-
Urls examples are not urlencoded for a better readability
12+
Examples are not urlencoded for a better readability
1313

1414
SQLAlchemy
1515
----------
@@ -27,7 +27,7 @@ In this example we want to retrieve persons which name is John. So we can see th
2727

2828
:name: the name of the field you want to filter on
2929
:op: the operation you want to use (all sqlalchemy operations are available)
30-
:val: the value that you want to compare. You can replace this by field if you want to compare against the value of an other field
30+
:val: the value that you want to compare. You can replace this by "field" if you want to compare against the value of an other field
3131

3232
Example with field:
3333

@@ -57,7 +57,7 @@ If you want to filter through relationships you can do that:
5757

5858
.. note ::
5959
60-
When you filter on relationships use "any" operator for to many relationships and "has" operator for to one relationships.
60+
When you filter on relationships use "any" operator for "to many" relationships and "has" operator for "to one" relationships.
6161
6262
There is a shortcut to achieve the same filter:
6363

@@ -106,6 +106,8 @@ You can also use boolean combinaison of operations:
106106
] HTTP/1.1
107107
Accept: application/vnd.api+json
108108

109+
Availables operators depend on field type
110+
109111
Common available operators:
110112

111113
* any: used to filter on to many relationships
@@ -128,3 +130,7 @@ Common available operators:
128130
* notin_: check if field is not in a list of values
129131
* notlike: check if field does not contains a string
130132
* startswith: check if field starts with a string
133+
134+
.. note::
135+
136+
Availables operators depend on field type in your model

0 commit comments

Comments
 (0)