【问题标题】:Django pagination of filtered results using AJAX使用 AJAX 对过滤结果进行 Django 分页
【发布时间】:2017-03-04 20:31:07
【问题描述】:

我几天来一直试图弄清楚这一点,但无法在网上找到任何有用的信息。 我想要做的是在使用下拉菜单过滤对象并通过 AJAX 将数据提供给 python 之后对模型中的对象进行分页。我知道问题出在哪里,但我不知道如何解决它。我有两个模板,第一个是:

entry_index.html

{% extends 'main/base.html' %}
<form action="" method="get" accept-charset="utf-8">
<select class="selectpicker" name="times" onchange="FilterCategories()" id="times">
     <option value="1">last 24 hours</option>
     <option value="30">past month</option>
     <option value="365">past year</option>
     <option value="10000">all time</option>
 </select>
</form>

<ul id="all-games" class="list-unstyled">
{% include page_template %}
</ul>

上述模板中包含的模板是entry_index_page.html

{% if objects %}
    {% for object in objects %}
     do something
    {% endfor %}

    <div class="pagination">
    <span class="step-links">
        {% if objects.has_previous %}
            <a href="?page={{ objects.previous_page_number }}">previous</a>
        {% endif %}

        <span class="current">
            Page {{ objects.number }} of {{ objects.paginator.num_pages }}.
        </span>

        {% if objects.has_next %}
            <a href="?page={{ objects.next_page_number }}">next</a>
        {% endif %}
    </span>
</div>

urls.py

url(r'^$', views.entry_index, name='index')

views.py

def entry_index(
    request,
    template='entry_index.html',
    page_template='entry_index_page.html'):

    date_from = timezone.now() - timezone.timedelta(days=1)
    obj_list=Object.objects.filter(submitted__gte = date_from).order_by('-votes')
    message=[]
    context = {
    'objects': obj_list,
    'page_template': page_template}

    if request.is_ajax():
        template = page_template
        message = []

        if request.method == "GET":
            time_range = request.GET.get('time_range')
            if time_range is not None and time_range != u"":
                time_range = request.GET['time_range']
                date_from = timezone.now() - timezone.timedelta(days=int(time_range))
                obj_list= Object.objects.filter.filter(submitted__gte=date_from)

    paginator = Paginator(obj_list, 2)

    page = request.GET.get('page')
    try:
        objects= paginator.page(page)
    except PageNotAnInteger:
        objects= paginator.page(1)
    except EmptyPage:
        objects= paginator.page(paginator.num_pages)

    context.update({"message":message,"objects":objects})

    return render_to_response(
    template, context, context_instance=RequestContext(request))

ajax.js

function FilterCategories() {
    var timePosted = document.getElementById('times');
    $.ajax({
        type: "GET",
        url: "",
        data: {
            'time_range': timePosted.value,
            'csrfmiddlewaretoken': $("input[csrfmiddlewaretoken]").val()
        },
        success: filterResults,
        dataType: 'html'
    });
}

现在解释一下我的想法,希望有人可以帮助我找到解决方案。

加载主页 (entry_index.html) 时,模型会根据下拉菜单中的第一个选项进行过滤(即 value="1",用于过滤最后一天提交的数据条目)。 obj_list 变量被填充并传递给分页器,一切都按预期工作。我得到了一定数量的页面,并且可以浏览页面。现在假设我们再次在主页上,我从下拉菜单中选择“所有时间”。这将触发 onchange 回调并调用 FilterCategories() 函数。注意 AJAX 中的 url 是 "" (一个空字符串,所以指向我的索引页)。根据 urls.py,它会调用我的 entry_index() 视图。因为request是ajax,所以使用的模板会发生变化(page_template变成了新的模板,page_template = entry_index_page.html)。因为使用下拉菜单指定的新时间范围并使用 ajax 传递,所以我得到一个新的 obj_list,然后对其进行分页并生成“对象”,然后将其作为上下文传递给模板。到目前为止,一切都按预期工作。我得到了适量的页面等。但是,当我尝试使用新选择的过滤器转到下一页时,问题就开始了。当我单击下一页按钮时,正在发出的请求不是 ajax 请求,因此 request.is_ajax() 条件中的所有内容都不会执行。所以换句话说,点击下一页再次调用我的 entry_index 视图,这次使用的模板是 entry_index.html,我的过滤器被重置为默认值,即“过去 24 小时”过滤器。因此,当我单击下一页时,我最终得到的实际上是默认主页,而不是使用我新选择的下拉过滤器获取下一页对象。

我的问题是,有没有一种简单的方法可以解决这个问题,以便我可以滚动浏览过滤模型的页面?还是我应该完全放弃这种方法,并且有更简单的方法可以做到这一点?我为一个长帖子道歉,我希望那里的人能够帮助我。感谢您花时间阅读本文。

【问题讨论】:

  • 我并不完全遵循您的流程,但您可能会发现使用单个模板更容易,只需使用 AJAX 替换页面本身的数据或 HTML 块,而不是创建新页面负载。它会更快,移动部件更少。

标签: python django


【解决方案1】:

这是我使用 Django 和 Ajax 在页面上显示动态内容的方法示例:

我正在制作一个小型浏览器科幻游戏,只是为了练习这种特定的技术。一切都发生在一个视图中:

class GameViewport(TemplateView):
    template_name = "game_viewport.html"

    @cached_property
    def slug(self):
        return self.kwargs['slug']

    @cached_property
    def game(self):
        return Game.objects.get(url=self.kwargs['slug'])

    @cached_property
    def player(self):
        return Player.objects.get(game=self.game)

    @cached_property
    def current_planet(self):
        return self.player.current_planet

    @cached_property
    def left_column(self):
        player = self.player

        if player.current_location:
            node = player.current_node
            if len(Location.objects.filter(node=node)) == 0:
                spawn_locations(node)
                locations = Location.objects.filter(node=node)
            else:
                locations = Location.objects.filter(planet=node)
            html = "Other Sites in ".format(str(node))
            for location in locations:
                html += '<li><a href="" class="locationChoice"></a>{} ({})</li>'.format(location.name, location.type.name)
            return html

        elif player.current_node:
            planet = player.current_planet
            if len(Node.objects.filter(planet=planet)) == 0:
                spawn_nodes(planet, get_name_choices())
                nodes = Node.objects.filter(planet=planet)
            else:
                nodes = Node.objects.filter(planet=planet)
            html = '<h4><b>Other Starports on {}:</b></h4>'.format(planet.name)
            for node in nodes:
                html += '<li><a href="" class="nodeChoice"> {} </a> ({})</li>'.format(node.name, node.type.name)
            return html

        elif player.current_planet:
            system = player.current_system
            html = '<h4><b>Known Planets in {}:</b></h4>'.format(system.name)
            for known_planet in player.known_planets.filter(solar_system=system):
                html += '<li><a href="" class="planetChoice"> {} </a> ({})</li>'.format(
                    known_planet.name,
                    known_planet.classification.name
                )
            return html
        else:
            html = '<h4><bShip Status</b></h4>'
            html += '<p><b>Fuel:</b> 100%</p>'
            return html

如您所见,左列会根据播放器的当前设置生成不同的 html 数据。这将像这样插入到模板中:

<div class="col-md-3">
    <div class="leftColumn">
        {% autoescape off %}
          {{ view.left_column }}
        {% endautoescape %}
    </div>
</div>
<a href="{% url "visit_planet" view.slug %}" id="visitPlanet"></a>

如果用户点击一个新位置,我会通过 AJAX 发送她的决定:

$(".planetChoice").click(function(){
    event.preventDefault();
    var submission_data = {planet: $(this).text()};
    console.log(submission_data);
    $.ajax({
        url: $('#visitPlanet').attr('href'),
        type: 'GET',
        dataType: "json",
        data: submission_data,
        success: function(html_data) {
                window.location.reload();
        },
        failure: function(data) {
            alert('Something went wrong. Please refresh the page.');
        }
    });
});

所有这一切都会更新玩家的状态并根据她的新设置重新加载页面:

def visit_planet(request, slug):
    player = Game.objects.get(url=slug).player_1
    planet = Planet.objects.get(name=request.GET.get('planet', "").strip())

    if planet:
        player.current_location = None
        player.current_node = None
        player.current_planet = planet
        player.save()

    response = {'status': 1, 'message': "Ok"}

    return JsonResponse(response)

因此显示由left_column 属性确定的新数据。

我通过更改数据库中的状态来解决此问题,但也可以使用会话变量轻松完成。我发现它是一种循环动态内容的相对干净和干燥的方式。它还具有让 Django 有机会在两次点击之间生成或修改数据的优势。

不确定这是否适用于您的情况,但希望它能激发一个想法!

编辑:您甚至不一定需要输出 HTML。这是我在不同应用程序中使用的一种方法:

<!--Product Tile #1-->
{% if view.tile_data.0 %}
<div class="col-md-4">
    <div class="card hoverable">
        <!--Card content-->
        <div class="card-block" id="tile_{{ view.tile_data.0.invoice }}_id">
            <!--Title-->
            <h4 class="card-title">Shipment {{ view.tile_data.0.invoice }}</h4>
            <!--Text-->
            <p class="card-text">{{ view.tile_data.0.supplier.name }}
            <br>
            <b>{{ view.tile_data.0.lbs|floatformat }} Lbs @ {{ view.tile_data.0.price }} USD</b>
            <br>
            {{ view.tile_data.0.variety.commodity }} {{ view.tile_data.0.variety }} {{ view.tile_data.0.inshell|shell_display }}</p>
        </div>
        <!--/.Card content-->
    </div>
</div>
{% endif %}
<!--./Product Tile #1-->

此数据直接通过模型管理器的视图提供:

def tile_data(self, status, first, last):
    return self.model.objects.filter(status=status)[first:last]

【讨论】:

    猜你喜欢
    • 2019-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 1970-01-01
    • 2018-01-31
    • 2016-11-30
    相关资源
    最近更新 更多