Skip to content

Commit

Permalink
Merge branch 'tortoise:develop' into patch-1
Browse files Browse the repository at this point in the history
  • Loading branch information
esrehmki committed Jun 6, 2023
2 parents 2c4e884 + a819a79 commit e855dd4
Show file tree
Hide file tree
Showing 26 changed files with 2,343 additions and 491 deletions.
10 changes: 0 additions & 10 deletions .github/workflows/codespell.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .github/workflows/gh-pages.yml
Expand Up @@ -23,4 +23,4 @@ jobs:
personal_token: ${{ secrets.PERSONAL_TOKEN }}
publish_dir: build/html
external_repository: tortoise/tortoise.github.io
publish_branch: master
publish_branch: main
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -8,6 +8,12 @@ Changelog

0.19
====
0.19.4
------
Fixed
^^^^^
- Fix foreign key constraint not generated on MSSQL Server. (#1400)

0.19.3
------
Added
Expand Down
2 changes: 2 additions & 0 deletions CONTRIBUTORS.rst
Expand Up @@ -51,6 +51,8 @@ Contributors
* Aleksandr Rozum ``@rozumalex``
* Mojix Coder ``@MojixCoder``
* Paul Serov ``@thakryptex``
* Stanislav Zmiev ``@Ovsyanka83``
* Waket Zheng ``@waketzheng``

Special Thanks
==============
Expand Down
3 changes: 1 addition & 2 deletions Makefile
Expand Up @@ -29,13 +29,12 @@ endif
#pylint -d C,W,R $(checkfiles)
#bandit -r $(checkfiles)make
twine check dist/*
codespell $(checkfiles)

lint: deps build
ifneq ($(shell which black),)
black --check $(checkfiles) || (echo "Please run 'make style' to auto-fix style issues" && false)
endif
flake8 $(checkfiles)
ruff $(checkfiles)
mypy $(checkfiles)
#pylint $(checkfiles)
bandit -r $(checkfiles)
Expand Down
8 changes: 8 additions & 0 deletions README.rst
Expand Up @@ -214,6 +214,14 @@ Contributing

Please have a look at the `Contribution Guide <docs/CONTRIBUTING.rst>`_.

ThanksTo
========

Powerful Python IDE `Pycharm <https://www.jetbrains.com/pycharm/>`_
from `Jetbrains <https://jb.gg/OpenSourceSupport>`_.

.. image:: https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg
:target: https://jb.gg/OpenSourceSupport

License
=======
Expand Down
2 changes: 1 addition & 1 deletion docs/functions.rst
Expand Up @@ -65,7 +65,7 @@ Base function class

Custom functions
================
You can custom functions which not builtin, such as ``TruncMonth`` and ``JsonExtract`` etc.
You can define custom functions which are not builtin, such as ``TruncMonth`` and ``JsonExtract`` etc.

.. code-block:: python3
Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started.rst
Expand Up @@ -54,7 +54,7 @@ Optional Accelerators
---------------------
The following libraries can be used as accelerators:

* `python-rapidjson <https://pypi.org/project/python-rapidjson/>`_: Automatically used if installed for JSON SerDes.
* `orjson <https://pypi.org/project/orjson/>`_: Automatically used if installed for JSON SerDes.
* `uvloop <https://pypi.org/project/uvloop/>`_: Shown to improve performance, but needs to be set up.
Please look at ``uvloop`` documentation for more info.
If you use a framework, it may already use it.
Expand Down
2,570 changes: 2,172 additions & 398 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "tortoise-orm"
version = "0.19.3"
version = "0.19.4"
description = "Easy async ORM for python, built with relations in mind"
authors = ["Andrey Bondar <andrey@bondar.ru>", "Nickolas Grigoriadis <nagrigoriadis@gmail.com>", "long2ice <long2ice@gmail.com>"]
license = "Apache-2.0"
Expand Down Expand Up @@ -44,8 +44,8 @@ uvloop = { version = "*", markers = "sys_platform != 'win32' and implementation_
orjson = { version = "*", optional = true }
asyncpg = { version = "*", optional = true }
aiomysql = { version = "*", optional = true }
asyncmy = { version = "^0.2.5", optional = true }
psycopg = { extras = ["pool", "binary"], version = "3.0.12", optional = true }
asyncmy = { version = "^0.2.8rc1", optional = true, allow-prereleases = true }
psycopg = { extras = ["pool", "binary"], version = "^3.0.12", optional = true }
asyncodbc = { version = "^0.1.1", optional = true }

[tool.poetry.dev-dependencies]
Expand Down
4 changes: 2 additions & 2 deletions tests/contrib/test_pydantic.py
@@ -1,5 +1,7 @@
import copy

from pydantic import BaseConfig as PydanticBaseConfig

from tests.testmodels import (
Address,
CamelCaseAliasPerson,
Expand All @@ -18,8 +20,6 @@
pydantic_queryset_creator,
)

from pydantic import BaseConfig as PydanticBaseConfig


class TestPydantic(test.TestCase):
async def asyncSetUp(self) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/schema/models_no_auto_create_m2m.py
Expand Up @@ -57,7 +57,7 @@ class Meta:
class Team(Model):
name = fields.CharField(max_length=50, pk=True, description="The TEAM name (and PK)")
key = fields.IntField()
manager: fields.ForeignKeyRelation["Team"] = fields.ForeignKeyField(
manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField(
"models.Team", related_name="team_members", null=True
)
talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField(
Expand Down
2 changes: 1 addition & 1 deletion tests/schema/models_no_db_constraint.py
Expand Up @@ -44,7 +44,7 @@ class Meta:
class Team(Model):
name = fields.CharField(max_length=50, pk=True, description="The TEAM name (and PK)")
key = fields.IntField()
manager: fields.ForeignKeyRelation["Team"] = fields.ForeignKeyField(
manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField(
"models.Team", db_constraint=False, related_name="team_members", null=True
)
talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField(
Expand Down
6 changes: 3 additions & 3 deletions tests/schema/models_schema_create.py
Expand Up @@ -43,7 +43,7 @@ class Meta:
class Team(Model):
name = fields.CharField(max_length=50, pk=True, description="The TEAM name (and PK)")
key = fields.IntField()
manager: fields.ForeignKeyRelation["Team"] = fields.ForeignKeyField(
manager: fields.ForeignKeyNullableRelation["Team"] = fields.ForeignKeyField(
"models.Team", related_name="team_members", null=True
)
talks_to: fields.ManyToManyRelation["Team"] = fields.ManyToManyField(
Expand Down Expand Up @@ -77,7 +77,7 @@ class VenueInformation(Model):
#: All this should not be part of the field description either!
capacity = fields.IntField()
rent = fields.FloatField()
team: fields.OneToOneRelation[Team] = fields.OneToOneField(
team: fields.OneToOneNullableRelation[Team] = fields.OneToOneField(
"models.Team", on_delete=fields.SET_NULL, null=True
)

Expand All @@ -86,7 +86,7 @@ class SourceFields(Model):
id = fields.IntField(pk=True, source_field="sometable_id")
chars = fields.CharField(max_length=255, source_field="some_chars_table", index=True)

fk: fields.ForeignKeyRelation["SourceFields"] = fields.ForeignKeyField(
fk: fields.ForeignKeyNullableRelation["SourceFields"] = fields.ForeignKeyField(
"models.SourceFields", related_name="team_members", null=True, source_field="fk_sometable"
)

Expand Down
13 changes: 13 additions & 0 deletions tests/test_relations.py
Expand Up @@ -328,6 +328,7 @@ async def test_select_related_sets_valid_nulls(self) -> None:
.get(id=getattr(root, "id")) # noqa
)
self.assertIsNone(retrieved_root.right)
assert retrieved_root.left is not None
self.assertEqual(retrieved_root.left, left_1st_lvl)
self.assertEqual(retrieved_root.left.left, left_2nd_lvl)

Expand Down Expand Up @@ -369,6 +370,18 @@ async def test_0_value_fk(self):
single_reload = await Single.get(id=single.id)
assert (await single_reload.extra).id == 0

tournament_0 = await Tournament.create(name="tournament zero", id=0)
await Event.create(name="event-zero", tournament=tournament_0)

e = await Event.get(name="event-zero")
id_before_fetch = e.tournament_id
await e.fetch_related("tournament")
id_after_fetch = e.tournament_id
self.assertEqual(id_before_fetch, id_after_fetch)

event_0 = await Event.get(name="event-zero").prefetch_related("tournament")
self.assertEqual(event_0.tournament, tournament_0)


class TestDoubleFK(test.TestCase):
select_match = r'SELECT [`"]doublefk[`"].[`"]name[`"] [`"]name[`"]'
Expand Down
12 changes: 6 additions & 6 deletions tests/testmodels.py
Expand Up @@ -140,7 +140,7 @@ class Dest_null(Model):

class O2O_null(Model):
name = fields.CharField(max_length=64)
event: fields.OneToOneRelation[Event] = fields.OneToOneField(
event: fields.OneToOneNullableRelation[Event] = fields.OneToOneField(
"models.Dest_null", on_delete=fields.CASCADE, related_name="address_null", null=True
)

Expand Down Expand Up @@ -668,10 +668,10 @@ class EnumFields(Model):

class DoubleFK(Model):
name = fields.CharField(max_length=50)
left: fields.ForeignKeyRelation["DoubleFK"] = fields.ForeignKeyField(
left: fields.ForeignKeyNullableRelation["DoubleFK"] = fields.ForeignKeyField(
"models.DoubleFK", null=True, related_name="left_rel"
)
right: fields.ForeignKeyRelation["DoubleFK"] = fields.ForeignKeyField(
right: fields.ForeignKeyNullableRelation["DoubleFK"] = fields.ForeignKeyField(
"models.DoubleFK", null=True, related_name="right_rel"
)

Expand Down Expand Up @@ -835,7 +835,7 @@ class Single(Model):
"""

id = fields.IntField(pk=True)
extra: fields.ForeignKeyRelation[Extra] = fields.ForeignKeyField(
extra: fields.ForeignKeyNullableRelation[Extra] = fields.ForeignKeyField(
"models.Extra", related_name="singles", null=True
)

Expand All @@ -846,10 +846,10 @@ class Pair(Model):
"""

id = fields.IntField(pk=True)
left: fields.ForeignKeyRelation[Single] = fields.ForeignKeyField(
left: fields.ForeignKeyNullableRelation[Single] = fields.ForeignKeyField(
"models.Single", related_name="lefts", null=True
)
right: fields.ForeignKeyRelation[Single] = fields.ForeignKeyField(
right: fields.ForeignKeyNullableRelation[Single] = fields.ForeignKeyField(
"models.Single", related_name="rights", null=True
)

Expand Down
2 changes: 1 addition & 1 deletion tortoise/__init__.py
Expand Up @@ -690,4 +690,4 @@ async def do_stuff():
loop.run_until_complete(connections.close_all(discard=True))


__version__ = "0.19.3"
__version__ = "0.19.4"
2 changes: 1 addition & 1 deletion tortoise/backends/base/executor.py
Expand Up @@ -512,7 +512,7 @@ async def _prefetch_direct_relation(
related_objects_for_fetch: Dict[str, list] = {}
relation_key_field = f"{field}_id"
for instance in instance_list:
if getattr(instance, relation_key_field):
if getattr(instance, relation_key_field) is not None:
key = cast(RelationalField, instance._meta.fields_map[field]).to_field
if key not in related_objects_for_fetch:
related_objects_for_fetch[key] = []
Expand Down
6 changes: 6 additions & 0 deletions tortoise/backends/mssql/schema_generator.py
Expand Up @@ -108,3 +108,9 @@ def _create_string(
comment=comment,
default=default,
)

def _get_inner_statements(self) -> List[str]:
extra = self._foreign_keys + list(dict.fromkeys(self._field_indexes))
self._field_indexes.clear()
self._foreign_keys.clear()
return extra
2 changes: 1 addition & 1 deletion tortoise/contrib/pydantic/base.py
@@ -1,7 +1,7 @@
from typing import TYPE_CHECKING, List, Type, Union

import pydantic
from pydantic import BaseModel, BaseConfig # pylint: disable=E0611
from pydantic import BaseConfig, BaseModel # pylint: disable=E0611

from tortoise import fields

Expand Down
2 changes: 1 addition & 1 deletion tortoise/contrib/pydantic/creator.py
Expand Up @@ -6,9 +6,9 @@
from pydantic import BaseConfig as PydanticBaseConfig
from pydantic import Extra

from tortoise.fields import relational, JSONField
from tortoise.contrib.pydantic.base import PydanticListModel, PydanticModel
from tortoise.contrib.pydantic.utils import get_annotations
from tortoise.fields import JSONField, relational

if TYPE_CHECKING: # pragma: nocoverage
from tortoise.models import Model
Expand Down
26 changes: 16 additions & 10 deletions tortoise/fields/base.py
Expand Up @@ -141,20 +141,26 @@ def function_cast(self, term: Term) -> Term:
GENERATED_SQL: str = None # type: ignore

# These methods are just to make IDE/Linters happy:
if TYPE_CHECKING:

def __new__(cls, *args: Any, **kwargs: Any) -> "Field[VALUE]":
return super().__new__(cls)
def __new__(cls, *args: Any, **kwargs: Any) -> "Field[VALUE]":
return super().__new__(cls)

@overload
def __get__(self, instance: None, owner: Type["Model"]) -> "Field[VALUE]":
...
@overload
def __get__(self, instance: None, owner: Type["Model"]) -> "Field[VALUE]":
...

@overload
def __get__(self, instance: "Model", owner: Type["Model"]) -> VALUE:
...
@overload
def __get__(self, instance: "Model", owner: Type["Model"]) -> VALUE:
...

def __get__(self, instance: Optional["Model"], owner: Type["Model"]):
...
def __get__(
self, instance: Optional["Model"], owner: Type["Model"]
) -> "Field[VALUE] | VALUE":
...

def __set__(self, instance: "Model", value: VALUE) -> None:
...

def __init__(
self,
Expand Down
8 changes: 1 addition & 7 deletions tortoise/fields/data.py
Expand Up @@ -298,7 +298,6 @@ def __init__(self, max_digits: int, decimal_places: int, **kwargs: Any) -> None:
self.quant = Decimal("1" if decimal_places == 0 else f"1.{('0' * decimal_places)}")

def to_python_value(self, value: Any) -> Optional[Decimal]:

if value is None:
value = None
else:
Expand Down Expand Up @@ -371,7 +370,6 @@ def to_python_value(self, value: Any) -> Optional[datetime.datetime]:
def to_db_value(
self, value: Optional[datetime.datetime], instance: "Union[Type[Model], Model]"
) -> Optional[datetime.datetime]:

# Only do this if it is a Model instance, not class. Test for guaranteed instance var
if hasattr(instance, "_saved_in_db") and (
self.auto_now
Expand Down Expand Up @@ -423,7 +421,6 @@ def to_python_value(self, value: Any) -> Optional[datetime.date]:
def to_db_value(
self, value: Optional[Union[datetime.date, str]], instance: "Union[Type[Model], Model]"
) -> Optional[datetime.date]:

if value is not None and not isinstance(value, datetime.date):
value = parse_datetime(value).date()
self.validate(value)
Expand Down Expand Up @@ -464,7 +461,6 @@ def to_db_value(
value: Optional[Union[datetime.time, datetime.timedelta]],
instance: "Union[Type[Model], Model]",
) -> Optional[Union[datetime.time, datetime.timedelta]]:

# Only do this if it is a Model instance, not class. Test for guaranteed instance var
if hasattr(instance, "_saved_in_db") and (
self.auto_now
Expand Down Expand Up @@ -542,7 +538,7 @@ class JSONField(Field[Union[dict, list]], dict, list): # type: ignore
This field can store dictionaries or lists of any JSON-compliant structure.
You can specify your own custom JSON encoder/decoder, leaving at the default should work well.
If you have ``python-rapidjson`` installed, we default to using that,
If you have ``orjson`` installed, we default to using that,
else the default ``json`` module will be used.
``encoder``:
Expand Down Expand Up @@ -679,15 +675,13 @@ def __init__(
self.enum_type = enum_type

def to_python_value(self, value: Union[int, None]) -> Union[IntEnum, None]:

value = self.enum_type(value) if value is not None else None
self.validate(value)
return value

def to_db_value(
self, value: Union[IntEnum, None, int], instance: "Union[Type[Model], Model]"
) -> Union[int, None]:

if isinstance(value, IntEnum):
value = int(value.value)
if isinstance(value, int):
Expand Down

0 comments on commit e855dd4

Please sign in to comment.