Search code examples
pythondjangofunctools

How can I set an attribute on a functools.partialmethod?


I have ported the following code from Django 2.2 to Django 3.1, and replaced django.utils.functional.curry with functools.partialmethod:

class CurrencyField(models.DecimalField):
    description = 'Currency amount'

    def __init__(self, *args, **kwargs):
        kwargs.setdefault('max_digits', 9)
        kwargs.setdefault('decimal_places', 2)
        super().__init__(*args, **kwargs)

    def contribute_to_class(self, cls, name):
        super().contribute_to_class(cls, name)
        if hasattr(cls, '_get_value_as_currency'):
            curried = partialmethod(cls._get_value_as_currency, field=self)
            curried.admin_order_field = self.name
            curried.short_description = self.verbose_name
            setattr(cls, f'get_{self.name}_as_currency', curried)

The method works correctly, however the admin_order_field and short_description attributes don't "stick" to the method like they did when I used curry.

getattr(cls, f'get_{self.name}_as_currency').short_description
*** AttributeError: 'function' object has no attribute 'short_description'

Is it possible to set attributes on a partialmethod, or will I need to replace this with some other mechanism?


Solution

  • So after a week or two of no answers, and a little more experimentation, I've given up on trying to get an attribute to stick to a partialmethod and instead dynamically defined a local function, and used the new @admin.display decorator from Django 3.2 to set the admin attributes, though the same could have been achieved without that syntactic sugar:

        def contribute_to_class(self, cls, name):
            super().contribute_to_class(cls, name)
            if hasattr(cls, '_get_value_as_currency'):
                @admin.display(
                    ordering=self.name,
                    description=self.verbose_name,
                )
                def getter(obj):
                    return obj._get_value_as_currency(self)
                setattr(cls, f'get_{self.name}_as_currency', getter)