From 5de5e25df615ba14db96bc020419005cab0f4904 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 9 May 2024 15:49:27 +0530 Subject: [PATCH] fix: datetime comparison in QB (#26364) closes https://github.com/frappe/frappe/issues/26363 --- frappe/database/database.py | 4 ++-- frappe/query_builder/terms.py | 5 ++++- frappe/tests/test_db.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index cb47d91d44..56bfc1cf84 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -1105,7 +1105,7 @@ class Database: """Return True if at least one row exists.""" return frappe.get_all(doctype, limit=1, order_by=None, as_list=True) - def exists(self, dt, dn=None, cache=False): + def exists(self, dt, dn=None, cache=False, *, debug=False): """Return the document name of a matching document, or None. Note: `cache` only works if `dt` and `dn` are of type `str`. @@ -1138,7 +1138,7 @@ class Database: dt = dt.copy() # don't modify the original dict dt, dn = dt.pop("doctype"), dt - return self.get_value(dt, dn, ignore=True, cache=cache, order_by=None) + return self.get_value(dt, dn, ignore=True, cache=cache, order_by=None, debug=debug) def count(self, dt, filters=None, debug=False, cache=False, distinct: bool = True): """Return `COUNT(*)` for given DocType and filters.""" diff --git a/frappe/query_builder/terms.py b/frappe/query_builder/terms.py index c33f890a39..46c73dd518 100644 --- a/frappe/query_builder/terms.py +++ b/frappe/query_builder/terms.py @@ -1,10 +1,11 @@ -from datetime import time, timedelta +from datetime import datetime, time, timedelta from typing import Any from pypika.queries import QueryBuilder from pypika.terms import Criterion, Function, ValueWrapper from pypika.utils import format_alias_sql +import frappe from frappe.utils.data import format_time, format_timedelta @@ -56,6 +57,8 @@ class ParameterizedValueWrapper(ValueWrapper): self.value = format_timedelta(self.value) elif isinstance(self.value, time): self.value = format_time(self.value) + elif isinstance(self.value, datetime): + self.value = frappe.db.format_datetime(self.value) sql = self.get_value_sql( quote_char=quote_char, diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 9469abb6b7..715522d868 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -17,6 +17,7 @@ from frappe.query_builder.functions import Concat_ws from frappe.tests.test_query_builder import db_type_is, run_only_if from frappe.tests.utils import FrappeTestCase, patch_hooks, timeout from frappe.utils import add_days, now, random_string, set_request +from frappe.utils.data import now_datetime from frappe.utils.testutils import clear_custom_fields @@ -504,6 +505,19 @@ class TestDB(FrappeTestCase): self.assertEqual(frappe.db.exists(dt, [["name", "=", dn]]), dn) + def test_datetime_serialization(self): + dt = now_datetime() + dt = dt.replace(microsecond=0) + self.assertEqual(str(dt), str(frappe.db.sql("select %s", dt)[0][0])) + + frappe.db.exists("User", {"creation": (">", dt)}) + self.assertIn(str(dt), str(frappe.db.last_query)) + + before = now_datetime() + note = frappe.get_doc(doctype="Note", title=frappe.generate_hash(), content="something").insert() + after = now_datetime() + self.assertEqual(note.name, frappe.db.exists("Note", {"creation": ("between", (before, after))})) + def test_bulk_insert(self): current_count = frappe.db.count("ToDo") test_body = f"test_bulk_insert - {random_string(10)}"