Opened 8 years ago

Closed 11 months ago

Last modified 8 months ago

#3371 closed New feature (wontfix)

Filter to get dict's value by key name (for the purpose of enabling variable lookups in the template)

Reported by: Alex Dedul Owned by: nobody
Component: Template system Version: master
Severity: Normal Keywords: template filter dict key
Cc: Triage Stage: Unreviewed
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

Filter's description is in summary. Example usage:

{{ dict|key:key_name }}

Filter:

def key(d, key_name):
    return d[key_name]
key = register.filter('key', key)

Looking that #2741 is closed as wontfix i have a little hope that it could be added to trunk, but i'll try :).

Change History (24)

comment:1 Changed 8 years ago by ubernostrum

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

What does {{ dict|key:key_name }} add that {{ dict.key_name }} doesn't already do (the dot syntax already does a dictionary key lookup as the first attempt to resolve to a value)?

Also, the proposed implementation doesn't meet the standards for built-in filters: it doesn't catch KeyError and so could propagate an exception all the way back to the template (filters should fail silently -- in this case the appropriate behavior is to catch KeyError and return an empty string or the value of TEMPLATE_STRING_IF_INVALID).

comment:2 Changed 8 years ago by Alex Dedul

With {{ dict|key:key_name }} key_name holds actual key dict should be looked up while {{ dict.key_name }} look ups only 'key_name' key in dict. So with the former if key_name variable contains value 'test_key' {{ dict|key:key_name }} will result in

dict['test_key']

Here is improved version of filter(silly me), i will make patch against trunk, add documentation and tests in case it will be approved by core devs.

def key(d, key_name):
    try:
        value = d[key_name]
    except:
        from django.conf import settings

        value = settings.TEMPLATE_STRING_IF_INVALID

    return value
key = register.filter('key', key)

comment:3 Changed 8 years ago by Alex Dedul

Ops, another version - catch only KeyError exception:

def key(d, key_name):
    try:
        value = d[key_name]
    except KeyError:
        from django.conf import settings

        value = settings.TEMPLATE_STRING_IF_INVALID

    return value
key = register.filter('key', key)

comment:4 Changed 8 years ago by Simon G. <dev@…>

  • Triage Stage changed from Unreviewed to Design decision needed

comment:5 Changed 7 years ago by jacob

  • Resolution set to wontfix
  • Status changed from new to closed

This is out of scope for the built-in template engine (though it sounds like a great utility to have in your toolbox). Maybe post this to djangosnippets.org?

comment:6 Changed 7 years ago by nullie

  • Resolution wontfix deleted
  • Status changed from closed to reopened

Why is it out of scope? Implementation can be really simple:

{{
def get(d, key):

return d.get(key, )

register.filter(get)
}}

And I here questions about this again and again.

comment:7 Changed 7 years ago by nullie

def get(d, key):
    return d.get(key, '')

register.filter(get)

comment:8 Changed 7 years ago by ubernostrum

  • Resolution set to wontfix
  • Status changed from reopened to closed

Reopening a ticket which has been closed by one of Django's lead developers is impolite. Try the developers' mailing list if you have followup questions.

comment:9 Changed 5 years ago by slacy

Instead of adding a special filter for this case, I think it would make sense to amend the template variable processing in general to have an extra step:

  • Dictionary lookup. Example: foobar?
  • Attribute lookup. Example: foo.bar
  • Method call. Example: foo.bar()
  • List-index lookup. Example: foo[bar]
  • Dictionary/List lookup by reference. Example: foo[resolve_variable("bar")]

comment:10 Changed 11 months ago by anonymous

  • Easy pickings set
  • Resolution wontfix deleted
  • Severity set to Normal
  • Status changed from closed to new
  • Summary changed from Filter to get dict's value by key name to Filter to get dict's value by key name (for the purpose of enabling variable lookups in the template)
  • Triage Stage changed from Design decision needed to Unreviewed
  • Type set to New feature
  • UI/UX unset

Dear "Django's lead developers":

Being unable to perform variable lookups in the template is the bane of Django.
Please be aware that after reviewing #12486, #1495, #959, and #577; I have determined that #3371 most appropriately addresses this source of Django's impotence. The individuals responsible for closing this ticket (@jacob, @ubernostrum) have provided nothing but non sequitur as motivation for its closing. I find it necessary to reopen #3371, and I will continue to reopen it until Django is capable of performing variable lookups in the template, or until a SOUND reason is given for insisting that Django inflict such an undue handicap on perfectionists with deadlines.

For the purpose of exemplifying the need for variable lookups in the template, consider a list of students ["Bob", ..., "Alice"] who enrol in courses ["Course1", ..., "CourseN"] and earn final marks of {"Bob" : {"Course1" : bobs_mark_for_course1, ..., "CourseN" : bobs_mark_for_courseN}, ..., "Alice" : {"Course1" : alices_mark_for_course1, ..., "CourseN" : alices_mark_for_courseN}}

Displaying this table of information is a template level concern, and should be performed trivially as follows:

Step 1 - In the views.py:

c[''students''] = ["Bob", ..., "Alice"] # NOTE student names are unique in this example!
c[''courses''] = ["Course1", ..., "CourseN"] # ditto the above comment for student names
c[''marks''] = {"Bob" : {"Course1" : bobs_mark_for_course1, ..., "CourseN" : bobs_mark_for_courseN}, ..., "Alice" : {"Course1" : alices_mark_for_course1, ..., "CourseN" : alices_mark_for_courseN}}

return render(request, c, "display.html")

Step 2 - In the template display.html:

<table>

<tr>

<td>Student Name</td>
{% for course in courses %}

<td>{{course}} Grade</td>

{% endfor %}

</tr>

{% for student in students %}

<tr>

<td>{{student}}</td>
{% for course in courses %}

<td>{{marks.student.course}}</td> {# ALERT Variable lookup! #}

{% endfor %}

</tr>

{% endfor %}

</table>


Sadly, it appears that this trivial exercise in templating is "out of scope" (@jacob) or even "impolite" (@ubernostrum). While not providing any alternatives to (or even any sound reasons for rejecting) @nullie's fix, one might speculate that the cited "Django's lead developers" would rather we perform such templating in the views.py:

Step 1 - In the views.py:
students = ["Bob", ..., "Alice"] # NOTE student names are unique in this example!
courses = ["Course1", ..., "CourseN"] # ditto the above comment for student names
marks = {"Bob" : {"Course1" : bobs_mark_for_course1, ..., "CourseN" : bobs_mark_for_courseN}, ..., "Alice" : {"Course1" : alices_mark_for_course1, ..., "CourseN" : alices_mark_for_courseN}}

table_to_display = []
html = table_to_display.append
html("<table><tr><td>Student Name</td>")
for course in courses:

html("<td>")
html(course)
html(" Grade</td>"

html("</tr>")
for student in students:

html("<tr><td>")
html(student)
html("</td>")
for course in courses:

html("<td>")
html(marks[student][course])
html("</td>")

html("</tr>")

html("</table>")

c[''marks_table''] = "".join(table_to_display)
return render(request, c, "display.html")

Step 2 - In the template display.html:
{{marks_table|safe}}

I'm sure that any person involved in a web-development project would agree that styling/upgrading/extending or otherwise maintaining under this paradigm is a doomed and futile effort.

Using @nullie's fix, our task is accomplished with the following:
Step 1 - In the views.py

c[''students''] = ["Bob", ..., "Alice"] # NOTE student names are unique in this example!
c[''courses''] = ["Course1", ..., "CourseN"] # ditto the above comment for student names
c[''marks''] = {"Bob" : {"Course1" : bobs_mark_for_course1, ..., "CourseN" : bobs_mark_for_courseN}, ..., "Alice" : {"Course1" : alices_mark_for_course1, ..., "CourseN" : alices_mark_for_courseN}}

return render(request, c, "display.html")

Step 2 - In the template display.html:

<table>

<tr>

<td>Student Name</td>
{% for course in courses %}

<td>{{course}} Grade</td>

{% endfor %}

</tr>
{% for student in students %}

<tr>

<td>{{student}}</td>
{% for course in courses %}

<td>

{{marks|get:student|get:course}} {# Chaining filters emulates the nested variable lookup #}

</td>

{% endfor %}

</tr>

{% endfor %}

</table>


In further defence of @nullie's fix: note that when the data does not exist, the filter simply returns None. This is advantageous because we can test for None in the template using an if-else block. When None is encountered, we make the TEMPLATE level decision of what content shall be displayed. We may then display that content (even change our minds later) without ever concerning ourselves with python code.

It is my position that the example of students and courses provided above is hardly a manifestation of an "out of scope" use-case for Django projects. @nullie has proposed a solution and it was rejected without any sound criticism. A blind eye is being turned on perfectionists with deadlines. Altogether, I trust that I have demonstrated a sufficient condition for reopening #3371 (regardless of how impolite @ubernostrum feels such a sufficient condition is). Please expect #3371 to continue being reopened until a sufficient condition is provided for closing it.

comment:11 follow-up: Changed 11 months ago by ubernostrum

  • Resolution set to wontfix
  • Status changed from new to closed

We have a simple and clear procedure if you disagree with a wontfix decision: rather than have a fight about it in the ticket, start a discussion on the django-developers mailing list, where it will likely also reach a broader audience. Which is exactly what I suggested when I last closed this ticket.

Please expect to have your arguments excluded from consideration until you respect what is a simple and useful procedure for raising disagreement about a decision on a ticket. Please also expect that if your approach is to petulantly re-open a ticket over and over again out of anger, steps will be taken to remove your ability to behave in such a fashion.

comment:12 Changed 11 months ago by aaugustin

Google.com/search?q=how+do+i+use+jinja2+with+django

comment:13 in reply to: ↑ 11 Changed 11 months ago by anonymous

Replying to ubernostrum:

We have a simple and clear procedure if you disagree with a wontfix decision: rather than have a fight about it in the ticket, start a discussion on the django-developers mailing list, where it will likely also reach a broader audience. Which is exactly what I suggested when I last closed this ticket.

Please expect to have your arguments excluded from consideration until you respect what is a simple and useful procedure for raising disagreement about a decision on a ticket. Please also expect that if your approach is to petulantly re-open a ticket over and over again out of anger, steps will be taken to remove your ability to behave in such a fashion.

Once again @ubernostrom, you are only providing scape-goating and argumentation ad hominem in your effort to throw bona fide use cases under the bus.

Replying to aaugustin:

Google.com/search?q=how+do+i+use+jinja2+with+django

It can be said with complete confidence that jinja2 is outside of the scope of #3371. @nullie has already suggested fix superior to configuring jinja2. Unless you are suggesting that jinja2 should replace the Django templating language as the way to resolve #3371, your pasting of urls to ambiguous lists of hyperlinks contributes absolutely nothing to this discussion.

comment:14 Changed 11 months ago by aaugustin

Since the Django template language doesn't match your requirements, I'm recommending that you use jinja2 instead. It's very easy.

comment:15 follow-up: Changed 11 months ago by aaugustin

I would also encourage you to read https://www.djangoproject.com/conduct/ and adjust your behavior accordingly.

comment:16 in reply to: ↑ 15 Changed 11 months ago by anonymous

Replying to aaugustin:

Since the Django template language doesn't match your requirements, I'm recommending that you use jinja2 instead. It's very easy.

I would also encourage you to read https://www.djangoproject.com/conduct/ and adjust your behavior accordingly.

These two comments direct zero focus to the subject of #3371 which is that of performing variable lookups in the template. Instead you attempt to obfuscate #3371 with irrelevant dogma.

If your recommendation to resolving failures of the Django templating language (as addressed by #3371) is to replace it with jinja2 as the defacto templating language, then #3371 should be reopened until that fix has been implemented. Simple, no?

comment:17 Changed 11 months ago by aaugustin

I suggested that you use jinja2 in your project, to match your requirements, which doesn't require any changes to Django.

comment:18 Changed 10 months ago by djdev7

There are 3 questions on SO that talk about this with a combined view count of 40,000+.
http://stackoverflow.com/q/8000022/781695
http://stackoverflow.com/q/2894365/781695
http://stackoverflow.com/q/2067006/781695

I agree with the reasoning about why the discussion should be moved to mailing list. This ticket ranks high up in google search for this issue and it'll be a big help for many newbies to simply find a workaround without opening security holes etc.. My polite request to core developers is to simply comment on usability/security of this solution. Most of us are pretty happy with Django templating language and wouldn't want to switch to Jinja2. However, often there are instances where a variable lookup is needed as witnessed by the activity on Stackoverflow.

http://stackoverflow.com/a/8000091/781695

@register.filter
def get_item(dictionary, key):
    return dictionary.get(key)


comment:19 Changed 10 months ago by anonymous

@djdev7:

The Core Developers already commented on your solution back in comment 13:
Google.com/search?q=how+do+i+use+jinja2+with+django

Combined with https://code.djangoproject.com/ticket/2594#comment:58, it is pretty clear that the core developers are following an abandon-ware model to the development of the Django Template system. It really is too bad that they don't just come out of the closet and deprecate the darn thing.

comment:20 follow-up: Changed 10 months ago by aaugustin

There are some practical issues with deprecating 10% of your framework before you've actually implemented a replacement :-)

comment:21 Changed 9 months ago by anonymous

I pass dict

AddonMap = {'menu': '1', 'opening_hour': '2', 'books': '3', 'Doctors': '4'}

and

addon_list = [{u'menu': [{u'price': 50, u'name': u'Momo', u'price_level': u'cheap'}, {u'price': 510, u'name': u'potato', u'price_level': u'expensive'}, {u'price': 100, u'name': u'Chowmein', u'price_level': u'expensive'}]}, {u'opening_hour': [{u'close': 17, u'open': 9, u'day': 0}, {u'close': 17, u'open': 9, u'day': 1}, {u'close': 16, u'open': 8, u'day': 2}]}]

Now I need put href of id based on key (menu) of dict. How can I achieve that. Please Help me.

{% if addon_list %}

    {% for addon_name in addon_list %}
        {% for key, values in addon_name.iteritems %}
            <table>
            <tr>
                <td>
                    <h2>{{ key }}</h2>{# title- menu #}
                </td>

                
                <td><h5><a href="{% url 'addon:update_addon' spot_id ''' <I need value of key variable here. for key == menu I need 1>''''''' %}">update</a></h5></td>
            </tr>


            <tr>
            </tr>

            {% for value in values %}
                {% for k,v in value.iteritems %}
                    <tr>
                        <td>{{ k }}</td>
                        <td>{{ v }}</td>
                        {#                        <td><h5><a href="{% url 'addon:update_addon_item' spot_id addon_id item_id %}" </h5></td>#}
                    </tr>
                {% endfor %}
            {% endfor %}
        {% endfor %}
    {% endfor %}
</table>
{% endif %}

comment:22 Changed 9 months ago by timo

Please see https://docs.djangoproject.com/en/stable/faq/help/. This ticket tracker is not a support channel.

comment:23 in reply to: ↑ 20 Changed 8 months ago by msteffeck

Replying to aaugustin:

There are some practical issues with deprecating 10% of your framework before you've actually implemented a replacement :-)

and

Google.com/search?q=how+do+i+use+jinja2+with+django


Does that mean Django core developers are endorsing Jinja2 over their own template system? Should I be migrating my system to something like django-jinja?
My impression from the conversation of this ticket is that Django developers have no interest in fixing simple but glaring issues with their system, and instead recommend usage of another, competing tool.

comment:24 Changed 8 months ago by collinanderson

That's a fine question, though the wrong place to ask it. If you want to discuss it, please use the django-users or django-development mailing list.

https://docs.djangoproject.com/en/dev/internals/mailing-lists/#django-users-mailing-list

Last edited 8 months ago by collinanderson (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top