Skip to content

Commit bb8472b

Browse files
authored
Merge pull request AdCombo#48 from AdCombo/develop
Update api, api_nested examples, update docs
2 parents b6303f3 + fc9a549 commit bb8472b

File tree

75 files changed

+1918
-960
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1918
-960
lines changed

README.rst

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
11
.. image:: https://github.com/AdCombo/flask-combo-jsonapi/workflows/Python%20tests%20and%20coverage/badge.svg
2-
:target: https://github.com/AdCombo/flask-combo-jsonapi/actions
2+
:alt: flask-combo-jsonapi actions
3+
:target: https://github.com/AdCombo/flask-combo-jsonapi/actions
34
.. image:: https://coveralls.io/repos/github/AdCombo/flask-combo-jsonapi/badge.svg
4-
:target: https://coveralls.io/github/AdCombo/flask-combo-jsonapi
5+
:alt: flask-combo-jsonapi coverage
6+
:target: https://coveralls.io/github/AdCombo/flask-combo-jsonapi
7+
.. image:: https://img.shields.io/pypi/v/flask-combo-jsonapi.svg
8+
:alt: PyPI
9+
:target: http://pypi.org/p/flask-combo-jsonapi
510

611

712
Flask-COMBO-JSONAPI
813
###################
914

1015
Flask-COMBO-JSONAPI is a flask extension for building REST APIs. It combines the power of `Flask-Restless <https://flask-restless.readthedocs.io/>`_ and the flexibility of `Flask-RESTful <https://flask-restful.readthedocs.io/>`_ around a strong specification `JSONAPI 1.0 <http://jsonapi.org/>`_. This framework is designed to quickly build REST APIs and fit the complexity of real life projects with legacy data and multiple data storages.
1116

12-
The main goal is to make it flexible using `plugin system <https://github.com/AdCombo/combojsonapi/blob/develop/docs/en/create_plugins.rst>`_
17+
The main goal is to make it flexible using `plugin system <https://combojsonapi.readthedocs.io/>`_
1318

1419

1520
Install
1621
=======
1722

1823
pip install Flask-COMBO-JSONAPI
1924

20-
Installation from pypi is not ready yet. Refer to the `installation manual <https://github.com/AdCombo/flask-combo-jsonapi/blob/develop/docs/installation.rst/>`_
21-
2225

2326
A minimal API
2427
=============
@@ -28,23 +31,27 @@ A minimal API
2831
from flask import Flask
2932
from flask_combo_jsonapi import Api, ResourceDetail, ResourceList
3033
from flask_sqlalchemy import SQLAlchemy
34+
from marshmallow import pre_load
3135
from marshmallow_jsonapi.flask import Schema
3236
from marshmallow_jsonapi import fields
3337
3438
# Create the Flask application and the Flask-SQLAlchemy object.
3539
app = Flask(__name__)
3640
app.config['DEBUG'] = True
37-
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
41+
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/api_minimal.db'
3842
db = SQLAlchemy(app)
3943
44+
4045
# Create model
4146
class Person(db.Model):
4247
id = db.Column(db.Integer, primary_key=True)
4348
name = db.Column(db.String)
4449
50+
4551
# Create the database.
4652
db.create_all()
4753
54+
4855
# Create schema
4956
class PersonSchema(Schema):
5057
class Meta:
@@ -54,18 +61,37 @@ A minimal API
5461
self_view_many = 'person_list'
5562
5663
id = fields.Integer(as_string=True, dump_only=True)
57-
name = fields.Str()
64+
name = fields.String()
65+
66+
@pre_load
67+
def remove_id_before_deserializing(self, data, **kwargs):
68+
"""
69+
We don't want to allow editing ID on POST / PATCH
70+
71+
Related issues:
72+
https://github.com/AdCombo/flask-combo-jsonapi/issues/34
73+
https://github.com/miLibris/flask-rest-jsonapi/issues/193
74+
"""
75+
if 'id' in data:
76+
del data['id']
77+
return data
5878
5979
# Create resource managers
6080
class PersonList(ResourceList):
6181
schema = PersonSchema
62-
data_layer = {'session': db.session,
63-
'model': Person}
82+
data_layer = {
83+
'session': db.session,
84+
'model': Person,
85+
}
86+
6487
6588
class PersonDetail(ResourceDetail):
6689
schema = PersonSchema
67-
data_layer = {'session': db.session,
68-
'model': Person}
90+
data_layer = {
91+
'session': db.session,
92+
'model': Person,
93+
}
94+
6995
7096
# Create the API object
7197
api = Api(app)
@@ -76,6 +102,7 @@ A minimal API
76102
if __name__ == '__main__':
77103
app.run()
78104
105+
79106
This example provides the following API structure:
80107

81108
======================== ====== ============= ===========================
@@ -88,6 +115,10 @@ URL method endpoint Usage
88115
/persons/<int:person_id> DELETE person_detail Delete a person
89116
======================== ====== ============= ===========================
90117

118+
119+
`More detailed example in the docs <https://flask-combo-jsonapi.readthedocs.io/en/stable/minimal_api_example.html>`_
120+
121+
91122
Flask-COMBO-JSONAPI vs `Flask-RESTful <https://flask-restful.readthedocs.io/en/latest/>`_
92123
==========================================================================================
93124

@@ -107,7 +138,7 @@ Flask-COMBO-JSONAPI vs `Flask-Restless <https://flask-restless.readthedocs.io/en
107138
Documentation
108139
=============
109140

110-
Documentation available here: http://Flask-COMBO-JSONAPI.readthedocs.io/en/latest/
141+
Documentation available here: https://flask-combo-jsonapi.readthedocs.io/
111142

112143
Thanks
113144
======

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
# The short X.Y version.
6464
version = "1.0"
6565
# The full version, including alpha/beta/rc tags.
66-
release = "1.0.6"
66+
release = "1.0.7"
6767

6868
# The language for content autogenerated by Sphinx. Refer to documentation
6969
# for a list of supported languages.

docs/configuration.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Configuration
66
You have access to 5 configration keys:
77

88
* PAGE_SIZE: the number of items in a page (default is 30)
9-
* MAX_PAGE_SIZE: the maximum page size. If you specify a page size greater than this value you will receive 400 Bad Request response.
9+
* MAX_PAGE_SIZE: the maximum page size. If you specify a page size greater than this value you will receive a 400 Bad Request response.
1010
* MAX_INCLUDE_DEPTH: the maximum length of an include through schema relationships
1111
* ALLOW_DISABLE_PAGINATION: if you want to disallow to disable pagination you can set this configuration key to False
12-
* CATCH_EXCEPTIONS: if you want flask_combo_jsonapi to catch all exceptions and return as JsonApiException (default is True)
12+
* CATCH_EXCEPTIONS: if you want flask_combo_jsonapi to catch all exceptions and return them as JsonApiException (default is True)

docs/data_layer.rst

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@ Data layer
55

66
.. currentmodule:: flask_combo_jsonapi
77

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.
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 uses multiple ORMs and data storage to manage your own objects. The data layer implements a CRUD interface for objects and relationships. It also manages pagination, filtering and sorting.
99
|
10-
| Flask-COMBO-JSONAPI has a full featured data layer that use the popular ORM `SQLAlchemy <https://www.sqlalchemy.org/>`_.
10+
| Flask-COMBO-JSONAPI has a full-featured data layer that uses the popular ORM `SQLAlchemy <https://www.sqlalchemy.org/>`_.
1111
1212
.. note::
1313

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
14+
The default data layer used by a resource manager is the SQLAlchemy one. So if that's what you want to use, you don't have to specify the class of the data layer in the resource manager
1515

1616
To configure the data layer you have to set its required parameters in the resource manager.
1717

@@ -28,11 +28,13 @@ Example:
2828
data_layer = {'session': db.session,
2929
'model': Person}
3030
31-
You can also plug additional methods to your data layer in the resource manager. There are two kinds of additional methods:
31+
You can also plug additional methods into your data layer in the resource manager. There are two kinds of additional methods:
3232

33-
* 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+
* query: the "query" additional method takes view_kwargs as parameter and returns an alternative query to retrieve the collection of objects in the GET method of the ResourceList manager.
3434

35-
* 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_combo_jsonapi.data_layers.base.Base <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/data_layers/base.py>`_ base class.
35+
* pre-/postprocess methods: all CRUD and relationship(s) operations have pre-/postprocess methods.
36+
Thanks to these you can do additional work before and after each operation of the data layer.
37+
Parameters of each pre-/postprocess method are available in the `flask_combo_jsonapi.data_layers.base.Base <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/data_layers/base.py>`_ base class.
3638

3739
Example:
3840

@@ -68,7 +70,7 @@ Example:
6870
6971
.. note::
7072

71-
You don't have to declare additional data layer methods in the resource manager. You can declare them in a dedicated module or in the declaration of the model.
73+
You don't have to declare additional data layer methods in the resource manager. You can declare them in a dedicated module or in the model's declaration.
7274

7375
Example:
7476

@@ -100,12 +102,14 @@ Optional parameters:
100102
:id_field: the field used as identifier field instead of the primary key of the model
101103
:url_field: the name of the parameter in the route to get value to filter with. Instead "id" is used.
102104

103-
By default SQLAlchemy eagerload related data specified in include querystring parameter. If you want to disable this feature you must add eagerload_includes: False to data layer parameters.
105+
By default SQLAlchemy eagerly loads related data specified in the include query string parameter. If you want to disable this feature you must add eagerload_includes: False to the data layer parameters.
104106

105107
Custom data layer
106108
-----------------
107109

108-
Like I said previously you can create and use your own data layer. A custom data layer must inherit from `flask_combo_jsonapi.data_layers.base.Base <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/data_layers/base.py>`_. You can see the full scope of possibilities of a data layer in this base class.
110+
As previously mentioned, you can create and use your own data layer.
111+
A custom data layer must inherit from `flask_combo_jsonapi.data_layers.base.Base <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/data_layers/base.py>`_.
112+
You can see the full scope of possibilities of a data layer in this base class.
109113

110114
Usage example:
111115

docs/errors.rst

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Errors
55

66
.. currentmodule:: flask_combo_jsonapi
77

8-
JSONAPI 1.0 specification recommand to return errors like that:
8+
The JSON:API 1.0 specification recommends to return errors like this:
99

1010
.. sourcecode:: http
1111

@@ -28,9 +28,9 @@ JSONAPI 1.0 specification recommand to return errors like that:
2828
}
2929
}
3030

31-
The "source" field gives information about the error if it is located in data provided or in querystring parameter.
31+
The "source" field gives information about the error if it is located in data provided or in a query string parameter.
3232

33-
The previous example displays error located in data provided instead of this next example displays error located in querystring parameter "include":
33+
The previous example shows an error located in data provided. The following example shows error in the query string parameter "include":
3434

3535
.. sourcecode:: http
3636

@@ -53,13 +53,13 @@ The previous example displays error located in data provided instead of this nex
5353
}
5454
}
5555

56-
Flask-COMBO-JSONAPI provides two kind of helpers to achieve error displaying:
56+
Flask-COMBO-JSONAPI provides two kinds of helpers for displaying errors:
5757

5858
| * **the errors module**: you can import jsonapi_errors from the `errors module <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/errors.py>`_ to create the structure of a list of errors according to JSONAPI 1.0 specification
5959
|
60-
| * **the exceptions module**: you can import lot of exceptions from this `module <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/exceptions.py>`_ that helps you to raise exceptions that will be well formatted according to JSONAPI 1.0 specification
60+
| * **the exceptions module**: you can import a lot of exceptions from this `module <https://github.com/AdCombo/flask-combo-jsonapi/blob/master/flask_combo_jsonapi/exceptions.py>`_ that helps you to raise exceptions that will be well-formatted according to the JSON:API 1.0 specification
6161
62-
When you create custom code for your api I recommand to use exceptions from exceptions module of Flask-COMBO-JSONAPI to raise errors because JsonApiException based exceptions are catched and rendered according to JSONAPI 1.0 specification.
62+
When you create custom code for your API I recommand using exceptions from the Flask-COMBO-JSONAPI's exceptions module to raise errors because JsonApiException-based exceptions are caught and rendered according to the JSON:API 1.0 specification.
6363

6464
Example:
6565

docs/filtering.rst

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,12 @@ Filtering
55

66
.. currentmodule:: flask_combo_jsonapi
77

8-
Flask-COMBO-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 the JSONAPI 1.0 specification.
8+
Flask-COMBO-JSONAPI has a very flexible filtering system.
9+
The filtering system is directly attached to the data layer used by the ResourceList manager.
10+
These examples show the filtering interface for SQLAlchemy's data layer
11+
but you can use the same interface for your custom data layer's filtering implementation as well.
12+
The only requirement is that you have to use the "filter" query string parameter
13+
to filter according to the JSON:API 1.0 specification.
914

1015
.. note::
1116

@@ -14,19 +19,19 @@ Flask-COMBO-JSONAPI as a very flexible filtering system. The filtering system is
1419
SQLAlchemy
1520
----------
1621

17-
The filtering system of SQLAlchemy data layer has exactly the same interface as the filtering system of `Flask-Restless <https://flask-restless.readthedocs.io/en/stable/searchformat.html#query-format>`_.
22+
The filtering system of SQLAlchemy's data layer has exactly the same interface as the one used in `Flask-Restless <https://flask-restless.readthedocs.io/en/stable/searchformat.html#query-format>`_.
1823
So this is a first example:
1924

2025
.. sourcecode:: http
2126

2227
GET /persons?filter=[{"name":"name","op":"eq","val":"John"}] HTTP/1.1
2328
Accept: application/vnd.api+json
2429

25-
In this example we want to retrieve persons which name is John. So we can see that the filtering interface completely fit the filtering interface of SQLAlchemy: a list a filter information.
30+
In this example we want to retrieve person records for people named John. So we can see that the filtering interface completely fits that of SQLAlchemy: a list a filter information.
2631

2732
:name: the name of the field you want to filter on
28-
:op: the operation you want to use (all sqlalchemy operations are available)
29-
: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
33+
:op: the operation you want to use (all SQLAlchemy operations are available)
34+
:val: the value that you want to compare. You can replace this by "field" if you want to compare against the value of another field
3035

3136
Example with field:
3237

@@ -35,7 +40,7 @@ Example with field:
3540
GET /persons?filter=[{"name":"name","op":"eq","field":"birth_date"}] HTTP/1.1
3641
Accept: application/vnd.api+json
3742

38-
In this example, we want to retrieve persons that name is equal to his birth_date. I know, this example is absurd but it is just to explain the syntax of this kind of filter.
43+
In this example, we want to retrieve people whose name is equal to their birth_date. This example is absurd, it's just here to explain the syntax of this kind of filter.
3944

4045
If you want to filter through relationships you can do that:
4146

@@ -56,9 +61,9 @@ If you want to filter through relationships you can do that:
5661

5762
.. note ::
5863
59-
When you filter on relationships use "any" operator for "to many" relationships and "has" operator for "to one" relationships.
64+
When you filter on relationships use the "any" operator for "to many" relationships and the "has" operator for "to one" relationships.
6065
61-
There is a shortcut to achieve the same filter:
66+
There is a shortcut to achieve the same filtering:
6267

6368
.. sourcecode:: http
6469

@@ -107,36 +112,36 @@ You can also use boolean combination of operations:
107112

108113
Common available operators:
109114

110-
* any: used to filter on to many relationships
115+
* any: used to filter on "to many" relationships
111116
* between: used to filter a field between two values
112-
* endswith: check if field ends with a string
113-
* eq: check if field is equal to something
114-
* ge: check if field is greater than or equal to something
115-
* gt: check if field is greater than to something
116-
* has: used to filter on to one relationships
117-
* ilike: check if field contains a string (case insensitive)
118-
* in\_: check if field is in a list of values
119-
* is\_: check if field is a value
120-
* isnot: check if field is not a value
121-
* like: check if field contains a string
122-
* le: check if field is less than or equal to something
123-
* lt: check if field is less than to something
124-
* match: check if field match against a string or pattern
125-
* ne: check if field is not equal to something
126-
* notilike: check if field does not contains a string (case insensitive)
127-
* notin\_: check if field is not in a list of values
128-
* notlike: check if field does not contains a string
129-
* startswith: check if field starts with a string
117+
* endswith: checks if field ends with a string
118+
* eq: checks if field is equal to something
119+
* ge: checks if field is greater than or equal to something
120+
* gt: checks if field is greater than something
121+
* has: used to filter on "to one" relationships
122+
* ilike: checks if field contains a string (case insensitive)
123+
* in\_: checks if field is in a list of values
124+
* is\_: checks if field is a value
125+
* isnot: checks if field is not a value
126+
* like: checks if field contains a string
127+
* le: checks if field is less than or equal to something
128+
* lt: checks if field is less than something
129+
* match: checks if field matches against a string or pattern
130+
* ne: checks if field is not equal to something
131+
* notilike: checks if field does not contain a string (case insensitive)
132+
* notin\_: checks if field is not in a list of values
133+
* notlike: checks if field does not contain a string
134+
* startswith: checks if field starts with a string
130135

131136
.. note::
132137

133-
Availables operators depend on field type in your model
138+
Available operators depend on the field type in your model
134139

135140
Simple filters
136141
--------------
137142

138-
Simple filter adds support for a simplified form of filters and supports only *eq* operator.
139-
Each simple filter transforms to original filter and appends to list of filters.
143+
Simple filters add support for a simplified form of filters and support only the *eq* operator.
144+
Each simple filter is transformed into a full filter and appended to the list of filters.
140145

141146
For example
142147

@@ -145,22 +150,22 @@ For example
145150
GET /persons?filter[name]=John HTTP/1.1
146151
Accept: application/vnd.api+json
147152

148-
equals to:
153+
equals:
149154

150155
.. sourcecode:: http
151156

152157
GET /persons?filter[name]=[{"name":"name","op":"eq","val":"John"}] HTTP/1.1
153158
Accept: application/vnd.api+json
154159

155160

156-
You can also use more than one simple filter in request:
161+
You can also use more than one simple filter in a request:
157162

158163
.. sourcecode:: http
159164

160165
GET /persons?filter[name]=John&filter[gender]=male HTTP/1.1
161166
Accept: application/vnd.api+json
162167

163-
which equals to:
168+
which is equal to:
164169

165170
.. sourcecode:: http
166171

0 commit comments

Comments
 (0)