web-dev-qa-db-ja.com

Django ListViewカスタマイズクエリセット

うまくいけば、これは私を助けるための簡単なものであるべきです。

3つの項目を含むドロップダウンメニューのあるページがあります。

<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>

ユーザーがアイテムを選択して送信を押すと、一般的な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

ただし、ページネーションコントロールを使用してページをめくろうとすると、フォームデータがリセットされているため、クエリセットが最初の(デフォルトの)項目である猫にリセットされます。

この問題を回避する方法はありますか?

ありがとう!

PS:ああ、そのメモで、クエリセットを最初からなしに設定することは可能ですか?とても感謝しております!

更新:Catsクエリセットでページネーションを使用すると、正常に動作するため、バグは他の2つのセットでのみ表示されます。

14
Sirrah

この問題を解決するために、フォームからのgetリクエストとURL文字列のページ番号の両方に対応するように、ページネーションHTMLを変更しました。

<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>

ここでの{{input}}は、フォームを介して送信されたオプションを含む文字列です。 「猫」または「ワーム」。

これをテンプレートに渡すことができるように、クラスベースビューの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

これで、url文字列は次のようになります。

/browse/?browse=Cats&page=3

つまり、ページネーションはフォームのgetメソッドと一緒に機能するようになりました。

14
Sirrah

シラーの答え に基づくクエリの使用を支援するために、テンプレートタグをまとめました。例:

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

クエリパラメータが辞書の場合{'foo': 'bar'}コンテキストで渡されると、次のようにレンダリングされます。

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

構文:

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

変数は、リスト、辞書、文字列、またはなし(なしはスキップされます)です。

コード:

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
3
JBernardo