Python Django ListView 自定义查询集

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22902457/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 01:56:31  来源:igfitidea点击:

Django ListView customising queryset

pythondjangolistviewviews

提问by Sirrah

Hopefully this should be a simple one to help me with.

希望这应该是一个简单的帮助我的。

I have a page with a dropdown menu containing three items:

我有一个包含三个项目的下拉菜单页面:

<form method="GET">

    <select name="browse">

        <option>Cats</option>

        <option>Dogs</option>

        <option>Worms</option>

    </select>

 <input type="submit" value="Submit" />

</form>

<!-- Output table -->

  <table id="myTable">

      <thead>
          <tr>
            <th>Name</th>
            <th>Colour</th>
          </tr>
      </thead>

      <tbody>
      {% for object in object_list %}
          <tr>
            <td>{{ object.name }}</td>
            <td>{{ object.colour }}</td>
          </tr>
      {% endfor %}
      </tbody>

  </table>

<!-- Pagination controls -->

<div class="pagination">
    <span class="page-links">
        {% if page_obj.has_previous %}
            <a href="?page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}
        <span class="page-current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>
        {% if page_obj.has_next %}
            <a href="?page={{ page_obj.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

When the user selects an item and hits submit, they are given the results in a table as generated by the generic ListView:

当用户选择一个项目并点击提交时,他们会在由通用 ListView 生成的表格中获得结果:

class Browse(generic.ListView):
    template_name = 'app/browse.html'
    paginate_by = 25

    def get_queryset(self):
        queryset = Cats.objects.all()
        if self.request.GET.get("browse"):
            selection = self.request.GET.get("browse")
            if selection == "Cats":
                queryset = Cats.objects.all()
            elif selection == "Dogs":
                queryset = Dogs.objects.all()
            elif selection == "Worms":
                queryset = Worms.objects.all()
            else:
                queryset = Cats.objects.all()
        return queryset

However, when I attempt to turn a page using the pagination controls, the queryset resets to the first (default) item Cats, because (I think) the form data is reset.

但是,当我尝试使用分页控件翻页时,查询集会重置为第一个(默认)项目 Cats,因为(我认为)表单数据已重置。

Any idea how to circumvent this problem?

知道如何规避这个问题吗?

Thanks!

谢谢!

PS: Oh, on that note, is it possible to set the queryset to none to begin with? Much obliged!

PS:哦,在那一点上,是否可以将查询集设置为无一开始?多谢!

UPDATE: When I use pagination on the Cats queryset it works fine so the bug is only displayed on the other two sets.

更新:当我在 Cats 查询集上使用分页时,它工作正常,因此该错误仅显示在其他两组上。

采纳答案by Sirrah

To solve this problem I just modified the pagination HTML, to accommodate both the get request from the form and the page number in the url string, like so:

为了解决这个问题,我只是修改了分页 HTML,以适应来自表单的 get 请求和 url 字符串中的页码,如下所示:

<div class="pagination">
    <span class="page-links">
        {% if page_obj.has_previous %}
            <a href="/browse/?browse={{ input }}&page={{ page_obj.previous_page_number }}">previous</a>
        {% endif %}
        <span class="page-current">
            Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}.
        </span>
        {% if page_obj.has_next %}
            <a href="/browse/?browse={{ input }}&page={{ page_obj.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

The {{ input }} here is a string containing the option submitted via the form, e.g. 'Cats' or 'Worms'.

这里的{{ input }} 是一个字符串,包含通过表单提交的选项,例如'Cats' 或'Worms'。

To be able to pass this into the template, I modified the get_context_data method of the class based view as such:

为了能够将其传递到模板中,我修改了基于类的视图的 get_context_data 方法,如下所示:

class Browse(generic.ListView):
    template_name = 'app/browse.html'
    paginate_by = 25

    # Modifying the get_context_data method

    def get_context_data(self, **kwargs):
        context = super(Browse, self).get_context_data(**kwargs)
        q = self.request.GET.get("browse")
        context['input'] = q
        return context

    def get_queryset(self):
        queryset = Cats.objects.all()
        if self.request.GET.get("browse"):
            selection = self.request.GET.get("browse")
            if selection == "Cats":
                queryset = Cats.objects.all()
            elif selection == "Dogs":
                queryset = Dogs.objects.all()
            elif selection == "Worms":
                queryset = Worms.objects.all()
            else:
                queryset = Cats.objects.all()
        return queryset

That was it, the url string now reads something like:

就是这样,url 字符串现在是这样的:

/browse/?browse=Cats&page=3

So there it is, pagination now works alongside the get method of the form.

就是这样,分页现在可以与表单的 get 方法一起使用。

回答by JBernardo

I put together a template tag to help using queries based on Sirrah's answer. Example:

我整理了一个模板标签来帮助使用基于Sirrah's answer 的查询。例子:

<a href="{% url view_url %}?{% query query_params page=num %}">{{ num }}</a>

If query params is a dictionary {'foo': 'bar'}passed in the context, it will be rendered to something like this:

如果 query params 是{'foo': 'bar'}在上下文中传递的字典,它将被呈现为如下所示:

<a href="myview/?foo=bar&page=2">2</a>

Syntax:

句法:

{% query var_name param=value 'name_only_param' other_param=value|default:'x' another_var %}

Variables can be lists, dicts, strings or None (None is skipped).

变量可以是列表、字典、字符串或无(无被跳过)。

Code:

代码:

from django import template
from django.utils.encoding import force_text
from django.template.base import Node, TemplateSyntaxError, kwarg_re, FilterExpression

register = template.Library()

@register.tag
def query(parser, token):
    bits = token.split_contents()
    args = []
    asvar = None
    bits = bits[1:]
    if len(bits) >= 2 and bits[-2] == 'as':
        asvar = bits[-1]
        bits = bits[:-2]

    if len(bits):
        for bit in bits:
            match = kwarg_re.match(bit)
            if not match:
                raise TemplateSyntaxError("Malformed arguments to url tag")
            name, value = match.groups()
            if name:
                args.append({name: parser.compile_filter(value)})
            else:
                args.append(parser.compile_filter(value))

    return QueryNode(args, asvar)


class QueryNode(Node):
    def __init__(self, args, asvar):
        self.args = args
        self.asvar = asvar

    def render(self, context):
        def join(thing, lst):
            if isinstance(thing, dict):
                for k, v in thing.items():
                    if isinstance(v, FilterExpression):
                        v = force_text(v.resolve(context))
                    if v is None:
                        continue
                    lst.append('{}={}'.format(k, v))
            elif isinstance(thing, list):
                for it in thing:
                    if isinstance(it, FilterExpression):
                        it = it.resolve(context)
                    join(it, lst)
            elif isinstance(thing, str):
                lst.append(thing + '=')
            elif thing is None:
                pass
            else:
                raise TypeError('Cannot join: %r' % thing)

        query_lst = []
        join(self.args, query_lst)
        query = '&'.join(query_lst)

        if self.asvar:
            context[self.asvar] = query
            return ''
        else:
            return query