From 6af92aa8934b802bf8b7567b2fa79dbdc21c91f9 Mon Sep 17 00:00:00 2001 From: collerek Date: Sun, 26 Jun 2022 19:36:13 +0200 Subject: [PATCH] Fix enum schema (#715) * fix schema with enum fields - issue #699 * fix drivers dependencies - make them optional * fix command * provide extras * add bolean field to related model * add test with select related and boolean * new test case based on issue * fix bool issue in postgres limit queries - issue #704 * fix coverage * bump version and add release info --- .github/workflows/deploy-docs.yml | 2 +- .github/workflows/test-package.yml | 2 +- docs/releases.md | 8 + ormar/fields/model_fields.py | 6 +- ormar/queryset/actions/order_action.py | 16 +- poetry.lock | 163 +++++++++--------- pyproject.toml | 38 ++-- tests/test_fastapi/test_enum_schema.py | 33 ++++ tests/test_relations/test_foreign_keys.py | 13 ++ ...est_postgress_select_related_with_limit.py | 107 ++++++++++++ 10 files changed, 280 insertions(+), 108 deletions(-) create mode 100644 tests/test_fastapi/test_enum_schema.py create mode 100644 tests/test_relations/test_postgress_select_related_with_limit.py diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index af859b8..596828a 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -16,7 +16,7 @@ jobs: - name: Install dependencies run: | python -m pip install poetry==1.1.11 - poetry install + poetry install --extras "all" env: POETRY_VIRTUALENVS_CREATE: false - name: Deploy diff --git a/.github/workflows/test-package.yml b/.github/workflows/test-package.yml index 9e65b1d..369eb93 100644 --- a/.github/workflows/test-package.yml +++ b/.github/workflows/test-package.yml @@ -50,7 +50,7 @@ jobs: - name: Install dependencies run: | python -m pip install poetry==1.1.11 - poetry install + poetry install --extras "all" env: POETRY_VIRTUALENVS_CREATE: false - name: Run mysql diff --git a/docs/releases.md b/docs/releases.md index f3fce4c..66d132d 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,3 +1,11 @@ +# 0.11.2 + +## 🐛 Fixes + +* Fix database drivers being required, while they should be optional [#713](https://github.com/collerek/ormar/issues/713) +* Fix boolean field problem in `limit` queries in postgres without `limit_raw_sql` flag [#704](https://github.com/collerek/ormar/issues/704) +* Fix enum_class spilling to schema causing errors in OpenAPI [#699](https://github.com/collerek/ormar/issues/699) + # 0.11.1 ## 🐛 Fixes diff --git a/ormar/fields/model_fields.py b/ormar/fields/model_fields.py index 4ad3faf..0d643e6 100644 --- a/ormar/fields/model_fields.py +++ b/ormar/fields/model_fields.py @@ -150,7 +150,7 @@ class ModelFieldFactory: scale=kwargs.get("scale", None), represent_as_str=kwargs.get("represent_as_base64_str", False), ) - enum_class = kwargs.get("enum_class", None) + enum_class = kwargs.pop("enum_class", None) field_type = cls._type if enum_class is None else enum_class namespace = dict( @@ -170,7 +170,9 @@ class ModelFieldFactory: unique=kwargs.pop("unique", False), pydantic_only=pydantic_only, autoincrement=autoincrement, - column_type=cls.get_column_type(**kwargs, sql_nullable=sql_nullable), + column_type=cls.get_column_type( + **kwargs, sql_nullable=sql_nullable, enum_class=enum_class + ), choices=choices, encrypt_secret=encrypt_secret, encrypt_backend=encrypt_backend, diff --git a/ormar/queryset/actions/order_action.py b/ormar/queryset/actions/order_action.py index 0927df5..7330d72 100644 --- a/ormar/queryset/actions/order_action.py +++ b/ormar/queryset/actions/order_action.py @@ -34,6 +34,12 @@ class OrderAction(QueryAction): def field_alias(self) -> str: return self.target_model.get_column_alias(self.field_name) + @property + def is_postgres_bool(self) -> bool: + dialect = self.target_model.Meta.database._backend._dialect.name + field_type = self.target_model.Meta.model_fields[self.field_name].__type__ + return dialect == "postgresql" and field_type == bool + def get_field_name_text(self) -> str: """ Escapes characters if it's required. @@ -49,15 +55,19 @@ class OrderAction(QueryAction): def get_min_or_max(self) -> sqlalchemy.sql.expression.TextClause: """ Used in limit sub queries where you need to use aggregated functions - in order to order by columns not included in group by. + in order to order by columns not included in group by. For postgres bool + field it's using bool_or function as aggregates does not work with this type + of columns. :return: min or max function to order :rtype: sqlalchemy.sql.elements.TextClause """ prefix = f"{self.table_prefix}_" if self.table_prefix else "" if self.direction == "": - return text(f"min({prefix}{self.table}" f".{self.field_alias})") - return text(f"max({prefix}{self.table}" f".{self.field_alias}) desc") + function = "min" if not self.is_postgres_bool else "bool_or" + return text(f"{function}({prefix}{self.table}" f".{self.field_alias})") + function = "max" if not self.is_postgres_bool else "bool_or" + return text(f"{function}({prefix}{self.table}" f".{self.field_alias}) desc") def get_text_clause(self) -> sqlalchemy.sql.expression.TextClause: """ diff --git a/poetry.lock b/poetry.lock index a5d727d..9edc01e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -3,7 +3,7 @@ name = "aiomysql" version = "0.0.22" description = "MySQL driver for asyncio." category = "main" -optional = false +optional = true python-versions = "*" [package.dependencies] @@ -16,8 +16,8 @@ sa = ["sqlalchemy (>=1.0)"] name = "aiopg" version = "1.3.3" description = "Postgres integration with asyncio." -category = "dev" -optional = false +category = "main" +optional = true python-versions = ">=3.6" [package.dependencies] @@ -32,7 +32,7 @@ name = "aiosqlite" version = "0.17.0" description = "asyncio bridge to the standard sqlite3 module" category = "main" -optional = false +optional = true python-versions = ">=3.6" [package.dependencies] @@ -71,8 +71,8 @@ typed = ["typed-ast"] name = "async-timeout" version = "4.0.2" description = "Timeout context manager for asyncio programs" -category = "dev" -optional = false +category = "main" +optional = true python-versions = ">=3.6" [package.dependencies] @@ -83,7 +83,7 @@ name = "asyncpg" version = "0.25.0" description = "An asyncio PostgreSQL driver" category = "main" -optional = false +optional = true python-versions = ">=3.6.0" [package.dependencies] @@ -168,7 +168,7 @@ python-versions = "*" [[package]] name = "certifi" -version = "2022.5.18.1" +version = "2022.6.15" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false @@ -238,7 +238,7 @@ python-versions = ">=3.6" [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.5" description = "Cross-platform colored terminal text." category = "dev" optional = false @@ -528,14 +528,14 @@ docs = ["sphinx"] [[package]] name = "griffe" -version = "0.20.0" +version = "0.21.0" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." category = "dev" optional = false python-versions = ">=3.7" [package.dependencies] -cached_property = {version = "*", markers = "python_version < \"3.8\""} +cached-property = {version = "*", markers = "python_version < \"3.8\""} [package.extras] async = ["aiofiles (>=0.7,<1.0)"] @@ -561,7 +561,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.11.4" +version = "4.12.0" description = "Read metadata from Python packages" category = "main" optional = false @@ -574,7 +574,7 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] perf = ["ipython"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -695,7 +695,7 @@ mkdocs = ">=1.0.3,<2.0.0" [[package]] name = "mkdocs-material" -version = "8.3.7" +version = "8.3.8" description = "Documentation that simply works" category = "dev" optional = false @@ -752,7 +752,7 @@ python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] [[package]] name = "mkdocstrings-python" -version = "0.7.0" +version = "0.7.1" description = "A Python handler for mkdocstrings." category = "dev" optional = false @@ -806,17 +806,17 @@ python-versions = "*" name = "mysqlclient" version = "2.1.1" description = "Python interface to MySQL" -category = "dev" -optional = false +category = "main" +optional = true python-versions = ">=3.5" [[package]] name = "nodeenv" -version = "1.6.0" +version = "1.7.0" description = "Node.js virtual environment builder" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" [[package]] name = "orjson" @@ -902,7 +902,7 @@ name = "psycopg2-binary" version = "2.9.3" description = "psycopg2 - Python-PostgreSQL Database Adapter" category = "main" -optional = false +optional = true python-versions = ">=3.6" [[package]] @@ -976,7 +976,7 @@ name = "pymysql" version = "0.9.3" description = "Pure Python MySQL Driver" category = "main" -optional = false +optional = true python-versions = "*" [package.extras] @@ -1077,20 +1077,20 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.27.1" +version = "2.28.0" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2.0.0,<2.1.0" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] @@ -1270,7 +1270,7 @@ python-versions = "*" [[package]] name = "types-requests" -version = "2.27.31" +version = "2.28.0" description = "Typing stubs for requests" category = "dev" optional = false @@ -1289,7 +1289,7 @@ python-versions = "*" [[package]] name = "types-ujson" -version = "5.3.0" +version = "5.3.1" description = "Typing stubs for ujson" category = "dev" optional = false @@ -1326,7 +1326,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" -version = "20.14.1" +version = "20.15.0" description = "Virtual Python Environment builder" category = "dev" optional = false @@ -1345,7 +1345,7 @@ testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", [[package]] name = "watchdog" -version = "2.1.8" +version = "2.1.9" description = "Filesystem events monitoring" category = "dev" optional = false @@ -1378,18 +1378,19 @@ docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [extras] +aiopg = ["aiopg", "psycopg2-binary"] +all = ["aiosqlite", "asyncpg", "aiopg", "psycopg2-binary", "aiomysql", "mysqlclient", "PyMySQL", "orjson", "cryptography"] crypto = ["cryptography"] -dev = ["asyncpg", "psycopg2-binary", "aiomysql", "orjson", "cryptography"] -mysql = ["aiomysql"] +mysql = ["aiomysql", "PyMySQL"] orjson = ["orjson"] postgres = ["asyncpg", "psycopg2-binary"] postgresql = ["asyncpg", "psycopg2-binary"] -sqlite = [] +sqlite = ["aiosqlite"] [metadata] lock-version = "1.1" python-versions = "^3.7.0" -content-hash = "49cf540235b2eae9a4a490926eed874d517c2ab18299609fd3d8165074f1831b" +content-hash = "f4656936d47932f540c58d77e097eb9437b0d38c6673ede4011b60e26ec5b564" [metadata.files] aiomysql = [ @@ -1486,8 +1487,8 @@ cached-property = [ {file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"}, ] certifi = [ - {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, - {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"}, + {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, + {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, ] cffi = [ {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, @@ -1562,8 +1563,8 @@ cognitive-complexity = [ {file = "cognitive_complexity-1.2.0.tar.gz", hash = "sha256:3c2b433a9e41502932f6aa629e1f57a5e8f145956c54facbb5241a9492af6fb7"}, ] colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, + {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, ] coverage = [ {file = "coverage-6.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f1d5aa2703e1dab4ae6cf416eb0095304f49d004c39e9db1d86f57924f43006b"}, @@ -1765,8 +1766,8 @@ greenlet = [ {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, ] griffe = [ - {file = "griffe-0.20.0-py3-none-any.whl", hash = "sha256:899e0c9c09baf22b31de1c969a03edaf0ddf72d0a7183df8de746b6c26ed62f4"}, - {file = "griffe-0.20.0.tar.gz", hash = "sha256:bf181de6e661c0d2a229c1dc7e90db0def280ee3a89c6829fcc1695baee65f7f"}, + {file = "griffe-0.21.0-py3-none-any.whl", hash = "sha256:e9fb5eeb7c721e1d84804452bdc742bd57b120b13aba663157668ae2d217088a"}, + {file = "griffe-0.21.0.tar.gz", hash = "sha256:61ab3bc02b09afeb489f1aef44c646a09f1837d9cdf15943ac6021903a4d3984"}, ] identify = [ {file = "identify-2.5.1-py2.py3-none-any.whl", hash = "sha256:0dca2ea3e4381c435ef9c33ba100a78a9b40c0bab11189c7cf121f75815efeaa"}, @@ -1777,8 +1778,8 @@ idna = [ {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.11.4-py3-none-any.whl", hash = "sha256:c58c8eb8a762858f49e18436ff552e83914778e50e9d2f1660535ffb364552ec"}, - {file = "importlib_metadata-4.11.4.tar.gz", hash = "sha256:5d26852efe48c0a32b0509ffbc583fda1a2266545a78d104a6f4aff3db17d700"}, + {file = "importlib_metadata-4.12.0-py3-none-any.whl", hash = "sha256:7401a975809ea1fdc658c3aa4f78cc2195a0e019c5cbc4c06122884e9ae80c23"}, + {file = "importlib_metadata-4.12.0.tar.gz", hash = "sha256:637245b8bab2b6502fcbc752cc4b7a6f6243bb02b31c5c26156ad103d3d45670"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -1859,8 +1860,8 @@ mkdocs-literate-nav = [ {file = "mkdocs_literate_nav-0.4.1-py3-none-any.whl", hash = "sha256:a4b761792ba21defbe2dfd5e0de6ba451639e1ca0f0661c37eda83cc6261e4f9"}, ] mkdocs-material = [ - {file = "mkdocs-material-8.3.7.tar.gz", hash = "sha256:e0e01f5deeacb126ad0a64998bb66d512c24467f4c9550a0afc74f7f0719a9ae"}, - {file = "mkdocs_material-8.3.7-py2.py3-none-any.whl", hash = "sha256:f24ed0fc185dd88b036abc0425ce9e31ecf1e7e673bf01d3b168b373c08e629a"}, + {file = "mkdocs-material-8.3.8.tar.gz", hash = "sha256:b9cd305c3c29ef758931dae06e4aea0ca9f8bcc8ac6b2d45f10f932a015d6b83"}, + {file = "mkdocs_material-8.3.8-py2.py3-none-any.whl", hash = "sha256:949c75fa934d4b9ecc7b519964e58f0c9fc29f2ceb04736c85809cdbc403dfb5"}, ] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, @@ -1875,8 +1876,8 @@ mkdocstrings = [ {file = "mkdocstrings-0.19.0.tar.gz", hash = "sha256:efa34a67bad11229d532d89f6836a8a215937548623b64f3698a1df62e01cc3e"}, ] mkdocstrings-python = [ - {file = "mkdocstrings-python-0.7.0.tar.gz", hash = "sha256:e54c67890e8bb7dc4604360c8ef5dd214b23b6924de7706f461e3c998d4ea061"}, - {file = "mkdocstrings_python-0.7.0-py3-none-any.whl", hash = "sha256:6964bd92f106766e771ac6cd5bc02643a960602b4d921b95362e31d491e9a6db"}, + {file = "mkdocstrings-python-0.7.1.tar.gz", hash = "sha256:c334b382dca202dfa37071c182418a6df5818356a95d54362a2b24822ca3af71"}, + {file = "mkdocstrings_python-0.7.1-py3-none-any.whl", hash = "sha256:a22060bfa374697678e9af4e62b020d990dad2711c98f7a9fac5c0345bef93c7"}, ] mr-proper = [ {file = "mr_proper-0.0.7-py3-none-any.whl", hash = "sha256:74a1b60240c46f10ba518707ef72811a01e5c270da0a78b5dd2dd923d99fdb14"}, @@ -1921,8 +1922,8 @@ mysqlclient = [ {file = "mysqlclient-2.1.1.tar.gz", hash = "sha256:828757e419fb11dd6c5ed2576ec92c3efaa93a0f7c39e263586d1ee779c3d782"}, ] nodeenv = [ - {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, - {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] orjson = [ {file = "orjson-3.7.3-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:1cd9cd9bb9cee39aca4f7e1079cc3e180ffad0b13342ac3fabf5ff3caf7285bb"}, @@ -2171,8 +2172,8 @@ pyyaml-env-tag = [ {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, + {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, + {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, @@ -2298,16 +2299,16 @@ types-pymysql = [ {file = "types_PyMySQL-1.0.19-py3-none-any.whl", hash = "sha256:fc43dceda3f3fc4ac388db7dd57b157c13d94cb84e30cca4848df590c314bf68"}, ] types-requests = [ - {file = "types-requests-2.27.31.tar.gz", hash = "sha256:6fab97b99fea52b9c7b466a4dd93e06bb325bc7e7420475e87831026a8dd35cc"}, - {file = "types_requests-2.27.31-py3-none-any.whl", hash = "sha256:1b6cf6a2bf57fd8018c1b636b69762900466fafddfb62e1330e092f3d4b0966a"}, + {file = "types-requests-2.28.0.tar.gz", hash = "sha256:9863d16dfbb3fa55dcda64fa3b989e76e8859033b26c1e1623e30465cfe294d3"}, + {file = "types_requests-2.28.0-py3-none-any.whl", hash = "sha256:85383b4ef0535f639c3f06c5bbb6494bbf59570c4cd88bbcf540f0b2ac1b49ab"}, ] types-toml = [ {file = "types-toml-0.10.7.tar.gz", hash = "sha256:a567fe2614b177d537ad99a661adc9bfc8c55a46f95e66370a4ed2dd171335f9"}, {file = "types_toml-0.10.7-py3-none-any.whl", hash = "sha256:05a8da4bfde2f1ee60e90c7071c063b461f74c63a9c3c1099470c08d6fa58615"}, ] types-ujson = [ - {file = "types-ujson-5.3.0.tar.gz", hash = "sha256:3b25f0ea993f0a9aa7b1edbf5d7a51fb6e17d4f375201684933ce16e4aa702de"}, - {file = "types_ujson-5.3.0-py3-none-any.whl", hash = "sha256:91702721e00c6f3003be6499f971ae7bbb1e5e1c9ffbad3a7648c7e7590d86e2"}, + {file = "types-ujson-5.3.1.tar.gz", hash = "sha256:64698711b7b5e21a270213aa491ae3a4e2d75406e9536b1d998c259efb8aa416"}, + {file = "types_ujson-5.3.1-py3-none-any.whl", hash = "sha256:09a9f1c1b511423c37cda6247492f93bde1157141b5ecff9509a28c920e8c0e4"}, ] types-urllib3 = [ {file = "types-urllib3-1.26.15.tar.gz", hash = "sha256:c89283541ef92e344b7f59f83ea9b5a295b16366ceee3f25ecfc5593c79f794e"}, @@ -2323,35 +2324,35 @@ urllib3 = [ {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, ] virtualenv = [ - {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, - {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, + {file = "virtualenv-20.15.0-py2.py3-none-any.whl", hash = "sha256:804cce4de5b8a322f099897e308eecc8f6e2951f1a8e7e2b3598dff865f01336"}, + {file = "virtualenv-20.15.0.tar.gz", hash = "sha256:4c44b1d77ca81f8368e2d7414f9b20c428ad16b343ac6d226206c5b84e2b4fcc"}, ] watchdog = [ - {file = "watchdog-2.1.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:676263bee67b165f16b05abc52acc7a94feac5b5ab2449b491f1a97638a79277"}, - {file = "watchdog-2.1.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aa68d2d9a89d686fae99d28a6edf3b18595e78f5adf4f5c18fbfda549ac0f20c"}, - {file = "watchdog-2.1.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e2e51c53666850c3ecffe9d265fc5d7351db644de17b15e9c685dd3cdcd6f97"}, - {file = "watchdog-2.1.8-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7721ac736170b191c50806f43357407138c6748e4eb3e69b071397f7f7aaeedd"}, - {file = "watchdog-2.1.8-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ce7376aed3da5fd777483fe5ebc8475a440c6d18f23998024f832134b2938e7b"}, - {file = "watchdog-2.1.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f9ee4c6bf3a1b2ed6be90a2d78f3f4bbd8105b6390c04a86eb48ed67bbfa0b0b"}, - {file = "watchdog-2.1.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:68dbe75e0fa1ba4d73ab3f8e67b21770fbed0651d32ce515cd38919a26873266"}, - {file = "watchdog-2.1.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0c520009b8cce79099237d810aaa19bc920941c268578436b62013b2f0102320"}, - {file = "watchdog-2.1.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efcc8cbc1b43902571b3dce7ef53003f5b97fe4f275fe0489565fc6e2ebe3314"}, - {file = "watchdog-2.1.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:746e4c197ec1083581bb1f64d07d1136accf03437badb5ff8fcb862565c193b2"}, - {file = "watchdog-2.1.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1ae17b6be788fb8e4d8753d8d599de948f0275a232416e16436363c682c6f850"}, - {file = "watchdog-2.1.8-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ddde157dc1447d8130cb5b8df102fad845916fe4335e3d3c3f44c16565becbb7"}, - {file = "watchdog-2.1.8-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4978db33fc0934c92013ee163a9db158ec216099b69fce5aec790aba704da412"}, - {file = "watchdog-2.1.8-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b962de4d7d92ff78fb2dbc6a0cb292a679dea879a0eb5568911484d56545b153"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_aarch64.whl", hash = "sha256:1e5d0fdfaa265c29dc12621913a76ae99656cf7587d03950dfeb3595e5a26102"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_armv7l.whl", hash = "sha256:036ed15f7cd656351bf4e17244447be0a09a61aaa92014332d50719fc5973bc0"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_i686.whl", hash = "sha256:2962628a8777650703e8f6f2593065884c602df7bae95759b2df267bd89b2ef5"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_ppc64.whl", hash = "sha256:156ec3a94695ea68cfb83454b98754af6e276031ba1ae7ae724dc6bf8973b92a"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:47598fe6713fc1fee86b1ca85c9cbe77e9b72d002d6adeab9c3b608f8a5ead10"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_s390x.whl", hash = "sha256:fed4de6e45a4f16e4046ea00917b4fe1700b97244e5d114f594b4a1b9de6bed8"}, - {file = "watchdog-2.1.8-py3-none-manylinux2014_x86_64.whl", hash = "sha256:24dedcc3ce75e150f2a1d704661f6879764461a481ba15a57dc80543de46021c"}, - {file = "watchdog-2.1.8-py3-none-win32.whl", hash = "sha256:6ddf67bc9f413791072e3afb466e46cc72c6799ba73dea18439b412e8f2e3257"}, - {file = "watchdog-2.1.8-py3-none-win_amd64.whl", hash = "sha256:88ef3e8640ef0a64b7ad7394b0f23384f58ac19dd759da7eaa9bc04b2898943f"}, - {file = "watchdog-2.1.8-py3-none-win_ia64.whl", hash = "sha256:0fb60c7d31474b21acba54079ce9ff0136411183e9a591369417cddb1d7d00d7"}, - {file = "watchdog-2.1.8.tar.gz", hash = "sha256:6d03149126864abd32715d4e9267d2754cede25a69052901399356ad3bc5ecff"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a735a990a1095f75ca4f36ea2ef2752c99e6ee997c46b0de507ba40a09bf7330"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b17d302850c8d412784d9246cfe8d7e3af6bcd45f958abb2d08a6f8bedf695d"}, + {file = "watchdog-2.1.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee3e38a6cc050a8830089f79cbec8a3878ec2fe5160cdb2dc8ccb6def8552658"}, + {file = "watchdog-2.1.9-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64a27aed691408a6abd83394b38503e8176f69031ca25d64131d8d640a307591"}, + {file = "watchdog-2.1.9-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:195fc70c6e41237362ba720e9aaf394f8178bfc7fa68207f112d108edef1af33"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bfc4d351e6348d6ec51df007432e6fe80adb53fd41183716017026af03427846"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8250546a98388cbc00c3ee3cc5cf96799b5a595270dfcfa855491a64b86ef8c3"}, + {file = "watchdog-2.1.9-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:117ffc6ec261639a0209a3252546b12800670d4bf5f84fbd355957a0595fe654"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:97f9752208f5154e9e7b76acc8c4f5a58801b338de2af14e7e181ee3b28a5d39"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:247dcf1df956daa24828bfea5a138d0e7a7c98b1a47cf1fa5b0c3c16241fcbb7"}, + {file = "watchdog-2.1.9-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:226b3c6c468ce72051a4c15a4cc2ef317c32590d82ba0b330403cafd98a62cfd"}, + {file = "watchdog-2.1.9-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d9820fe47c20c13e3c9dd544d3706a2a26c02b2b43c993b62fcd8011bcc0adb3"}, + {file = "watchdog-2.1.9-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:70af927aa1613ded6a68089a9262a009fbdf819f46d09c1a908d4b36e1ba2b2d"}, + {file = "watchdog-2.1.9-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:ed80a1628cee19f5cfc6bb74e173f1b4189eb532e705e2a13e3250312a62e0c9"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_aarch64.whl", hash = "sha256:9f05a5f7c12452f6a27203f76779ae3f46fa30f1dd833037ea8cbc2887c60213"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_armv7l.whl", hash = "sha256:255bb5758f7e89b1a13c05a5bceccec2219f8995a3a4c4d6968fe1de6a3b2892"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_i686.whl", hash = "sha256:d3dda00aca282b26194bdd0adec21e4c21e916956d972369359ba63ade616153"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64.whl", hash = "sha256:186f6c55abc5e03872ae14c2f294a153ec7292f807af99f57611acc8caa75306"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:083171652584e1b8829581f965b9b7723ca5f9a2cd7e20271edf264cfd7c1412"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_s390x.whl", hash = "sha256:b530ae007a5f5d50b7fbba96634c7ee21abec70dc3e7f0233339c81943848dc1"}, + {file = "watchdog-2.1.9-py3-none-manylinux2014_x86_64.whl", hash = "sha256:4f4e1c4aa54fb86316a62a87b3378c025e228178d55481d30d857c6c438897d6"}, + {file = "watchdog-2.1.9-py3-none-win32.whl", hash = "sha256:5952135968519e2447a01875a6f5fc8c03190b24d14ee52b0f4b1682259520b1"}, + {file = "watchdog-2.1.9-py3-none-win_amd64.whl", hash = "sha256:7a833211f49143c3d336729b0020ffd1274078e94b0ae42e22f596999f50279c"}, + {file = "watchdog-2.1.9-py3-none-win_ia64.whl", hash = "sha256:ad576a565260d8f99d97f2e64b0f97a48228317095908568a9d5c786c829d428"}, + {file = "watchdog-2.1.9.tar.gz", hash = "sha256:43ce20ebb36a51f21fa376f76d1d4692452b2527ccd601950d69ed36b9e21609"}, ] yappi = [ {file = "yappi-1.3.5.tar.gz", hash = "sha256:f54c25f04aa7c613633b529bffd14e0699a4363f414dc9c065616fd52064a49b"}, diff --git a/pyproject.toml b/pyproject.toml index 4c79030..c2c5906 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "ormar" [tool.poetry] name = "ormar" -version = "0.11.1" +version = "0.11.2" description = "A simple async ORM with fastapi in mind and pydantic validation." authors = ["Radosław Drążkiewicz "] license = "MIT" @@ -45,11 +45,16 @@ python = "^3.7.0" databases = ">=0.3.2,!=0.5.0,!=0.5.1,!=0.5.2,!=0.5.3,<0.6.1" pydantic = ">=1.6.1,!=1.7,!=1.7.1,!=1.7.2,!=1.7.3,!=1.8,!=1.8.1,<=1.9.1" SQLAlchemy = ">=1.3.18,<1.4.39" -asyncpg = { version = ">=0.24,<0.26", optional = true } -psycopg2-binary = { version = "^2.9.1", optional = true } -aiomysql = { version = ">=0.0.21,<0.0.23", optional = true } -aiosqlite = { version = "^0.17.0", optional = true } cryptography = { version = ">=35,<38", optional = true } +# Async database drivers +aiosqlite = { version = "^0.17.0", optional = true } +aiomysql = { version = ">=0.0.21,<0.0.23", optional = true } +aiopg = { version = "^1.3.3", optional = true } +asyncpg = { version = ">=0.24,<0.26", optional = true } +# Sync database drivers for standard tooling around setup/teardown/migrations. +psycopg2-binary = { version = "^2.9.1", optional = true } +mysqlclient = { version = "^2.1.0", optional = true } +PyMySQL = { version = ">=0.9,<=0.9.3", optional = true } [tool.poetry.dependencies.orjson] version = ">=3.6.4" @@ -65,17 +70,6 @@ python = "<3.8" [tool.poetry.dev-dependencies] -# Async database driversy -aiomysql = ">=0.0.21,<0.0.23" -aiosqlite = "^0.17.0" -aiopg = "^1.3.3" -asyncpg = ">=0.24,<0.26" - -# Sync database drivers for standard tooling around setup/teardown/migrations. -psycopg2-binary = "^2.9.1" -mysqlclient = "^2.1.1" -PyMySQL = ">=0.9,<=0.9.3" - # Testing pytest = "^7.1.2" pytest-cov = "^3.0.0" @@ -124,15 +118,19 @@ pre-commit = "^2.19.0" [tool.poetry.extras] postgresql = ["asyncpg", "psycopg2-binary"] postgres = ["asyncpg", "psycopg2-binary"] -mysql = ["aiomysql"] -sqlite = ["sqlite"] +aiopg = ["aiopg", "psycopg2-binary"] +mysql = ["aiomysql", "PyMySQL"] +sqlite = ["aiosqlite"] orjson = ["orjson"] crypto = ["cryptography"] -dev = [ +all = [ + "aiosqlite", "asyncpg", + "aiopg", "psycopg2-binary", "aiomysql", - "sqlite", + "mysqlclient", + "PyMySQL", "orjson", "cryptography", ] diff --git a/tests/test_fastapi/test_enum_schema.py b/tests/test_fastapi/test_enum_schema.py new file mode 100644 index 0000000..6fa3ce1 --- /dev/null +++ b/tests/test_fastapi/test_enum_schema.py @@ -0,0 +1,33 @@ +from enum import Enum + +import databases +import sqlalchemy + +import ormar +from tests.settings import DATABASE_URL + +database = databases.Database(DATABASE_URL, force_rollback=True) +metadata = sqlalchemy.MetaData() + + +class MyEnum(Enum): + SMALL = 1 + BIG = 2 + + +class EnumExample(ormar.Model): + class Meta: + tablename = "enum_example" + metadata = metadata + database = database + + id: int = ormar.Integer(primary_key=True) + size: MyEnum = ormar.Enum(enum_class=MyEnum, default=MyEnum.SMALL) + + +def test_proper_schema(): + schema = EnumExample.schema_json() + assert ( + '{"MyEnum": {"title": "MyEnum", "description": "An enumeration.", ' + '"enum": [1, 2]}}' in schema + ) diff --git a/tests/test_relations/test_foreign_keys.py b/tests/test_relations/test_foreign_keys.py index dbcf87f..e3412ed 100644 --- a/tests/test_relations/test_foreign_keys.py +++ b/tests/test_relations/test_foreign_keys.py @@ -34,6 +34,7 @@ class Track(ormar.Model): title: str = ormar.String(max_length=100) position: int = ormar.Integer() play_count: int = ormar.Integer(nullable=True, default=0) + is_disabled: bool = ormar.Boolean(default=False) class Cover(ormar.Model): @@ -350,6 +351,18 @@ async def test_limit_and_offset(): assert len(tracks) == 1 assert tracks[0].title == "Sample2" + album = await Album.objects.select_related("tracks").limit(1).get() + assert len(album.tracks) == 3 + assert album.tracks[0].title == "Sample" + + album = ( + await Album.objects.select_related("tracks") + .limit(1, limit_raw_sql=True) + .get() + ) + assert len(album.tracks) == 1 + assert album.tracks[0].title == "Sample" + @pytest.mark.asyncio async def test_get_exceptions(): diff --git a/tests/test_relations/test_postgress_select_related_with_limit.py b/tests/test_relations/test_postgress_select_related_with_limit.py new file mode 100644 index 0000000..0369fa4 --- /dev/null +++ b/tests/test_relations/test_postgress_select_related_with_limit.py @@ -0,0 +1,107 @@ +# Models +import uuid +from datetime import date +from enum import Enum +from typing import Optional + +from pydantic import EmailStr + +import databases +import sqlalchemy +from sqlalchemy import create_engine + +import ormar +import pytest + +from tests.settings import DATABASE_URL + +database = databases.Database(DATABASE_URL, force_rollback=True) +metadata = sqlalchemy.MetaData() + + +class PrimaryKeyMixin: + id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4) + + +class Level(Enum): + ADMIN = "0" + STAFF = "1" + + +class MainMeta(ormar.ModelMeta): + database = database + metadata = metadata + + +class User(PrimaryKeyMixin, ormar.Model): + """User Model Class to Implement Method for Operations of User Entity""" + + mobile: str = ormar.String(unique=True, index=True, max_length=10) + password: str = ormar.String(max_length=128) + level: str = ormar.String( + max_length=1, choices=list(Level), default=Level.STAFF.value + ) + email: Optional[str] = ormar.String(max_length=255, nullable=True, default=None) + avatar: Optional[str] = ormar.String(max_length=255, nullable=True, default=None) + fullname: Optional[str] = ormar.String(max_length=64, nullable=True, default=None) + is_active: bool = ormar.Boolean(index=True, nullable=False, default=True) + + class Meta(MainMeta): + orders_by = ["-is_active", "-level"] + + +class Task(PrimaryKeyMixin, ormar.Model): + """Task Model Class to Implement Method for Operations of Task Entity""" + + name: str = ormar.String(max_length=64, nullalbe=False) + description: Optional[str] = ormar.Text(nullable=True, default=None) + start_date: Optional[date] = ormar.Date(nullable=True, default=None) + end_date: Optional[date] = ormar.Date(nullable=True, default=None) + is_halted: bool = ormar.Boolean(index=True, nullable=False, default=True) + user: User = ormar.ForeignKey(to=User) + + class Meta(MainMeta): + orders_by = ["-end_date", "-start_date"] + constraints = [ + ormar.UniqueColumns("user", "name"), + ] + + +@pytest.fixture(autouse=True, scope="module") +def create_test_database(): + engine = create_engine(DATABASE_URL) + metadata.create_all(engine) + yield + metadata.drop_all(engine) + + +@pytest.mark.asyncio +async def test_selecting_related_with_limit(): + async with database: + user1 = await User(mobile="9928917653", password="pass1").save() + user2 = await User(mobile="9928917654", password="pass2").save() + await Task(name="one", user=user1).save() + await Task(name="two", user=user1).save() + await Task(name="three", user=user2).save() + await Task(name="four", user=user2).save() + + users = ( + await User.objects.limit(2, limit_raw_sql=True) + .select_related(User.tasks) + .all() + ) + users2 = ( + await User.objects.select_related(User.tasks) + .limit(2, limit_raw_sql=True) + .all() + ) + assert users == users2 + assert len(users) == 1 + assert len(users[0].tasks) == 2 + + users3 = await User.objects.limit(2).select_related(User.tasks).all() + users4 = await User.objects.select_related(User.tasks).limit(2).all() + assert users3 == users4 + assert len(users3) == 2 + assert len(users3[0].tasks) == 2 + assert len(users3[1].tasks) == 2