Skip to content

Add optional context_object_name to TemplateColumn and make extra_context optionally callable#931

Open
jieter wants to merge 10 commits intomasterfrom
feature/improve-template-column
Open

Add optional context_object_name to TemplateColumn and make extra_context optionally callable#931
jieter wants to merge 10 commits intomasterfrom
feature/improve-template-column

Conversation

@jieter
Copy link
Owner

@jieter jieter commented Oct 17, 2023

fixes: #928

@jieter jieter force-pushed the feature/improve-template-column branch from 475dd3d to fe78309 Compare October 17, 2023 12:12
@danielvdp danielvdp self-requested a review October 25, 2023 13:24
Copy link
Collaborator

@danielvdp danielvdp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interessant. Ik heb wat vragen en suggesties

}
additional_context.update(self.extra_context)

extra_context = self.extra_context
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ik vind "additional" en "extra" content wel dubbelop? Waar is additional voor en waar is extra voor? Kunnen we de namen niet wat aanpassen in termen van waar ze voor dienen?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to improve on this

additional_context.update(self.extra_context)

extra_context = self.extra_context
if callable(extra_context):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ik had uit de docstring begrepen dat extra_context een dict was - dus dan zou ik de callable meegeven in een dict ... maar blijkbaar moet je een callable toewijzen aan extra_context? Dus voor mij is de docstring van verwarrend

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should return a callable returning a dict.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to clarify it in the docstring.

table = Table([{"name": "Bob"}])
self.assertEqual(list(table.as_values()), [["Name"], ["Bob"]])

def test_extra_context_callable(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test voor extra context dict was er al toch?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, just added one.

def test_extra_context_callable(self):
class Table(tables.Table):
size = tables.TemplateColumn(
"{{ size }}", extra_context=lambda record: {"size": record["clothes"]["size"]}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ik zou het wel beter vinden als er meer optional arguments getest zouden worden? table / value / bound column - ik zie niet goed hoe dat hier terug komt.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added another column with value.

@federicobond
Copy link
Contributor

Would it make sense to extract context building so it can be done in an overrideable get_context_data method?

Here's an example use case where I want to resolve a second accessor and pass it to the template. This is the original code:

class NameAndDescriptionColumn(tables.Column):
    def __init__(self, *args, **kwargs):
        description_accessor = kwargs.pop("description_accessor", "description")
        super().__init__(*args, **kwargs)
        self.description_accessor = (
            Accessor(description_accessor) if description_accessor else None
        )

    def render(self, record):
        return format_html(
            """<span class="h5">{name}</span>
            <br>
            <span class="fs-5 text-body">{description}</span>""",
            name=self.accessor.resolve(record),
            description=self.description_accessor.resolve(record),
        )

With a get_context_data method, I could transform it into a TemplateColumn subclass with an idiomatic Django API:

class NameAndDescriptionColumn(tables.TemplateColumn):
    template_code = """<span class="h5">{{ value }}</span><br>
        <span class="fs-5 text-body">{{ description }}</span>"""

    def __init__(self, *args, **kwargs):
        description_accessor = kwargs.pop("description_accessor", "description")
        super().__init__(*args, **kwargs)
        self.description_accessor = (
            Accessor(description_accessor) if description_accessor else None
        )

    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["description"] = self.description_accessor.resolve(ctx["record"])
        return ctx

This is arguably more flexible than passing callable to extra_context, but I'm not sure how well you think it fits with the django_tables2 design.

@federicobond
Copy link
Contributor

Hi @jieter, I'm keen to move this forward. How can I help?

Would you be interested in reviewing a rebased PR with a changelog entry if I make one?

@jieter
Copy link
Owner Author

jieter commented Dec 22, 2025

Would it make sense to extract context building so it can be done in an overrideable get_context_data method?

Excellent idea, I updated the PR to use that pattern.

@federicobond If you can review the current state of the PR I think we can get this released quickly.

Copy link
Contributor

@federicobond federicobond left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left a couple of comments. One thought: it might be helpful to add a short note in the docs about overriding get_context_data in TemplateColumn subclasses.

For use cases that need more complex or dynamic context, defining a custom column (similar to class-based views) feels quite idiomatic in Python/Django:

class MyColumn(TemplateColumn):
    def get_context_data(self, **kwargs):
        ctx = super().get_context_data(**kwargs)
        ctx["some_variable"] = my_function(ctx["record"])
        return ctx

Compared to a lambda-based extra_context, which can become a bit harder to read once the logic grows:

my_column = TemplateColumn(
    extra_context=lambda record: {"some_variable": my_function(record)}
)

Totally optional, but having this mentioned somewhere in the docs could help steer users toward the pattern that best fits their use case.

# attaches the context to the table as a gift to `TemplateColumn`.
parent_context = getattr(table, "context", Context())

with parent_context.update(self.get_context_data(table=table, **kwargs)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd extract local variable context for readability.

Suggested change
with parent_context.update(self.get_context_data(table=table, **kwargs)):
context = self.get_context_data(table=table, **kwargs)
with parent_context.update(context):

# attaches the context to the table as a gift to `TemplateColumn`.
context = getattr(table, "context", Context())
additional_context = {
def get_context_data(self, record, table, value, bound_column, **kwargs):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these be kwargs-only? A specific positional argument order can make the API harder to change later

Suggested change
def get_context_data(self, record, table, value, bound_column, **kwargs):
def get_context_data(self, *, record, table, value, bound_column, **kwargs):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Make TemplateColumn more dynamic

3 participants