tortoise-orm
tortoise-orm copied to clipboard
DatetimeField Field identification error
Describe the bug I have a project that is a table created by django with a time field called models.DateTimeField that records the time in the database as follows: 2022-07-29 11:00:24.794 I created a new fastapi project, using tortoise-orm to connect to Oracle database There are two problems:
- When I define the time field in the model, if I define the field like this: EDIT_DATE = fields.DatetimeField(description=' Modify date ') The following error is reported: Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 693, in lifespan async with self.lifespan_context(app) as maybe_state: File "/usr/lib64/python3.11/contextlib.py", line 210, in aenter return await anext(self.gen) ^^^^^^^^^^^^^^^^^^^^^ File "/home/whatsup_alarm/core/event.py", line 29, in lifespan await get_whatsup_servers_information() File "/home/whatsup_alarm/core/event.py", line 41, in get_whatsup_servers_information objs = await NetDeviceSysMonitor.filter(**filter_par) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/queryset.py", line 1151, in _execute instance_list = await self._db.executor_class( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/backends/base/executor.py", line 139, in execute_select instance: "Model" = self.model._init_from_db(**dict(row_items[:current_idx])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/models.py", line 754, in _init_from_db setattr(self, model_field, field.to_python_value(kwargs[key])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/fields/data.py", line 354, in to_python_value value = parse_datetime(value) ^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/iso8601/iso8601.py", line 124, in parse_date raise ParseError(e) iso8601.iso8601.ParseError: expected string or bytes-like object, got 'datetime.date'
If I define this field like this: EDIT_DATE = fields.DateField(description=' Modify date ') I can only get the date information, not the time information
If I define this field like this: EDIT_DATE = fields.TimeField(description=' Modify date ') The following error is reported: ERROR: Traceback (most recent call last): File "/usr/local/lib/python3.11/site-packages/starlette/routing.py", line 693, in lifespan async with self.lifespan_context(app) as maybe_state: File "/usr/lib64/python3.11/contextlib.py", line 210, in aenter return await anext(self.gen) ^^^^^^^^^^^^^^^^^^^^^ File "/home/whatsup_alarm/core/event.py", line 29, in lifespan await get_whatsup_servers_information() File "/home/whatsup_alarm/core/event.py", line 41, in get_whatsup_servers_information objs = await NetDeviceSysMonitor.filter(**filter_par) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/queryset.py", line 1151, in _execute instance_list = await self._db.executor_class( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/backends/base/executor.py", line 139, in execute_select instance: "Model" = self.model._init_from_db(**dict(row_items[:current_idx])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/models.py", line 754, in _init_from_db setattr(self, model_field, field.to_python_value(kwargs[key])) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/fields/data.py", line 444, in to_python_value if timezone.is_naive(value): ^^^^^^^^^^^^^^^^^^^^^^^^ File "/usr/local/lib/python3.11/site-packages/tortoise/timezone.py", line 84, in is_naive return value.utcoffset() is None ^^^^^^^^^^^^^^^ AttributeError: 'datetime.date' object has no attribute 'utcoffset'
- The field names in the database are all uppercase. When I use lowercase field names in the model definition, I get an error, indicating that the field name is illegal
Did I misdescribe the problem? Why is there no response
@phw50365036, unfortunately, none of the contributors have access to an Oracle database. Recently we implemented query parametrization but we haven't tested it properly against Oracle. We would appreciate contribution from someone having access to an Oracle DB.
I can connect to Oracle database, but I need to figure out the whole process of query to know where the problem occurs, which is difficult for me. I built a test environment, and the model looked like this:
from tortoise import fields, models
class ConfigTask(models.Model):
ID = fields.IntField(primary_key=True)
BEGIN_TIME = fields.DatetimeField()
class Meta:
table = 'CONFIG_TASK'
When I run a query:
objs = await ConfigTask.filter(ID=1935)
for obj in objs:
print(obj.ID, obj.BEGIN_TIME)
I printed a few places, and I found some anomalies:
parse_date 2020-06-23 17:00:00.000000
key, model_field, field BEGIN_TIME BEGIN_TIME <tortoise.fields.data.DatetimeField object at 0x7f47cbb766d0>
value 2020-06-23 <class 'datetime.date'>
parse_date 2020-06-23
iso8601 parse_date was called twice, the two results are inconsistent, the first time is right, the second time is wrong, it seems that the error is reported after the second time, I do not know why it will be called twice, if you need any other information, I can cooperate to do a test, I did not think of a good test method, is familiar with the source code
After a day of testing, the datetime field reported the reason for the error in backends\oracle\client.py
class OraclePoolConnectionWrapper(PoolConnectionWrapper):
def _timestamp_convert(self, value: bytes) -> datetime.date:
try:
return parse_datetime(value.decode()).date()
except ValueError:
return parse_datetime(value.decode()[:-32]).astimezone(tz=pytz.utc)
async def __aenter__(self) -> "asyncodbc.Connection":
connection = await super().__aenter__()
if getattr(self.client, "database", False) and not hasattr(connection, "current_schema"):
await connection.execute(f'ALTER SESSION SET CURRENT_SCHEMA = "{self.client.user}"')
await connection.execute("ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD'")
await connection.execute(
"ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT = 'YYYY-MM-DD\"T\"HH24:MI:SSTZH:TZM'"
)
await connection.add_output_converter(
pyodbc.SQL_TYPE_TIMESTAMP, self._timestamp_convert
)
setattr(connection, "current_schema", self.client.user)
return connection
return parse_datetime(value.decode()).date() converts all types of data to date if return parse_datetime(value.decode()),Successful But I still don't know why time resolution is called twice, and why it is necessary to distinguish the case of the field