Opened 8 years ago

Last modified 2 years ago

#5865 new New feature

cycle template tag should accept a single argument

Reported by: gwilson Owned by: munhitsu
Component: Template system Version: master
Severity: Normal Keywords:
Cc: mateusz@… Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: yes Patch needs improvement: yes
Easy pickings: no UI/UX: no

Description

When passed a single argument, the cycle tag should treat it as an iterable to cycle through. For example, with the context:

{'colors': ['red', 'blue', 'green']}

you should be able to have a template like:

{% for row in mydata %}
<tr class="{% cycle colors %}">...</tr>
{% endfor %}

that would output:

<tr class="red">...</tr>
<tr class="blue">...</tr>
<tr class="green">...</tr>
<tr class="red">...</tr>
...

Attachments (2)

cycle.diff (6.7 KB) - added by munhitsu 8 years ago.
v2
cycle-doc.diff (1.2 KB) - added by munhitsu 8 years ago.

Download all attachments as: .zip

Change History (18)

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

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

This sounds like a sensible improvement

comment:2 Changed 8 years ago by munhitsu

  • Owner changed from nobody to munhitsu

comment:3 follow-up: Changed 8 years ago by mtredinnick

I'm a little concerned that this is ambiguous when you're reading it: you can't easily tell if it's referring to a previously named "cycle" tag or creating a new one using a variable as an iterator.

Is there a better way to write this that won't cause confusion when reading templates?

comment:4 Changed 8 years ago by munhitsu

So far I've managed to perform the request but maintaining compatibility with older approach makes my code not so... clean.

What do you think about a new keyword i.e.: cycletable?

Maybe its a time to refer to named cycle tag by variable reference? To be honest referring by name is not to clear for me.

comment:5 in reply to: ↑ 3 Changed 8 years ago by gwilson

Replying to mtredinnick:

I'm a little concerned that this is ambiguous when you're reading it: you can't easily tell if it's referring to a previously named "cycle" tag or creating a new one using a variable as an iterator.

Is it really that different from using the "as varname" syntax of other tags or the {% with %} tag though? The way I see it, when you encounter any variable name in the templates, you have to check where it came from. Was it passed in the context? Was it defined as the target of "as varname" syntax of another variable? Was it defined with a with tag? Is it a loop variable? etc. This would be no different.

comment:6 Changed 8 years ago by munhitsu

  • Cc mateusz@… added

Guys take a look at following unit test suite. Does it reflect your expectations?

{
'cycle20': ("{% cycle colors %}", {'colors': 'red'}, template.TemplateSyntaxError),
'cycle21': ("{% cycle colors %}{% cycle colors %}", {'colors': ['red', 'blue', 'green']}, 'redblue'),
'cycle22': ("{% cycle colors %}{% cycle colors %}{% cycle colors %}", {'colors': ['red', 'blue', 'green']}, 'redbluegreen'),
'cycle23': ("{% cycle colors %}{% cycle colors %}{% cycle colors %}{% cycle colors %}", {'colors': ['red', 'blue', 'green']}, 'redbluegreenred'),
'cycle24': ("{% cycle colors as color %}{% cycle color %}", {'colors': ['red', 'blue', 'green']}, 'redblue'),
'cycle25': ("{% cycle colors as color %}{% cycle color %}{% cycle color %}", {'colors': ['red', 'blue', 'green']}, 'redbluegreen'),
'cycle26': ("{% cycle colors as color %}{% cycle color %}{% cycle color %}{% cycle color %}", {'colors': ['red', 'blue', 'green']}, 'redbluegreenred'),
'cycle27': ("{% for i in test %}{% cycle colors %}{{ i }},{% endfor %}", {'test': range(5),'colors': ['a', 'b']}, 'a0,b1,a2,b3,a4,'),
}

comment:7 Changed 8 years ago by munhitsu

  • Has patch set
  • Resolution set to fixed
  • Status changed from new to closed

Seems to be working. Old regression is passing. New has been added.
I've added a small enhancement - a cycle pushed to context. Thanks to it cycle internals can be used inside template.

        context['cycle'] = {
            # cycle internals
            'collection': self.collection,
            'name':self.variable_name,
            'value':value,
        }

One note. Last part of syntax parsing has to be done in render method. We need context to say if {% cycle colors %} refers to table or is a mistake.

comment:8 Changed 8 years ago by munhitsu

  • Resolution fixed deleted
  • Status changed from closed to reopened

comment:9 Changed 8 years ago by gwilson

  • Needs documentation set
  • Patch needs improvement set

Your cycle20 test case should not raise a TemplateSyntaxError, but rather should output "r" since a string is iterable. A TemplateSyntaxError should only be raised if the syntax for the tag in not correct, i.e. {% cycle colors as %}. If the object passed is not iterable, the template tag should fail silently and return an empty string. Also, your patch should allow any iterable instead of just lists. I also don't see a reason that we should be exposing the internal state of the tag. Could you explain your reasoning for wanting to do that?

Changed 8 years ago by munhitsu

v2

comment:10 Changed 8 years ago by munhitsu

  • Patch needs improvement unset

Replying to gwilson:

Thanks for the comments.

  1. Functionality for test cycle20 updated. Do you have an idea how to prepare context with non iterable variable? I have no clue how to make unit test for it.
    'cycle20': ("{% cycle colors %}", {'colors': 'r'}, 'r')
    
  2. Regarding silent fail... Actual implementation will throw TemplateSyntaxError for test cycle01 as original one. But i.e if 'a' would be defined in context than it will fall back to Variable('a').resolve(context).
    'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError)
    
  3. I've just added NameError exception catch as a backup to make sure we will fail silently
  4. Cycle patch now iterates over everything that is iterable - see test cycle22. It's hashed as Python does not guarantee iteration sequence on hash.
    'cycle22': ("{% cycle colors %}{% cycle colors %}", {'colors': {'r': 1, 'g': 2}}, 'rg')
    
  5. Regarding cycle internal state in tag. I must say that I was inspired by ForNode and cycle is a somehow close to it. Take a look at tests cycle28..29. That the only reasonable case that I see now to use cycle internals. It's your choice.
    'cycle28': ("{% for i in test %}{% cycle colors %}{{ cycle.value }}{{ i }},{% endfor %}", {'test': range(5),'colors': ['r', 'g', 'b']}, 'rr0,gg1,bb2,rr3,gg4,'),
    'cycle29': ("{% for i in test %}{% cycle colors %}{% cycle colors %}{{ i }},{% endfor %}", {'test': range(5),'colors': ['r', 'g', 'b']}, 'rg0,br1,gb2,rg3,br4,'),
    

Changed 8 years ago by munhitsu

comment:11 Changed 8 years ago by munhitsu

  • Cc mateusz@… added; mateusz@… removed
  • Needs documentation unset

comment:12 Changed 4 years ago by gabrielhurley

  • Severity set to Normal
  • Type set to New feature

comment:13 Changed 4 years ago by julien

  • Easy pickings unset
  • Triage Stage changed from Accepted to Design decision needed

A design decision hasn't been reached yet.

comment:14 Changed 4 years ago by Alex

  • Triage Stage changed from Design decision needed to Accepted
  • UI/UX unset

Discussion with Carl: this new syntax would be ambiguous with the current syntax, however this is a good feature, and the current API is kind of silly. This is accepted, provisional on creating a new, more sane and consistent syntax, to be placed in the future module.

comment:15 Changed 4 years ago by ptone

  • Needs documentation set
  • Needs tests set
  • Patch needs improvement set

comment:16 Changed 2 years ago by aaugustin

  • Status changed from reopened to new
Note: See TracTickets for help on using tickets.
Back to Top