Search code examples
pythondjangodjango-adminsuperclass

How to access subcclass attributes in super class methods, without passing them in


How does one access a derived class'es property (static variable in other languages), in its base classes method? I.e.

class Base:
    @classmethod
    def get_list(cls):
        return [1, 2, cls.x]

class Derived(Base):
    x = 3
    foo = Base.get_list()    

Derived.foo # Hope to have it set to [1, 2, 3]

I wish to not pass x directly to the base class, i.e. foo = Base.get_list(x) because I have a lot of methods to be called with the same variables.

EDIT

The comments have asked for context. I tried to make a minimum reproducible example to spare you guys the details.

Here's the full context: So its actually to do with Django Admin. I have a abstract model. Then I have various admin interfaces for the various concrete implementation of the abstract model. As you can guess I created an abstract admin model to handle the common logic. A part of handling the common admin logic, e.g. generating properties on the derived admin model.

So for example the list display, here's the logic to create the list display:

class FactAdmin(admin.ModelAdmin):
    @classmethod
    def build_list_display(cls, *custom):
        return (
            'created_utc',
            *custom,
            'foo__bar__abbreviation',
            'foo__baz__name',
            'foo__caz__name',
            'foo__daz__description',
            'foo__eaz__code',
        )

@admin.register(FooEntry)
class FoopEntryAdmin(FactAdmin):
    fieldsets = FactAdmin.build_list_display('cannon_ball', 'pogo_stick')

So in the base admin class, it keeps reusing a list of custom fields, custom models, and other things. So instead of passing them in each time, for each property, it seemed more DRY to set them as static attributes. Hence I wished to set them as static member fields on the derived class, and access them in the base class.


Solution

  • Accessing subclass attributes in a superclass classmethod called on the subclass isn't a problem. This works as expected:

    class Base:
        @classmethod
        def get_list(cls):
            return [1, 2, cls.x]
    
    
    class Derived(Base):
        x = 3
    
    
    assert Derived.get_list() == [1, 2, 3]
    

    However, you can't easily call that classmethod right in the subclass's class body, as at that time, the class object for Derived and indeed the name Derived don't exist yet. (You could call it in methods, classmethods and staticmathods, because their body is evaluated when they are invoked, and not already when they are defined. But that doesn't help here.) And if called on Base, it obviously lacks x. (And being a Python classmethod, get_list has to be explicitly called on something. We can't just make an unqualified call as in Java.)

    So

    class Derived(Base):
        x = 3
        foo = get_list()
    

    results in NameError: name 'get_list' is not defined,

    class Derived(Base):
        x = 3
        foo = Base.get_list()
    

    results in AttributeError: type object 'Base' has no attribute 'x' and

    class Derived(Base):
        x = 3
        foo = Derived.get_list()
    

    results in NameError: name 'Derived' is not defined.

    But all is not lost. Let's not forget that Python allows us to create and set class attributes from outside the class. So this works:

    class Base:
        @classmethod
        def get_list(cls):
            return [1, 2, cls.x]
    
    
    class Derived(Base):
        x = 3
    
    
    Derived.foo = Derived.get_list()
    
    
    assert Derived.foo == [1, 2, 3]