Remix.run Logo
echoangle an hour ago

> I can't quite picture how operator overloading would look like, could you give an example?

Instead of this:

self.filter(end__gt=self._midnight(today))

You could write a "Field" class that implements __getattr__ and __gt__ so you could do

self.filter(Field.end > self._midnight(today))

The "Field.end > self._midnight(today)" would evaluate to an object that would just store "my field name is end and my value needs to be larger than xyz".

filter() can then look into its argument list and construct the filter criteria from the passed Field objects instead of the key value pairs as it does now.

pbalau 43 minutes ago | parent [-]

if self._midnight(today) returns a datetime object, than:

   self.filter(end__gt=self._midnight(today))
will evaluate to:

   self.filter(end__gt=<some_datetime_object>)

While

    self.filter(Field.end > self._midnight(today))
will evaluate to:

   self.filter(<True/False>)
echoangle 19 minutes ago | parent [-]

Not if you do the magic with getattr and comparison overrides. You actually need to do it on the metaclass because the Field as I wrote it isn't an instance but this works:

    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 prints

end__gt = 2024-01-01 00:00:00