Skip to content

fix #181: allow FilterExpressions in bird tags#229

Merged
joshuadavidthomas merged 3 commits intojoshuadavidthomas:mainfrom
benbacardi:fix-181
Feb 11, 2026
Merged

fix #181: allow FilterExpressions in bird tags#229
joshuadavidthomas merged 3 commits intojoshuadavidthomas:mainfrom
benbacardi:fix-181

Conversation

@benbacardi
Copy link
Copy Markdown
Contributor

@benbacardi benbacardi commented Mar 17, 2025

This is a draft PR for #181. It's a bit of a reshuffle for parameters, in particular the Value class, where raw is now expected to be a FilterExpression (from django.template.base).

This means we can now call components as such, and the Django filters are correctly parsed:

{% bird button badge_count=users|length %}
  Users
{% endbird %}

A summary of the changes:

  • the "bits" from a bird tag (and a bird:prop tag) are parsed into key/value pairs split on =, where the value is a FilterExpression instance
  • this FilterExpression instance is passed directly to the Value class when parsing the Params from the node
  • when rendering, the FilterExpression is rendered with the context to provide the final value, and this greatly simplifies the Value.render method
  • a couple of additional lines of logic is required to handle when a value is an attribute rather than a prop, so that things like disabled=False disappear from the attrs rather than appearing as disabled="False"—i.e. to mimic current behaviour.

There is one side effect that I'm not sure whether it's a problem or not. Currently, a prop passed with an in-template value of False ends up with a value of None inside the component, but with the above changes it will come through as it was originally with False.

My biggest bugbear with it is having to test the VariableDoesNotExist before calling the filter's resolve function, because the function itself has no way to stop it swallowing the error and returning the default empty string. I'd rather not do this, but it would mean no longer supporting unquoted raw strings. Personally I think that'd be an improvement, though…

I'm only considering the PR a draft because it clearly needs your input, @joshuadavidthomas — and I'm also not sure how best to rewrite all the test_params.py tests that don't expect FilterExpression instances all over the place! I've rejiggle a couple of the other tests (but locally the asset tests are also failing for seemingly unrelated reasons, so I'm not sure what will happen when they're run under CI).

All thoughts appreciated! I realise this isn't a "one-line change" and therefore a trivial PR to review/approve/reject.

@joshuadavidthomas
Copy link
Copy Markdown
Owner

giphy

Yeah, this is going to take me a minute to work through 😅

Not sure of when I'll be able to set aside some time to dive in, but I promise I'll get to it ASAP. Thanks for taking the time to dive in and contribute! 🎉💚

@joshuadavidthomas
Copy link
Copy Markdown
Owner

Promise I haven't forgotten about this. I've been down the Rust/LSP rabbit hole working on https://github.com/joshuadavidthomas/django-language-server with any spare time I have. Sorry for just letting this sit here! Thanks for taking the time to work on it (and for the nice write up in your blog post)!

@benbacardi
Copy link
Copy Markdown
Contributor Author

No worries, I know what it's like! 🧡

Copy link
Copy Markdown
Owner

@joshuadavidthomas joshuadavidthomas left a comment

Choose a reason for hiding this comment

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

Great work @benbacardi! Truly sorry for the enormous delay in putting together a proper review. I started it a few times, but couldn't push it over the edge to finish.

Left a few comments -- some nits, some questions, a couple small blockers -- nothing major here.

Before merge, we'll need some additional test cases with actual filters in the args for a bird tag as well as some documentation updates. I'm fine taking the docs on myself, though you're welcome a crack at it if you're up for it.

I realize given the time gulf between when you submitted this and my review now, you may have lost interest in finishing it which is totally understandable. I'll leave this open for you to finish if you would like, and if not I'll take it over in a week or two, make some gut decisions around my suggestions and merge it while crediting you.

Thanks again for the contribution and for your patience!

Comment thread src/django_bird/_typing.py Outdated
Comment thread src/django_bird/params.py
Comment thread src/django_bird/params.py
Comment thread src/django_bird/templatetags/tags/bird.py
self,
name: str,
attrs: TagBits,
attrs: dict[str, str],
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Type hint needs adjustment slightly:

Suggested change
attrs: dict[str, str],
attrs: dict[str, FilterExpression],

Comment thread src/django_bird/templatetags/tags/prop.py
@joshuadavidthomas joshuadavidthomas marked this pull request as ready for review February 11, 2026 05:13
@joshuadavidthomas
Copy link
Copy Markdown
Owner

@benbacardi thank you again for the original implementation here — and I’m sorry I let this PR sit for so long.

I’ve pushed the remaining updates to get this over the line:

  • parse args with split("=", 1) in both bird and bird:prop
  • type-hint cleanup around parsed FilterExpression tag bits
  • additional tests for filter expressions in {% bird %} args (attrs + props), including None vs "None" behavior
  • docs update in docs/params.md
  • changelog update in CHANGELOG.md (with contributor credit)

I kept scope focused on landing this safely, and left broader refactor ideas for follow-up work. Thanks again for the contribution — this feature exists because of your work.

@joshuadavidthomas joshuadavidthomas merged commit 82c433b into joshuadavidthomas:main Feb 11, 2026
25 checks passed
joshuadavidthomas pushed a commit that referenced this pull request Feb 19, 2026
Two recent PRs introduced uncovered lines that pushed coverage from ~98.5%
to 97.68%:

- `ab7f667` (fix 3.14) added a Windows-only fallback in templates.py that
  can't be reached on Linux CI — mark with `pragma: no cover`
- `82c433be` (#229 FilterExpressions) added defensive code paths in
  params.py — add tests for the catch-all TypeError case and the
  `is_attr=True` with `False` value branch

https://claude.ai/code/session_01Rfs15Qp5BaeXiK8TLExfe7
joshuadavidthomas added a commit that referenced this pull request Feb 19, 2026
* Fix code coverage dropping below 98% threshold

Two recent PRs introduced uncovered lines that pushed coverage from ~98.5%
to 97.68%:

- `ab7f667` (fix 3.14) added a Windows-only fallback in templates.py that
  can't be reached on Linux CI — mark with `pragma: no cover`
- `82c433be` (#229 FilterExpressions) added defensive code paths in
  params.py — add tests for the catch-all TypeError case and the
  `is_attr=True` with `False` value branch

https://claude.ai/code/session_01Rfs15Qp5BaeXiK8TLExfe7

* Raise coverage threshold to 100% with tests for all uncovered lines

Adds tests covering 15 previously uncovered lines across 5 files:
- apps.py: pre_ready/ready plugin hook execution
- components.py: fallback name when template is outside component dirs
- manifest.py: ValueError in normalize_path when path is prefix of BASE_DIR
- params.py: render_props early return when component.nodelist is None
- staticfiles.py: AssetTypes.reset, Asset.render/absolute_path,
  get_component_assets filtering, BirdAssetStorage.url(None),
  BirdAssetFinder legacy all param, BirdAssetFinder.list error handling

https://claude.ai/code/session_01Rfs15Qp5BaeXiK8TLExfe7

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
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.

2 participants