Django使用AJAX

时间:2017-03-04 07:51:33

标签: python django

我一直在努力解决这个问题,并且无法在线找到任何有用的信息。 我想要做的是使用下拉菜单过滤它们并通过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变量被填充并传递给paginator,一切都按预期工作。我得到一定数量的页面,可以浏览页面。现在假设我们再次出现在主页上,然后从下拉菜单中选择“所有时间”。这将触发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小时”过滤器。因此,当我单击下一页时,我最终得到的实际上是默认主页,而不是使用我新选择的下拉过滤器获取下一页对象。

我的问题是,是否有一种简单的方法可以解决这个问题,以便我可以滚动浏览过滤模型的页面?或者我应该完全放弃这种方法,还有一种更简单的方法吗?我为一篇很长的帖子道歉,我希望那里的人能够帮助我。感谢您抽出宝贵时间阅读本文。

1 个答案:

答案 0 :(得分: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]