Skip to main content
 首页 » 编程设计

python之使用 AJAX 过滤结果的 Django 分页

2025年02月15日32emanlee

几天来我一直在努力解决这个问题,但无法在线找到任何有用的信息。 我想要做的是在使用下拉菜单过滤对象并通过 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() View 。因为request是ajax,所以使用的模板会变(page_template变成新的模板,page_template = entry_index_page.html)。因为使用下拉菜单指定的新时间范围并使用 ajax 传递,所以我得到一个新的 obj_list,然后对其进行分页并生成“对象”,然后将其作为上下文传递到模板上。到目前为止,一切都按预期进行。我得到了适量的页面等。但是,当我尝试使用新选择的过滤器转到下一页时,问题就开始了。当我单击下一页按钮时,发出的请求不是 ajax 请求,因此 request.is_ajax() 条件中的所有内容都不会执行。所以换句话说,点击下一页会再次调用我的 entry_index View ,这次使用的模板是 entry_index.html,我的过滤器被重置为默认值,即“过去 24 小时”过滤器。因此,当我单击下一页时,我最终得到的实际上是默认主页,而不是使用新选择的下拉过滤器获取下一页对象。

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

请您参考如下方法:

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

我正在制作一个小型浏览器科幻游戏来练习这项特定技术。一切都发生在一个 View 中:

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 属性确定的新数据。

我通过更改数据库中的状态来解决这个问题,但它可以通过 session 变量轻松完成。我发现它是一种相对干净和干燥的循环动态内容的方式。它还具有让 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--> 

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

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