首页 > 解决方案 > Cycle through 2D list with Jinja

问题描述

For simplicity, I will make a hypothetical which is equivalent to my current situation. I am using Flask as a backend to render Jinja templates for the frontend. Suppose I have a list of classes, each class being a list of students (Python classes/nodes with attributes and all). I want to render a single "class of students" at a time, and have buttons to cycle to the next group of students. Here is an example of how that looks:

app.py

@app.route('/', methods=['GET', 'POST'])
def get_students():
    groups = None
    # some calculations and algorithms
    groups = [['foo', 'bar'], ['baz', 'boo']]
    return render_template('index.html', groups=groups, index=0)

index.html

{% if groups %}
{% set display_group = groups[index] %}
    <ul id="students" class="">
        {% for student in display_group %}
            <li class="student">{{ student.name }}</li>
        {% endfor %}
    </ul>
{% endif %}
<button onclick="next_group()">Next Group</button>
<button onclick="prev_group()">Previous Group</button>

I would like these buttons to re-render this list as if the index incremented/decremented respectively. I do not want this to be done through URL parameters (e.g. page_url/number). How can I achieve this affect, and if possible, without a page refresh?

标签: pythonajaxflaskjinja2

解决方案


这类似于分页。首先,您可以创建一个简单的分页对象以更好地帮助正确查找新索引的页面,同时还可以控制下一页和上一页的索引:

import typing

class StudentList(typing.NamedTuple):
  name:str

class Pagination:
  def __init__(self, _num = 1):
     self.num = _num
     self.data = [['foo', 'bar'], ['baz', 'boo'], ['first', 'last']]
  @property
  def has_next(self):
     return self.num < len(self.data)
  @property
  def has_previous(self):
     return self.num > 0
  @property
  def next(self):
     return self.num + 1
  @property
  def previous(self):
     return self.num - 1
  def __iter__(self):
     for i in self.data[self.num-1]:
        yield StudentList(i)

接下来,为了创建动态查找,html需要两部分:主页,用 javascript 控制按钮点击并与后端通信,以及html作为查询的一部分返回给后端ajax。首先,创建查询html

students_and_classes.html

<div class='student_class'>
 {%for student in lecture%} 
   <span class='student'>Name: {{student.name}}</span>
 {%endfor%}
 {%if lecture.has_previous%}
   <button id='previous_{{lecture.previous}}'>Previous</button>
 {%endif%}
 {%if lecture.has_next%}
   <button id='next_{{lecture.next}}'>Next</button>
 {%endif%}
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
 <script>
 $(document).ready(function() {
  $('button').click(function(event) {
   var result = event.target.id;
   $.ajax({
    url: "/update_page",
    type: "get",
    data: {results: result},
    success: function(response) {
    $("#pagination_results").html(response.html);
    },
    error: function(xhr) {
     $("#pagination_results").html('<p>Opps, something when wrong</p>');
    }
   });
  });
 });
</script>
</div>

其次,显示完整学生分页的页面,以及jqueryajax

main.html

<html>
  <body>
    <div id='pagination_results'>
      <div class='student_class'>
      {%for student in lecture%} 
        <span class='student'>Name: {{student.name}}</span>
      {%endfor%}
      {%if lecture.has_previous%}
        <button id='previous_{{lecture.previous}}'>Previous</button>
      {%endif%}
      {%if lecture.has_next%}
        <button id='next_{{lecture.next}}'>Next</button>
     {%endif%}
    </div>
    </div>
  </body>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
  <script>
  $(document).ready(function() {
   $('button').click(function(event) {
    var result = event.target.id;
    $.ajax({
      url: "/update_page",
      type: "get",
      data: {results: result},
      success: function(response) {
      $("#pagination_results").html(response.html);
     },
     error: function(xhr) {
      $("#pagination_results").html('<p>Opps, something when wrong</p>');
    }
   });
 });
});
</script>
</html>

最后,在所需的路线(在本例中为“/”)中,main.html可以创建要服务的路线:

@app.route('/', methods = ['GET'])
def home():
  return flask.render_template('main.html', lecture=Pagination())

ajax GET然后,需要创建从方法接收数据的路由:

import re
@app.route('/update_page')
def update_page():
  _r = flask.request.args.get('results')
  _l = Pagination(int(re.findall('\d+$', _r)[0]))
  return flask.jsonify({'html':flask.render_template('students_and_classes.html', lecture=_l)})

笔记:

  1. self.datainPagination可以替换为数据库查询等,如果您的项目是这种情况
  2. StudentList如果所有列表值都是基本数据类型,则用于更清晰地呈现模板。在您的示例中,没有必要,因为您提到您的列表已经存储了自定义类对象,并且yield i可以替换为yield StudentList(i).

推荐阅读