flask-marshmallow 0.14.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/CHANGELOG.rst +44 -2
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/CONTRIBUTING.rst +1 -1
- flask_marshmallow-1.0.0/PKG-INFO +171 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/README.rst +8 -8
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_templates/side-primary.html +1 -1
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_templates/side-secondary.html +1 -1
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/conf.py +5 -8
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/index.rst +5 -1
- flask_marshmallow-1.0.0/pyproject.toml +66 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/src/flask_marshmallow/__init__.py +19 -13
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/src/flask_marshmallow/fields.py +80 -14
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/src/flask_marshmallow/schema.py +1 -7
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/src/flask_marshmallow/sqla.py +12 -20
- flask_marshmallow-1.0.0/src/flask_marshmallow/validate.py +183 -0
- flask_marshmallow-1.0.0/tests/__init__.py +0 -0
- flask_marshmallow-1.0.0/tests/conftest.py +112 -0
- flask_marshmallow-1.0.0/tests/markers.py +9 -0
- flask_marshmallow-1.0.0/tests/test_core.py +64 -0
- flask_marshmallow-1.0.0/tests/test_fields.py +169 -0
- flask_marshmallow-1.0.0/tests/test_io.py +0 -0
- flask_marshmallow-1.0.0/tests/test_sqla.py +288 -0
- flask_marshmallow-1.0.0/tests/test_validate.py +107 -0
- flask_marshmallow-1.0.0/tox.ini +40 -0
- flask-marshmallow-0.14.0/MANIFEST.in +0 -5
- flask-marshmallow-0.14.0/PKG-INFO +0 -163
- flask-marshmallow-0.14.0/setup.cfg +0 -13
- flask-marshmallow-0.14.0/setup.py +0 -84
- flask-marshmallow-0.14.0/src/flask_marshmallow/compat.py +0 -9
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/PKG-INFO +0 -163
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/SOURCES.txt +0 -37
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/dependency_links.txt +0 -1
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/not-zip-safe +0 -1
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/requires.txt +0 -52
- flask-marshmallow-0.14.0/src/flask_marshmallow.egg-info/top_level.txt +0 -1
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/LICENSE +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/Makefile +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_static/logo.png +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/LICENSE +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/README +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask/layout.html +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask/relations.html +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask/static/flasky.css_t +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask/theme.conf +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask_small/layout.html +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask_small/static/flasky.css_t +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask_small/theme.conf +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/_themes/flask_theme_support.py +7 -7
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/changelog.rst +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/license.rst +0 -0
- {flask-marshmallow-0.14.0 → flask_marshmallow-1.0.0}/docs/make.bat +0 -0
|
@@ -1,12 +1,54 @@
|
|
|
1
1
|
Changelog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
1.0.0 (2024-01-16)
|
|
5
|
+
******************
|
|
6
|
+
|
|
7
|
+
Features:
|
|
8
|
+
|
|
9
|
+
* Add field ``fields.File``, ``validate.FileType``, and ``validate.FileSize``
|
|
10
|
+
for deserializing uploaded files (:issue:`280`, :pr:`282`).
|
|
11
|
+
Thanks :user:`uncle-lv` for the PR
|
|
12
|
+
* Add field ``Config`` for serializing Flask configuration values (:issue:`280`, :pr:`281`).
|
|
13
|
+
Thanks :user:`greyli` for the PR.
|
|
14
|
+
|
|
15
|
+
Support:
|
|
16
|
+
|
|
17
|
+
* Support marshmallow-sqlalchemy>=0.29.0.
|
|
18
|
+
* Test against Python 3.12.
|
|
19
|
+
* Drop support for Python 3.7.
|
|
20
|
+
|
|
21
|
+
Other changes:
|
|
22
|
+
|
|
23
|
+
* *Backwards-incompatible*: Remove ```flask_marshmallow.__version__``
|
|
24
|
+
and ``flask_marshmallow.__version_info__`` attributes (:pr:`284`).
|
|
25
|
+
Use feature detection or ``importlib.metadata.version("flask-marshmallow")`` instead.
|
|
26
|
+
|
|
27
|
+
0.15.0 (2023-04-05)
|
|
28
|
+
*******************
|
|
29
|
+
|
|
30
|
+
* Changes to supported software versions.
|
|
31
|
+
|
|
32
|
+
* python3.6 or later and marshmallow>=3.0.0 are now required
|
|
33
|
+
* Add support for python3.11
|
|
34
|
+
* For ``sqlalchemy`` integration, marshmallow-sqlalchemy>=0.28.2 and
|
|
35
|
+
flask-sqlalchemy>=3.0.0 are now required
|
|
36
|
+
|
|
37
|
+
* *Backwards-incompatible*: ``URLFor`` and ``AbsoluteURLFor`` now do not accept
|
|
38
|
+
parameters for ``flask.url_for`` as top-level parameters. They must always be
|
|
39
|
+
passed in the ``values`` dictionary, as explained in the v0.14.0 changelog.
|
|
40
|
+
|
|
41
|
+
Bug fixes:
|
|
42
|
+
|
|
43
|
+
* Address distutils deprecation warning in Python 3.10 (:pr:`242`).
|
|
44
|
+
Thanks :user:`GabrielLins64` for the PR.
|
|
45
|
+
|
|
4
46
|
0.14.0 (2020-09-27)
|
|
5
47
|
*******************
|
|
6
48
|
|
|
7
49
|
* Add ``values`` argument to ``URLFor`` and ``AbsoluteURLFor`` for passing values to ``flask.url_for``.
|
|
8
|
-
|
|
9
|
-
|
|
50
|
+
This prevents unrelated parameters from getting passed (:issue:`52`, :issue:`67`).
|
|
51
|
+
Thanks :user:`AlrasheedA` for the PR.
|
|
10
52
|
|
|
11
53
|
Deprecation:
|
|
12
54
|
|
|
@@ -108,7 +108,7 @@ To run syntax checks: ::
|
|
|
108
108
|
|
|
109
109
|
$ tox -e lint
|
|
110
110
|
|
|
111
|
-
(Optional) To run tests
|
|
111
|
+
(Optional) To run tests in all supported Python versions in their own virtual environments (must have each interpreter installed): ::
|
|
112
112
|
|
|
113
113
|
$ tox
|
|
114
114
|
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: flask-marshmallow
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Flask + marshmallow for beautiful APIs
|
|
5
|
+
Maintainer-email: Steven Loria <sloria1@gmail.com>
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/x-rst
|
|
8
|
+
Classifier: Environment :: Web Environment
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
19
|
+
Requires-Dist: Flask
|
|
20
|
+
Requires-Dist: marshmallow>=3.0.0
|
|
21
|
+
Requires-Dist: packaging>=17.0
|
|
22
|
+
Requires-Dist: flask-marshmallow[tests] ; extra == "dev"
|
|
23
|
+
Requires-Dist: tox ; extra == "dev"
|
|
24
|
+
Requires-Dist: pre-commit~=3.5 ; extra == "dev"
|
|
25
|
+
Requires-Dist: marshmallow-sqlalchemy>=0.19.0 ; extra == "docs"
|
|
26
|
+
Requires-Dist: Sphinx==7.2.6 ; extra == "docs"
|
|
27
|
+
Requires-Dist: sphinx-issues==3.0.1 ; extra == "docs"
|
|
28
|
+
Requires-Dist: flask-sqlalchemy>=3.0.0 ; extra == "sqlalchemy"
|
|
29
|
+
Requires-Dist: marshmallow-sqlalchemy>=0.29.0 ; extra == "sqlalchemy"
|
|
30
|
+
Requires-Dist: flask-marshmallow[sqlalchemy] ; extra == "tests"
|
|
31
|
+
Requires-Dist: pytest ; extra == "tests"
|
|
32
|
+
Project-URL: Funding, https://opencollective.com/marshmallow
|
|
33
|
+
Project-URL: Issues, https://github.com/marshmallow-code/flask-marshmallow/issues
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Provides-Extra: docs
|
|
36
|
+
Provides-Extra: sqlalchemy
|
|
37
|
+
Provides-Extra: tests
|
|
38
|
+
|
|
39
|
+
*****************
|
|
40
|
+
Flask-Marshmallow
|
|
41
|
+
*****************
|
|
42
|
+
|
|
43
|
+
|pypi-package| |build-status| |docs| |marshmallow3|
|
|
44
|
+
|
|
45
|
+
Flask + marshmallow for beautiful APIs
|
|
46
|
+
======================================
|
|
47
|
+
|
|
48
|
+
Flask-Marshmallow is a thin integration layer for `Flask`_ (a Python web framework) and `marshmallow`_ (an object serialization/deserialization library) that adds additional features to marshmallow, including URL and Hyperlinks fields for HATEOAS-ready APIs. It also (optionally) integrates with `Flask-SQLAlchemy <http://flask-sqlalchemy.pocoo.org/>`_.
|
|
49
|
+
|
|
50
|
+
Get it now
|
|
51
|
+
----------
|
|
52
|
+
::
|
|
53
|
+
|
|
54
|
+
pip install flask-marshmallow
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Create your app.
|
|
58
|
+
|
|
59
|
+
.. code-block:: python
|
|
60
|
+
|
|
61
|
+
from flask import Flask
|
|
62
|
+
from flask_marshmallow import Marshmallow
|
|
63
|
+
|
|
64
|
+
app = Flask(__name__)
|
|
65
|
+
ma = Marshmallow(app)
|
|
66
|
+
|
|
67
|
+
Write your models.
|
|
68
|
+
|
|
69
|
+
.. code-block:: python
|
|
70
|
+
|
|
71
|
+
from your_orm import Model, Column, Integer, String, DateTime
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class User(Model):
|
|
75
|
+
email = Column(String)
|
|
76
|
+
password = Column(String)
|
|
77
|
+
date_created = Column(DateTime, auto_now_add=True)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
Define your output format with marshmallow.
|
|
81
|
+
|
|
82
|
+
.. code-block:: python
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class UserSchema(ma.Schema):
|
|
86
|
+
class Meta:
|
|
87
|
+
# Fields to expose
|
|
88
|
+
fields = ("email", "date_created", "_links")
|
|
89
|
+
|
|
90
|
+
# Smart hyperlinking
|
|
91
|
+
_links = ma.Hyperlinks(
|
|
92
|
+
{
|
|
93
|
+
"self": ma.URLFor("user_detail", values=dict(id="<id>")),
|
|
94
|
+
"collection": ma.URLFor("users"),
|
|
95
|
+
}
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
user_schema = UserSchema()
|
|
100
|
+
users_schema = UserSchema(many=True)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
Output the data in your views.
|
|
104
|
+
|
|
105
|
+
.. code-block:: python
|
|
106
|
+
|
|
107
|
+
@app.route("/api/users/")
|
|
108
|
+
def users():
|
|
109
|
+
all_users = User.all()
|
|
110
|
+
return users_schema.dump(all_users)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@app.route("/api/users/<id>")
|
|
114
|
+
def user_detail(id):
|
|
115
|
+
user = User.get(id)
|
|
116
|
+
return user_schema.dump(user)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# {
|
|
120
|
+
# "email": "fred@queen.com",
|
|
121
|
+
# "date_created": "Fri, 25 Apr 2014 06:02:56 -0000",
|
|
122
|
+
# "_links": {
|
|
123
|
+
# "self": "/api/users/42",
|
|
124
|
+
# "collection": "/api/users/"
|
|
125
|
+
# }
|
|
126
|
+
# }
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
http://flask-marshmallow.readthedocs.io/
|
|
130
|
+
========================================
|
|
131
|
+
|
|
132
|
+
Learn More
|
|
133
|
+
==========
|
|
134
|
+
|
|
135
|
+
To learn more about marshmallow, check out its `docs <http://marshmallow.readthedocs.io/en/latest/>`_.
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
Project Links
|
|
140
|
+
=============
|
|
141
|
+
|
|
142
|
+
- Docs: https://flask-marshmallow.readthedocs.io/
|
|
143
|
+
- Changelog: http://flask-marshmallow.readthedocs.io/en/latest/changelog.html
|
|
144
|
+
- PyPI: https://pypi.org/project/flask-marshmallow/
|
|
145
|
+
- Issues: https://github.com/marshmallow-code/flask-marshmallow/issues
|
|
146
|
+
|
|
147
|
+
License
|
|
148
|
+
=======
|
|
149
|
+
|
|
150
|
+
MIT licensed. See the bundled `LICENSE <https://github.com/marshmallow-code/flask-marshmallow/blob/master/LICENSE>`_ file for more details.
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
.. _Flask: http://flask.pocoo.org
|
|
154
|
+
.. _marshmallow: http://marshmallow.readthedocs.io
|
|
155
|
+
|
|
156
|
+
.. |pypi-package| image:: https://badgen.net/pypi/v/flask-marshmallow
|
|
157
|
+
:target: https://pypi.org/project/flask-marshmallow/
|
|
158
|
+
:alt: Latest version
|
|
159
|
+
|
|
160
|
+
.. |build-status| image:: https://github.com/marshmallow-code/flask-marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
161
|
+
:target: https://github.com/marshmallow-code/flask-marshmallow/actions/workflows/build-release.yml
|
|
162
|
+
:alt: Build status
|
|
163
|
+
|
|
164
|
+
.. |docs| image:: https://readthedocs.org/projects/flask-marshmallow/badge/
|
|
165
|
+
:target: https://flask-marshmallow.readthedocs.io/
|
|
166
|
+
:alt: Documentation
|
|
167
|
+
|
|
168
|
+
.. |marshmallow3| image:: https://badgen.net/badge/marshmallow/3
|
|
169
|
+
:target: https://marshmallow.readthedocs.io/en/latest/upgrading.html
|
|
170
|
+
:alt: marshmallow 3 compatible
|
|
171
|
+
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Flask-Marshmallow
|
|
3
3
|
*****************
|
|
4
4
|
|
|
5
|
-
|pypi-package| |build-status| |docs| |
|
|
5
|
+
|pypi-package| |build-status| |docs| |marshmallow3|
|
|
6
6
|
|
|
7
7
|
Flask + marshmallow for beautiful APIs
|
|
8
8
|
======================================
|
|
@@ -103,7 +103,7 @@ Project Links
|
|
|
103
103
|
|
|
104
104
|
- Docs: https://flask-marshmallow.readthedocs.io/
|
|
105
105
|
- Changelog: http://flask-marshmallow.readthedocs.io/en/latest/changelog.html
|
|
106
|
-
- PyPI: https://pypi.
|
|
106
|
+
- PyPI: https://pypi.org/project/flask-marshmallow/
|
|
107
107
|
- Issues: https://github.com/marshmallow-code/flask-marshmallow/issues
|
|
108
108
|
|
|
109
109
|
License
|
|
@@ -118,15 +118,15 @@ MIT licensed. See the bundled `LICENSE <https://github.com/marshmallow-code/flas
|
|
|
118
118
|
.. |pypi-package| image:: https://badgen.net/pypi/v/flask-marshmallow
|
|
119
119
|
:target: https://pypi.org/project/flask-marshmallow/
|
|
120
120
|
:alt: Latest version
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
|
|
122
|
+
.. |build-status| image:: https://github.com/marshmallow-code/flask-marshmallow/actions/workflows/build-release.yml/badge.svg
|
|
123
|
+
:target: https://github.com/marshmallow-code/flask-marshmallow/actions/workflows/build-release.yml
|
|
123
124
|
:alt: Build status
|
|
125
|
+
|
|
124
126
|
.. |docs| image:: https://readthedocs.org/projects/flask-marshmallow/badge/
|
|
125
127
|
:target: https://flask-marshmallow.readthedocs.io/
|
|
126
128
|
:alt: Documentation
|
|
127
|
-
|
|
129
|
+
|
|
130
|
+
.. |marshmallow3| image:: https://badgen.net/badge/marshmallow/3
|
|
128
131
|
:target: https://marshmallow.readthedocs.io/en/latest/upgrading.html
|
|
129
132
|
:alt: marshmallow 3 compatible
|
|
130
|
-
.. |black| image:: https://badgen.net/badge/code%20style/black/000
|
|
131
|
-
:target: https://github.com/ambv/black
|
|
132
|
-
:alt: code style: black
|
|
@@ -14,7 +14,7 @@ Flask + marshmallow for beautiful APIs
|
|
|
14
14
|
|
|
15
15
|
<h3>Useful Links</h3>
|
|
16
16
|
<ul>
|
|
17
|
-
<li><a href="
|
|
17
|
+
<li><a href="https://pypi.org/project/flask-marshmallow/">Flask-Marshmallow @ PyPI</a></li>
|
|
18
18
|
<li><a href="http://github.com/marshmallow-code/flask-marshmallow">Flask-Marshmallow @ GitHub</a></li>
|
|
19
19
|
<li><a href="http://github.com/marshmallow-code/flask-marshmallow/issues">Issue Tracker</a></li>
|
|
20
20
|
</ul>
|
|
@@ -18,7 +18,7 @@ Flask + marshmallow for beautiful APIs
|
|
|
18
18
|
|
|
19
19
|
<h3>Useful Links</h3>
|
|
20
20
|
<ul>
|
|
21
|
-
<li><a href="
|
|
21
|
+
<li><a href="https://pypi.org/project/flask-marshmallow/">Flask-Marshmallow @ PyPI</a></li>
|
|
22
22
|
<li><a href="http://github.com/marshmallow-code/flask-marshmallow">Flask-Marshmallow @ GitHub</a></li>
|
|
23
23
|
<li><a href="http://github.com/marshmallow-code/flask-marshmallow/issues">Issue Tracker</a></li>
|
|
24
24
|
</ul>
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
import datetime as dt
|
|
3
|
-
import
|
|
2
|
+
import importlib.metadata
|
|
4
3
|
import os
|
|
5
|
-
|
|
6
|
-
sys.path.insert(0, os.path.abspath(os.path.join("..", "src")))
|
|
7
|
-
import flask_marshmallow # noqa: E402
|
|
4
|
+
import sys
|
|
8
5
|
|
|
9
6
|
sys.path.append(os.path.abspath("_themes"))
|
|
10
7
|
extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx_issues"]
|
|
@@ -34,13 +31,13 @@ source_suffix = ".rst"
|
|
|
34
31
|
master_doc = "index"
|
|
35
32
|
|
|
36
33
|
# General information about the project.
|
|
37
|
-
project =
|
|
38
|
-
copyright =
|
|
34
|
+
project = "Flask-Marshmallow"
|
|
35
|
+
copyright = "2014-{:%Y}".format(
|
|
39
36
|
dt.datetime.utcfromtimestamp(os.path.getmtime("../CHANGELOG.rst"))
|
|
40
37
|
)
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
version = release =
|
|
40
|
+
version = release = importlib.metadata.version("flask-marshmallow")
|
|
44
41
|
exclude_patterns = ["_build"]
|
|
45
42
|
# The name of the Pygments (syntax highlighting) style to use.
|
|
46
43
|
pygments_style = "flask_theme_support.FlaskyStyle"
|
|
@@ -4,7 +4,7 @@ Flask-Marshmallow: Flask + marshmallow for beautiful APIs
|
|
|
4
4
|
|
|
5
5
|
:ref:`changelog <changelog>` //
|
|
6
6
|
`github <http://github.com/marshmallow-code/flask-marshmallow>`_ //
|
|
7
|
-
`pypi <
|
|
7
|
+
`pypi <https://pypi.org/project/flask-marshmallow/>`_ //
|
|
8
8
|
`issues <http://github.com/marshmallow-code/flask-marshmallow/issues>`_
|
|
9
9
|
|
|
10
10
|
|
|
@@ -178,6 +178,7 @@ You can now use your schema to dump and load your ORM objects.
|
|
|
178
178
|
|
|
179
179
|
- By default, `~flask_marshmallow.sqla.SQLAlchemySchema` uses the scoped session created by Flask-SQLAlchemy.
|
|
180
180
|
- `~flask_marshmallow.sqla.SQLAlchemySchema` subclasses `flask_marshmallow.Schema`, so it includes the `~flask_marshmallow.Schema.jsonify` method.
|
|
181
|
+
|
|
181
182
|
Note: By default, Flask's `jsonify` method sorts the list of keys and returns consistent results to ensure that external HTTP caches aren't trashed. As a side effect, this will override `ordered=True <https://marshmallow.readthedocs.io/en/latest/quickstart.html#ordering-output>`_
|
|
182
183
|
in the SQLAlchemySchema's `class Meta` (if you set it). To disable this, set `JSON_SORT_KEYS=False` in your Flask app config. In production it's recommended to let `jsonify` sort the keys and not set `ordered=True` in your `~flask_marshmallow.sqla.SQLAlchemySchema` in order to minimize generation time and maximize cacheability of the results.
|
|
183
184
|
|
|
@@ -228,6 +229,9 @@ API
|
|
|
228
229
|
.. automodule:: flask_marshmallow.fields
|
|
229
230
|
:members:
|
|
230
231
|
|
|
232
|
+
.. automodule:: flask_marshmallow.validate
|
|
233
|
+
:members:
|
|
234
|
+
|
|
231
235
|
.. automodule:: flask_marshmallow.sqla
|
|
232
236
|
:members:
|
|
233
237
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "flask-marshmallow"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "Flask + marshmallow for beautiful APIs"
|
|
5
|
+
readme = "README.rst"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
maintainers = [{ name = "Steven Loria", email = "sloria1@gmail.com" }]
|
|
8
|
+
classifiers = [
|
|
9
|
+
"Environment :: Web Environment",
|
|
10
|
+
"Intended Audience :: Developers",
|
|
11
|
+
"License :: OSI Approved :: MIT License",
|
|
12
|
+
"Natural Language :: English",
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Programming Language :: Python :: 3.8",
|
|
15
|
+
"Programming Language :: Python :: 3.9",
|
|
16
|
+
"Programming Language :: Python :: 3.10",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
20
|
+
]
|
|
21
|
+
requires-python = ">=3.8"
|
|
22
|
+
dependencies = ["Flask", "marshmallow>=3.0.0", "packaging>=17.0"]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Issues = "https://github.com/marshmallow-code/flask-marshmallow/issues"
|
|
26
|
+
Funding = "https://opencollective.com/marshmallow"
|
|
27
|
+
|
|
28
|
+
[project.optional-dependencies]
|
|
29
|
+
docs = [
|
|
30
|
+
"marshmallow-sqlalchemy>=0.19.0",
|
|
31
|
+
"Sphinx==7.2.6",
|
|
32
|
+
"sphinx-issues==3.0.1",
|
|
33
|
+
]
|
|
34
|
+
tests = ["flask-marshmallow[sqlalchemy]", "pytest"]
|
|
35
|
+
dev = ["flask-marshmallow[tests]", "tox", "pre-commit~=3.5"]
|
|
36
|
+
sqlalchemy = ["flask-sqlalchemy>=3.0.0", "marshmallow-sqlalchemy>=0.29.0"]
|
|
37
|
+
|
|
38
|
+
[build-system]
|
|
39
|
+
requires = ["flit_core<4"]
|
|
40
|
+
build-backend = "flit_core.buildapi"
|
|
41
|
+
|
|
42
|
+
[tool.flit.sdist]
|
|
43
|
+
include = ["docs/", "tests/", "CHANGELOG.rst", "CONTRIBUTING.rst", "tox.ini"]
|
|
44
|
+
exclude = ["docs/_build/"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
src = ["src"]
|
|
48
|
+
fix = true
|
|
49
|
+
show-fixes = true
|
|
50
|
+
show-source = true
|
|
51
|
+
|
|
52
|
+
[tool.ruff.lint]
|
|
53
|
+
select = [
|
|
54
|
+
"B", # flake8-bugbear
|
|
55
|
+
"E", # pycodestyle error
|
|
56
|
+
"F", # pyflakes
|
|
57
|
+
"I", # isort
|
|
58
|
+
"UP", # pyupgrade
|
|
59
|
+
"W", # pycodestyle warning
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
filterwarnings = [
|
|
64
|
+
"error",
|
|
65
|
+
"ignore:distutils Version classes are deprecated\\. Use packaging.version instead\\.:DeprecationWarning:marshmallow",
|
|
66
|
+
]
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
"""
|
|
3
2
|
flask_marshmallow
|
|
4
3
|
~~~~~~~~~~~~~~~~~
|
|
@@ -7,9 +6,9 @@
|
|
|
7
6
|
with your Flask application.
|
|
8
7
|
"""
|
|
9
8
|
import warnings
|
|
10
|
-
from distutils.version import LooseVersion
|
|
11
9
|
|
|
12
|
-
from marshmallow import
|
|
10
|
+
from marshmallow import exceptions, pprint
|
|
11
|
+
from marshmallow import fields as base_fields
|
|
13
12
|
|
|
14
13
|
from . import fields
|
|
15
14
|
from .schema import Schema
|
|
@@ -25,14 +24,20 @@ else:
|
|
|
25
24
|
except ImportError:
|
|
26
25
|
warnings.warn(
|
|
27
26
|
"Flask-SQLAlchemy integration requires "
|
|
28
|
-
"marshmallow-sqlalchemy to be installed."
|
|
27
|
+
"marshmallow-sqlalchemy to be installed.",
|
|
28
|
+
stacklevel=2,
|
|
29
29
|
)
|
|
30
30
|
else:
|
|
31
31
|
has_sqla = True
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
__all__ = [
|
|
34
|
+
"EXTENSION_NAME",
|
|
35
|
+
"Marshmallow",
|
|
36
|
+
"Schema",
|
|
37
|
+
"fields",
|
|
38
|
+
"exceptions",
|
|
39
|
+
"pprint",
|
|
40
|
+
]
|
|
36
41
|
|
|
37
42
|
EXTENSION_NAME = "flask-marshmallow"
|
|
38
43
|
|
|
@@ -48,7 +53,7 @@ def _attach_fields(obj):
|
|
|
48
53
|
setattr(obj, attr, getattr(fields, attr))
|
|
49
54
|
|
|
50
55
|
|
|
51
|
-
class Marshmallow
|
|
56
|
+
class Marshmallow:
|
|
52
57
|
"""Wrapper class that integrates Marshmallow with a Flask application.
|
|
53
58
|
|
|
54
59
|
To use it, instantiate with an application::
|
|
@@ -76,14 +81,15 @@ class Marshmallow(object):
|
|
|
76
81
|
})
|
|
77
82
|
|
|
78
83
|
|
|
79
|
-
In order to integrate with Flask-SQLAlchemy, this extension must be initialized
|
|
80
|
-
`flask_sqlalchemy.SQLAlchemy`. ::
|
|
84
|
+
In order to integrate with Flask-SQLAlchemy, this extension must be initialized
|
|
85
|
+
*after* `flask_sqlalchemy.SQLAlchemy`. ::
|
|
81
86
|
|
|
82
87
|
db = SQLAlchemy(app)
|
|
83
88
|
ma = Marshmallow(app)
|
|
84
89
|
|
|
85
|
-
This gives you access to `ma.SQLAlchemySchema` and `ma.SQLAlchemyAutoSchema`, which
|
|
86
|
-
marshmallow `~marshmallow.Schema` classes
|
|
90
|
+
This gives you access to `ma.SQLAlchemySchema` and `ma.SQLAlchemyAutoSchema`, which
|
|
91
|
+
generate marshmallow `~marshmallow.Schema` classes
|
|
92
|
+
based on the passed in model or table. ::
|
|
87
93
|
|
|
88
94
|
class AuthorSchema(ma.SQLAlchemyAutoSchema):
|
|
89
95
|
class Meta:
|
|
@@ -112,7 +118,7 @@ class Marshmallow(object):
|
|
|
112
118
|
|
|
113
119
|
# If using Flask-SQLAlchemy, attach db.session to SQLAlchemySchema
|
|
114
120
|
if has_sqla and "sqlalchemy" in app.extensions:
|
|
115
|
-
db = app.extensions["sqlalchemy"]
|
|
121
|
+
db = app.extensions["sqlalchemy"]
|
|
116
122
|
if self.SQLAlchemySchema:
|
|
117
123
|
self.SQLAlchemySchema.OPTIONS_CLASS.session = db.session
|
|
118
124
|
if self.SQLAlchemyAutoSchema:
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
"""
|
|
3
2
|
flask_marshmallow.fields
|
|
4
3
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
|
@@ -10,12 +9,18 @@
|
|
|
10
9
|
"""
|
|
11
10
|
import re
|
|
12
11
|
|
|
13
|
-
from flask import url_for
|
|
14
|
-
from marshmallow import fields
|
|
15
|
-
from marshmallow import missing
|
|
12
|
+
from flask import current_app, url_for
|
|
13
|
+
from marshmallow import fields, missing
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
__all__ = [
|
|
16
|
+
"URLFor",
|
|
17
|
+
"UrlFor",
|
|
18
|
+
"AbsoluteURLFor",
|
|
19
|
+
"AbsoluteUrlFor",
|
|
20
|
+
"Hyperlinks",
|
|
21
|
+
"File",
|
|
22
|
+
"Config",
|
|
23
|
+
]
|
|
19
24
|
|
|
20
25
|
|
|
21
26
|
_tpl_pattern = re.compile(r"\s*<\s*(\S*)\s*>\s*")
|
|
@@ -70,7 +75,10 @@ class URLFor(fields.Field):
|
|
|
70
75
|
Usage: ::
|
|
71
76
|
|
|
72
77
|
url = URLFor('author_get', values=dict(id='<id>'))
|
|
73
|
-
https_url = URLFor(
|
|
78
|
+
https_url = URLFor(
|
|
79
|
+
'author_get',
|
|
80
|
+
values=dict(id='<id>', _scheme='https', _external=True),
|
|
81
|
+
)
|
|
74
82
|
|
|
75
83
|
:param str endpoint: Flask endpoint name.
|
|
76
84
|
:param dict values: Same keyword arguments as Flask's url_for, except string
|
|
@@ -81,9 +89,9 @@ class URLFor(fields.Field):
|
|
|
81
89
|
|
|
82
90
|
_CHECK_ATTRIBUTE = False
|
|
83
91
|
|
|
84
|
-
def __init__(self, endpoint, values=None, **kwargs):
|
|
92
|
+
def __init__(self, endpoint, values=None, id=None, **kwargs):
|
|
85
93
|
self.endpoint = endpoint
|
|
86
|
-
self.values = values or
|
|
94
|
+
self.values = values or {}
|
|
87
95
|
fields.Field.__init__(self, **kwargs)
|
|
88
96
|
|
|
89
97
|
def _serialize(self, value, key, obj):
|
|
@@ -101,8 +109,7 @@ class URLFor(fields.Field):
|
|
|
101
109
|
param_values[name] = attribute_value
|
|
102
110
|
else:
|
|
103
111
|
raise AttributeError(
|
|
104
|
-
"{attr_name!r} is not a valid "
|
|
105
|
-
"attribute of {obj!r}".format(attr_name=attr_name, obj=obj)
|
|
112
|
+
f"{attr_name!r} is not a valid " f"attribute of {obj!r}"
|
|
106
113
|
)
|
|
107
114
|
else:
|
|
108
115
|
param_values[name] = attr_tpl
|
|
@@ -116,10 +123,10 @@ class AbsoluteURLFor(URLFor):
|
|
|
116
123
|
"""Field that outputs the absolute URL for an endpoint."""
|
|
117
124
|
|
|
118
125
|
def __init__(self, endpoint, values=None, **kwargs):
|
|
119
|
-
if values:
|
|
126
|
+
if values:
|
|
120
127
|
values["_external"] = True
|
|
121
128
|
else:
|
|
122
|
-
|
|
129
|
+
values = {"_external": True}
|
|
123
130
|
URLFor.__init__(self, endpoint=endpoint, values=values, **kwargs)
|
|
124
131
|
|
|
125
132
|
|
|
@@ -127,7 +134,9 @@ AbsoluteUrlFor = AbsoluteURLFor
|
|
|
127
134
|
|
|
128
135
|
|
|
129
136
|
def _rapply(d, func, *args, **kwargs):
|
|
130
|
-
"""Apply a function to all values in a dictionary or
|
|
137
|
+
"""Apply a function to all values in a dictionary or
|
|
138
|
+
list of dictionaries, recursively.
|
|
139
|
+
"""
|
|
131
140
|
if isinstance(d, (tuple, list)):
|
|
132
141
|
return [_rapply(each, func, *args, **kwargs) for each in d]
|
|
133
142
|
if isinstance(d, dict):
|
|
@@ -179,3 +188,60 @@ class Hyperlinks(fields.Field):
|
|
|
179
188
|
|
|
180
189
|
def _serialize(self, value, attr, obj):
|
|
181
190
|
return _rapply(self.schema, _url_val, key=attr, obj=obj)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class File(fields.Field):
|
|
194
|
+
"""A binary file field for uploaded files.
|
|
195
|
+
|
|
196
|
+
Examples: ::
|
|
197
|
+
|
|
198
|
+
class ImageSchema(Schema):
|
|
199
|
+
image = File(required=True)
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
def __init__(self, *args, **kwargs):
|
|
203
|
+
super().__init__(*args, **kwargs)
|
|
204
|
+
# Metadata used by apispec
|
|
205
|
+
self.metadata["type"] = "string"
|
|
206
|
+
self.metadata["format"] = "binary"
|
|
207
|
+
|
|
208
|
+
default_error_messages = {"invalid": "Not a valid file."}
|
|
209
|
+
|
|
210
|
+
def _deserialize(self, value, attr, data, **kwargs):
|
|
211
|
+
from werkzeug.datastructures import FileStorage
|
|
212
|
+
|
|
213
|
+
if not isinstance(value, FileStorage):
|
|
214
|
+
raise self.make_error("invalid")
|
|
215
|
+
return value
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
class Config(fields.Field):
|
|
219
|
+
"""A field for Flask configuration values.
|
|
220
|
+
|
|
221
|
+
Examples: ::
|
|
222
|
+
|
|
223
|
+
from flask import Flask
|
|
224
|
+
|
|
225
|
+
app = Flask(__name__)
|
|
226
|
+
app.config['API_TITLE'] = 'Pet API'
|
|
227
|
+
|
|
228
|
+
class FooSchema(Schema):
|
|
229
|
+
user = String()
|
|
230
|
+
title = Config('API_TITLE')
|
|
231
|
+
|
|
232
|
+
This field should only be used in an output schema. A ``ValueError`` will
|
|
233
|
+
be raised if the config key is not found in the app config.
|
|
234
|
+
|
|
235
|
+
:param str key: The key of the configuration value.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
_CHECK_ATTRIBUTE = False
|
|
239
|
+
|
|
240
|
+
def __init__(self, key: str, **kwargs):
|
|
241
|
+
fields.Field.__init__(self, **kwargs)
|
|
242
|
+
self.key = key
|
|
243
|
+
|
|
244
|
+
def _serialize(self, value, attr, obj, **kwargs):
|
|
245
|
+
if self.key not in current_app.config:
|
|
246
|
+
raise ValueError(f"The key {self.key!r} is not found in the app config.")
|
|
247
|
+
return current_app.config[self.key]
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
1
|
import flask
|
|
3
2
|
import marshmallow as ma
|
|
4
3
|
|
|
5
|
-
from flask_marshmallow.compat import _MARSHMALLOW_VERSION_INFO
|
|
6
|
-
|
|
7
4
|
sentinel = object()
|
|
8
5
|
|
|
9
6
|
|
|
@@ -35,8 +32,5 @@ class Schema(ma.Schema):
|
|
|
35
32
|
"""
|
|
36
33
|
if many is sentinel:
|
|
37
34
|
many = self.many
|
|
38
|
-
|
|
39
|
-
data = self.dump(obj, many=many)
|
|
40
|
-
else:
|
|
41
|
-
data = self.dump(obj, many=many).data
|
|
35
|
+
data = self.dump(obj, many=many)
|
|
42
36
|
return flask.jsonify(data, *args, **kwargs)
|