from datetime import datetime
class Filter():
def __init__(self, name):
self.name = name
def __gt__(self, value):
return {
"field": self.name,
"operator": ">",
"value": value
}
class FieldMeta(type):
def __getattr__(cls, name):
return Filter(name)
class Field(metaclass=FieldMeta):
pass
print(Field.end > datetime(2024, 1, 1))
This gives: {'field': 'end', 'operator': '>', 'value': datetime.datetime(2024, 1, 1, 0, 0)}
You can make python return arbitrary values for comparisons by overriding __gt__ (and lt, eq) on the first operand (which we control here since it is a Field class), it doesn't have to be a bool.Edit:
You can even make a little adapter to use this with the current filter system if you really want to:
from datetime import datetime
class Filter():
def __init__(self, name):
self.name = name
def __gt__(self, value):
return {
"field": self.name,
"operator": "gt",
"value": value
}
def __lt__(self, value):
return {
"field": self.name,
"operator": "lt",
"value": value
}
def __eq__(self, value):
return {
"field": self.name,
"operator": "eq",
"value": value
}
class FieldMeta(type):
def __getattr__(cls, name):
return Filter(name)
class Field(metaclass=FieldMeta):
pass
def _(*args):
kwargs = {}
for arg in args:
k = arg["field"] + "__" + arg["operator"]
kwargs[k] = arg["value"]
return kwargs
def filter(**kwargs):
for k, v in kwargs.items():
print(f"{k} = {v}")
filter(**_(Field.end > datetime(2024, 1, 1)))
This printsend__gt = 2024-01-01 00:00:00
And despite my misgivings, it’s really ergonomic.
Also, apparently SQLAlchemy does exactly what I proposed so apparently they are erring in their ways too.
I honestly don’t find it that bad.