Add optional context_object_name to TemplateColumn and make extra_context optionally callable#931
Add optional context_object_name to TemplateColumn and make extra_context optionally callable#931
context_object_name to TemplateColumn and make extra_context optionally callable#931Conversation
…a_context` optionally callable fixes: #928
cba0965 to
7691bf3
Compare
475dd3d to
fe78309
Compare
danielvdp
left a comment
There was a problem hiding this comment.
Interessant. Ik heb wat vragen en suggesties
| } | ||
| additional_context.update(self.extra_context) | ||
|
|
||
| extra_context = self.extra_context |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
I tried to improve on this
| additional_context.update(self.extra_context) | ||
|
|
||
| extra_context = self.extra_context | ||
| if callable(extra_context): |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
You should return a callable returning a dict.
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
test voor extra context dict was er al toch?
| def test_extra_context_callable(self): | ||
| class Table(tables.Table): | ||
| size = tables.TemplateColumn( | ||
| "{{ size }}", extra_context=lambda record: {"size": record["clothes"]["size"]} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
I added another column with value.
|
Would it make sense to extract context building so it can be done in an overrideable 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 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 ctxThis 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. |
|
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? |
Co-authored-by: danielvdp <danielvdp@users.noreply.github.com>
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. |
federicobond
left a comment
There was a problem hiding this comment.
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 ctxCompared 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)): |
There was a problem hiding this comment.
Nit: I'd extract local variable context for readability.
| 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): |
There was a problem hiding this comment.
Should these be kwargs-only? A specific positional argument order can make the API harder to change later
| def get_context_data(self, record, table, value, bound_column, **kwargs): | |
| def get_context_data(self, *, record, table, value, bound_column, **kwargs): |
fixes: #928