#16676 closed Bug (fixed)
The 'add' filter should stringify value or argument if the other value is a string
| Reported by: | dtrebbien | Owned by: | Aymeric Augustin |
|---|---|---|---|
| Component: | Template system | Version: | 1.3 |
| Severity: | Normal | Keywords: | |
| Cc: | dtrebbien@… | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
The 1.3 documentation for the add filter states:
This filter will first try to coerce both values to integers. If this fails, it'll attempt to add the values together anyway. This will work on some data types (strings, list, etc.) and fail on others. If it fails, the result will be an empty string.
When the value and argument represent the second case ("If this fails, it'll attempt to add the values together anyway.") and one is a string while the other is a number, the result of the add filter is simply the value.
Similar to the fix for ticket #393, I think that for this second case and if either value or argument to the filter is a string, the other value should be stringified.
Test cases
{{ 'test'|add:2 }}
Result is: test
Expected result: test2
{{ 2|add:'test' }}
Result is: 2
Expected result: 2test
Workarounds
For the first test case, one workaround is to use a {% with %} tag:
{% with 2|stringformat:'d' as arg_as_str %}
{{ 'test'|add:arg_as_str }}
{% endwith %}
For the second test case, the stringformat filter can be used to convert the number to a string before add is applied:
{{ 2|stringformat:'d'|add:'test' }}
Related tickets
Attachments (2)
Change History (15)
comment:1 by , 14 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
by , 14 years ago
| Attachment: | 16676.patch added |
|---|
comment:2 by , 14 years ago
| Has patch: | set |
|---|
comment:3 by , 14 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
comment:4 by , 14 years ago
| Triage Stage: | Ready for checkin → Design decision needed |
|---|
Although the documentation has stated (for quite some time) that it returns an empty string, the current behavior of returning the original input is an acceptable response to an error. The docs say:
In case of error, they should return either the original input or an empty string -- whichever makes more sense.
There could be minor elements of backwards incompatibility if we change this. Perhaps just changing the docs to match the behavior would be alright to do here?
comment:5 by , 14 years ago
In theory, I find it slightly better to display nothing that to display a wrong result. For instance, if someone adds total price and shipping cost with this filter (an horrible idea for sure, and a strong argument against the existence of the add filter), and accidentally has one of these values represented as a string, the resulting price would be wrong.
In practice, I understand the value of preserving backwards compatibility.
I'm attaching an alternative patch that changes the docs to match the code. I'm still slightly in favor of the initial patch. But what really matters is to resolve the inconsistency between the code and the docs, one way or another.
comment:6 by , 14 years ago
I prefer Aymeric's first patch. Preserving backward compatibility with behavior that contradicts the docs does not seem preferable to making the behavior conform to current wording. Also, I think that returning an empty string is more consistent with the template system's tendency to return nothing if there was an error (e.g.: attempting to print an undefined variable).
comment:7 by , 14 years ago
| Owner: | changed from to |
|---|
comment:8 by , 14 years ago
An approach would be to return settings.TEMPLATE_STRING_IF_INVALID (which defaults to an empty string). However, I'd be happy enough with the doc being modified to reflect the current behaviour.
comment:9 by , 14 years ago
| Triage Stage: | Design decision needed → Accepted |
|---|
The fix here is to change the template to return the empty string as the documentation says. Returning the left-hand-side on a failure is weird. The auto-stringifying-behavior suggested doesn't sound like it matches Django very wel, so let's not do that.
comment:10 by , 14 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
The initial patch was reviewed and marked as RFC by Russell. Since you just confirmed that the solution it uses is correct, I'm marking this ticket as RFC again (even though I'm the author).
The patch that must be committed is the first one, "16676.patch".
comment:11 by , 14 years ago
Question: should settings.TEMPLATE_STRING_IF_INVALID be returned instead of a systematic hardcoded empty string, or should that setting be reserved for invalid variables?
comment:12 by , 14 years ago
No, TEMPLATE_STRING_IF_INVALID is for variables; there's no precedent in using it for filters, and I don't think we should expand it at all. It already causes enough problems!
In Python it's an error to add a string and an int:
In this case, the proper output is an empty string, as the docs says. I could confirm that Django returns the first value. It's a bug.
However,
|adddoesn't really make sense with strings.+on strings is just concatenation, and concatenation is natively provided by the template engine.Instead of
{{ strval|add:intval }}, just write{{ strval }}{{ intval }}.For this reason, I don't like the idea of converting both values to strings.
I'm attaching a patch with tests (actually, the tests existed, but they contradicted the documentation; I think the documentation has precedence). I also fixed a naked except -- we never want to catch BaseException.