如何在Django中向视图传递多个参数?

4 投票
2 回答
590 浏览
提问于 2025-04-15 22:03

我刚接触Django,正在尝试建立一个应用程序,用来展示我的数据,主要是以表格和图表的形式。到目前为止,我的学习过程还算顺利,但现在遇到了一些问题。

我的页面视图从数据库中获取大量数据,并把这些数据放到上下文中。然后,模板会生成不同的HTML表格。到这里一切都很好。
现在我想在模板中添加不同的图表。我通过定义<img src=".../>标签来实现这个目标。我的图表是在chartview中生成的,并通过以下方式返回:

response=HttpResponse(content_type='image/png')
canvas.print_png(response)
return response

现在我有几个问题:

  1. 数据从数据库中获取了两次。一次是在页面视图中用来渲染表格,另一次是在图表视图中用来生成图表。有没有什么好的方法可以把已经在页面上下文中的数据传递给图表视图呢?
  2. 我需要很多图表,每个图表都有不同的数据集。我可以为每个图表创建一个图表视图,但可能有更好的方法。如何将不同的数据集名称传递给图表视图呢?有些图表有20个数据集,所以我觉得通过URL传递这些数据集参数(比如:<img src="chart/dataset1/dataset2/.../dataset20/chart.png />)并不是个好主意。
    有什么建议吗?

2 个回答

1

在这种情况下,我会选择使用模板标签。我之前遇到过类似的情况,就是在同一页面上以不同的格式多次显示日历信息。我处理这个问题的方法是把查询到的数据放到请求上下文中,然后简单地把这个查询集作为模板标签的参数使用。这样一来,你就能得到像这样的模板语法:

视图

def my_view(request, *args, **kwargs):
    yearly_sales_qs = SaleRecord.objects.filter(param=value)
    monthly_sales_qs = SalesRecord.objects.filter(param=foo)

    return render_to_response( ..., locals(), ... )

模板

{% load data_tags %}

<div class="year">
    {% render_data_table for yearly_sales_qs %}
    {% render_bar_chart for yearly_sales_qs %}
</div>

<div class="month">
    {% render_data_table for monthly_sales_qs %}
    {% render_bar_chart for monthly_sales_qs %}
</div>

那么,怎么做出这样的东西呢?首先可以看看Django的文档,了解一下自定义模板标签和过滤器。这比Django的其他部分稍微难一点,但一旦掌握了,就会觉得很简单。

  • 首先在你应用的文件夹里创建一个名为“templatetags”的文件夹。
  • 在这个新文件夹里创建一个空文件“init.py”。
  • 把这个templatetags文件夹的位置添加到TEMPLATE_DIRS设置中,通常是在settings.py文件里(如果还没有的话)。

因为我们会制作几个这样的标签,所以可以先做一个基础模板标签,包含我们基本的功能...

data_tags.py(存放在templatetags文件夹里)

class DataForTag(tempalte.Node):
    @classmethod
    def handle_token(cls, parser, token, template):
        tokens = token.contents.split()
        if tokens[1] != 'for':
                raise template.TemplateSyntaxError("First argument in %r must be 'for'" % tokens[0])

        if len(tokens) == 3:
            return cls(queryset=parser.compile_filter(tokens[2]), template=template)
        else:
            raise template.TemplateSyntaxError("%r tag requires 2 arguments" % tokens[0])

    def __init__(self, queryset=None, template=None):
        self.queryset = queryset
        self.template = template

    def render(self, context):
        return render_to_string(self.template, {'queryset':self.queryset})

然后我们可以制作处理我们需要的各种单独标签...

@register.tag
def render_bar_chart(parser, token):
    return DataForTag.handle_token(parser, token, 'data/charts/barchart.html')

@register.tag
def render_pie_chart(parser, token):
    return DataForTag.handle_token(parser, token, 'data/charts/piechart.html')    

@register.tag
def render_data_table(parser, token):
    return DataForTag.handle_token(parser, token, 'data/table.html')   
6

你不能把页面视图的数据传递到图表视图,因为它们是两个独立的HTTP请求。你有几个选择:

  1. 把所有数据放在图表的URL里。这听起来可能有点疯狂,但这正是Google Charts的做法:http://code.google.com/apis/chart/docs/making_charts.html

  2. 把数据存储在会话中。页面视图会把数据放进会话里,然后图表视图会用这些数据来生成图表。

  3. 把数据库查询结果缓存到memcache中。因为页面和图表都会引用同一个查询,所以你很可能会命中缓存。这是个不错的解决方案,因为即使页面没有先渲染,图表也能正常工作。

  4. 再查询一次数据库。你的数据库管理系统可能有很好的缓存,性能可能并不像你想的那么糟。

至于你的第二个问题,URL里有20个单词似乎并不算太大问题。当然,你也可以找一些数据集的规律,这样就不需要每次都指定所有数据,但如果需要的话,就直接用长一些的URL吧。

撰写回答