第五章 分享内容到你的网站

5 分享内容到你的网站

上一章中,你在网站中构建了用户注册和认证。你学会了如何为用户创建自定义的个人资料模型,并添加了主流社交网站的社交认证。

在这一章中,你会学习如何创建JavaScript书签工具,来从其它网站分享内容到你的网站,你还会使用jQuery和Django实现AJAX特性。

本章会覆盖以下知识点:

  • 创建多对多的关系
  • 定制表单行为
  • 在Django中使用jQuery
  • 构建jQuery书签工具
  • 使用sorl-thumbnail生成图片缩略图
  • 实现AJAX视图,并与jQuery集成
  • 为视图创建自定义装饰器
  • 构建AJAX分页

5.1 创建图片标记网站

我们将允许用户在其他网站上标记和分享他们发现的图片,并将其分享到我们的网站。为了实现这个功能,我们需要完成以下任务:

  1. 定义一个存储图片和图片信息的模型。
  2. 创建处理图片上传的表单和视图。
  3. 为用户构建一个系统,让用户可以上传在其它网站找到的图片。

首先在bookmarks项目目录中,使用以下命令创建一个新的应用:

django-admin startapp images

settings.py文件的INSTALLED_APPS设置中添加images

INSTALLED_APPS = (
    # ...
    'images',
)

现在Django知道新应用已经激活了。

5.1.1 创建图片模型

编辑images应用的models.py文件,添加以下代码:

from django.db import models
from django.conf import settings

class Image(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='images_created')
    title = models.CharField(max_length=200)
    slug = models.CharField(max_length=200, blank=True)
    url = models.URLField()
    image = models.ImageField(upload_to='/images/%Y/%m/%d')
    description = models.TextField(blank=True)
    created = models.DateField(auto_now_add=True, db_index=True)

    def __str__(self):
        return self.title

我们将使用这个模型存储来自不同网站的被标记的图片。让我们看看这个模型中的字段:

  • user:标记这张图片的User对象。这是一个ForeignKey字段,它指定了一对多的关系:一个用户可以上传多张图片,但一张图片只能由一个用户上传。
  • title:图片的标题。
  • slug:只包括字母,数据,下划线或连字符的短标签,用于构建搜索引擎友好的URL。
  • url:图片的原始URL。
  • image:图片文件。
  • description:一个可选的图片描述。
  • created:在数据库中创建对象的时间。因为我们使用了auto_now_add,所以创建对象时会自动设置时间。我们使用了db_index=True,所以Django会在数据库中为该字段创建一个索引。

数据库索引会提高查询效率。考虑为经常使用filter()exclude()order_by()查询的字段设置db_index=TrueForeignKey字段或带unique=True的字段隐式的创建了索引。你也可以使用Meta.index_together为多个字段创建索引。

我们会覆写Image模型的save()方法,根据title字段的值自动生成slug字段。在Image模型中导入slugify()函数,并添加save()方法,如下所示:

from django.utils.text import slugify

class Image(models.Model):
    # ...
    def save(self, *args, **kwargs):
        if not self.slug:
            self.slug = slugify(self.title)
            super().save(*args, **kwargs)

没有提供别名(slug)时,我们根据给定的标题,使用Django提供的slufigy()函数自动生成图片的slug字段。然后保存对象。我们为图片自动生成别名,所以用户不需要为每张图片输入slug字段。

5.1.2 创建多对多的关系

我们将会在Image模型中添加另一个字段,用于存储喜欢这张图片的用户。这种情况下,我们需要一个多对多的关系,因为一个用户可能喜欢多张图片,每张图片也可能被多个用户喜欢。

添加以下代码到Image模型中:

users_like = models.ManyToManyField(settings.AUTH_USER_MODEL,
                                    related_name='images_liked',
                                    blank=True)

当你定义一个ManyToManyFeild时,Django会使用两个模型的主键创建一张中介连接表。ManyToManyFeild可以在两个关联模型的任何一个中定义。

ForeignKey字段一样,ManyToManyFeild允许我们命名从关联对象到这个对象的逆向关系。ManyToManyFeild字段提供了一个多对多管理器,允许我们检索关联的对象,比如:image.users_like.all(),或者从user对象检索:user.images_liked.all()

打开命令行,执行以下命令创建初始数据库迁移:

python manage.py makemigrations images

你会看到以下输出:

Migrations for 'images':
  images/migrations/0001_initial.py
    - Create model Image

现在运行这条命令,让迁移生效:

python manage.py migrate images

你会看到包括这一行的输出:

Applying images.0001_initial... OK

现在Image模型已经同步到数据库中。

5.1.3 在管理站点注册图片模型

编辑images应用的admin.py文件,在管理站点注册Image模型,如下所示:

from django.contrib import admin
from .models import Image

class ImageAdmin(admin.ModelAdmin):
    list_display = ('title', 'slug', 'image', 'created')
    list_filter = ['created']

admin.site.register(Image, ImageAdmin)

执行python manage.py runserver命令启动开发服务器。在浏览器中打开http://127.0.0.1:8000/amdin/,可以看到Image模型已经在管理站点注册,如下图所示:

5.2 从其它网站上传内容

我们将允许用户标记从其它网站找到的图片。用户将提供图片的URL,一个标题和一个可选的描述。我们的应用会下载图片,并在数据库中创建一个新的Image对象。

我们从构建一个提交新图片的表单开始。在images应用目录中创建forms.py文件,并添加以下代码:

from django import forms
from .models import Image

class ImageCreateForm(forms.ModelForm):
    class Meta:
        model = Image
        fields = ('title', 'url', 'description')
        widgets = {
            'url': forms.HiddenInput,
        }

正如你所看到的,这是一个从Image模型创建的ModelForm表单,只包括titleurldescription字段。用户不会直接在表单中输入图片URL。而是使用一个JavaScript工具,从其它网站选择一张图片,我们的表单接收这张图片的URL作为参数。我们用HiddenInput组件覆盖了url字段的默认组件。这个组件渲染为带有type="hidden"属性的HTML输入元素。使用这个组件是因为我们不想用户看见这个字段。

5.2.1 清理表单字段

为了确认提供的图片URL是有效的,我们会检查文件名是否以.jpg.jpeg扩展名结尾,只允许JPG文件。Django允许你通过形如clean_()的方法,定义表单方法来清理指定字段。如果存在这个方法,它会在调用表单实例的is_valid()方法时执行。在清理方法中,你可以修改字段的值,或者需要时,为这个字段抛出任何验证错误。在ImageCreateForm中添加以下方法:

def clean_url(self):
    url = self.cleaned_data['url']
    valid_extensions = ['jpg', 'jpeg']
    extension = url.rsplit('.', 1)[1].lower()
    if extension not in valid_extensions:
        raise forms.ValidationError('The given URL does not match valid image extensions.')
    return url

我们在这段代码中定义了clean_url()方法来清理url字段。它是这样工作的:

  1. 从表单示例的cleaned_data字典中获得url字段的值。
  2. 通过分割URL获得文件扩展名,并检查是否为合法的扩展名。如果不是,抛出ValidationError,表单实例不会通过验证。我们执行了一个非常简单的验证。你可以使用更好的方法验证给定的URL是否提供了有效的图片。

除了验证给定的URL,我们还需要下载并保存图片。比如,我们可以用处理这个表单的视图来下载图片文件。不过我们会使用更通用的方式:覆写模型表单的save()方法,在每次保存表单时执行这个任务。

5.2.2 覆写ModelForm的save()方法

你知道,ModelForm提供了save()方法,用于把当前模型的实例保存到数据库中,并返回该对象。这个方法接收一个commit布尔参数,允许你指定是否把该对象存储到数据库中。如果commitFalsesave()方法会返回模型的实例,但不会保存到数据库中。我们会覆写表单的save()方法下载指定的图片,然后保存。

forms.py文件顶部添加以下导入:

from urllib import request
from django.core.files.base import ContentFile
from django.utils.text import slugify

接着在ImageCreateForm中添加save()方法:

def save(self, force_insert=False, force_update=False, commit=True):
    image = super().save(commit=False)
    image_url = self.cleaned_data['url']
    image_name = '{}.{}'.format(slugify(image.title), image_url.rsplit('.', 1)[1].lower())

    #download image from the given URL
    response = request.urlopen(image_url)
    image.image.save(image_name, ContentFile(response.read()), save=False)

    if commit:
        image.save()
    return image

我们覆写了save()方法,保留了ModelForm必需的参数。这段代码完成以下操作:

  1. 我们用commit=False调用表单的save()方法,创建了一个新的image实例。
  2. 我们从表单的cleaned_data字典中获得URL。
  3. 我们用image的标题别名和原始文件扩展名的组合生成图片名。
  4. 我们使用urllib模块下载图片,然后调用image字段的save()方法,并传递一个ContentFile对象,这个对象由下载的文件内容实例化。这样就把文件保存到项目的media目录中了。我们还传递了save=False参数,避免把对象保存到数据库中。
  5. 为了与被我们覆写的save()方法保持一致的行为,只有在commit参数为True时,才把表单保存到数据库中。

现在我们需要一个处理表单的视图。编辑images应用的views.py文件,添加以下代码:

from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import message
from .forms import ImageCreateForm

@login_required
def image_create(request):
    if request.method == 'POST':
        # form is sent
        form = ImageCreateForm(data=request.POST)
        if form.is_valid():
            # form data is valid
            cd = form.cleaned_data
            new_item = form.save(commit=False)

            # assign current user to the item
            new_item.user = request.user
            new_item.save()
            message.success(request, 'Image added successfully')

            # redirect to new created item detail view
            return redirect(new_item.get_absolute_url())
    else:
        # build form with data provided by the bookmarklet via GET
        form = ImageCreateForm(data=request.GET)

    return render(request, 'images/image/create.html', {'section': 'images', 'form': form})

为了阻止未认证用户访问,我们在image_create视图上添加了login_required装饰器。这个视图是这样工作的:

  1. 我们期望通过GET请求获得创建表单实例的初始数据。数据由其它网站的图片urltitle属性组成,这个数据由我们之后会创建的JavaScript工具提供。现在我们假设初始的时候有数据。
  2. 如果提交了表单,我们检查表单是否有效。如果有效,我们创建一个新的Image实例,但我们通过传递commit=False来阻止对象保存到数据库中。
  3. 我们把当前对象赋值给新的image对象。这样就知道每张图片是谁上传的。
  4. 我们把图片对象保存到数据库中。
  5. 最后,我们用Django消息框架创建一条成功消息,并重定向到新图片的标准URL。我们还没有实现Image模型的get_absolute_url()方法,我们会马上完成这个工作。

images应用中创建urls.py文件,添加以下代码:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^create/$', views.image_create, name='create'),
]

编辑项目的主urls.py文件,引入我们刚创建的images应用的模式:

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^account/', include('account.urls')),
    url(r'^images/', include('images.urls', namespace='images')),
]

最近,你需要创建模板来渲染表单。在images应用目录中创建以下目录结构:

templates/
    images/
        image/
            create.html

编辑create.hmtl文件,添加以下代码:

{% extends "base.html" %}

{% block title %}Bookmark an image{% endblock %}

{% block content %}
    

Bookmark an image

![]({{ request.GET.url }})
{{ form.as_p }} {% csrf_token %}
{% endblock %}

现在在浏览器中打开http://127.0.0.1:8000/images/create/?title=...&url=...,其中包括titleurl参数,后者是现有的JPG图片的URL。

例如,你可以使用以下URL:

http://127.0.0.1:8000/images/create/?title=%20Django%20and%20Duke&url=http%3A%2F%2Fmvimg2.meitudata.com%2F56d7967dd02951453.jpg

你会看到带一张预览图片的表单,如下所示:

第五章 分享内容到你的网站_第1张图片

添加描述并点击Bookmark it!按钮。一个新的Image对象会保存到数据库中。你会得到一个错误,显示Image对象没有get_absolute_url()方法。现在不用担心,我们之后会添加这个方法。在浏览器打开http://127.0.0.1:8000/admin/images/image/,确认新的图片对象已经保存了。

5.2.3 用jQuery构建书签工具

书签工具是保存在web浏览器中的书签,其中包括JavaScript代码,可以扩展浏览器的功能。当你点击书签,JavaScript代码会在浏览器正在显示的网页中执行。这对构建与其它网站交互的工具非常有用。

某些在线服务(比如Pinterest)实现了自己的书签工具,让用户可以在自己的平台分享其它网站的内容。我们会创建一个书签工具,让用户以类似的方式在我们的网站中分享其它网站的图片。

我们将使用jQuery构建书签工具。jQuery是一个非常流行的JavaScript框架,可以快速开发客户端的功能。你可以在官方网站进一步了解jQuery。

以下是用户如何在浏览器中添加书签工具,并使用它:

  1. 用户从你的网站中拖拽一个链接到浏览器的书签中。该链接在href属性中包含JavaScript代码。这段代码会存储在书签中。
  2. 用户导航到任意网站,并点击该书签。该书签的JavaScript代码会执行。

因为JavaScript代码会以书签的形式存储,所以之后你不能更新它。这是一个显著的缺点,但你可以实现一个简单的启动脚本解决这个问题。该脚本从URL中加载实际的JavaScript书签工具。用户会以书签的形式保存启动脚本,这样你就可以在任何时候更新书签工具的代码了。我们将采用这种方式构建书签工具。让我们开始吧。

images/templates/中创建一个bookmarklet_launcher.js模板。这是启动脚本,并添加以下代码:

(function() {
    if (window.myBookmarklet !== underfined) {
        myBookmarklet();
    }
    else {
        document.body.appendChild(document.createElement('script'))
            .src='http://127.0.0.1:8000/static/js/bookmarklet.js?r='+
            Math.floor(Math.random()*99999999999999999999);
    }
})();

这个脚本检查是否定义myBookmarklet变量,来判断书签工具是否加载。这样我们就避免了用户重复点击书签时多次加载它。如果没有定义myBookmarklet,我们通过在文档中添加

我们从Google加载jQuery框架,Google在高速内容分发网络中托管了流行的JavaScript框架。你也可以从http://jquery.com/下载jQuery,然后把它添加到应用的static目录中。

我们添加一个

这段代码完成以下工作:

  1. 我们从一个公有CDN加载jQuery Cookie插件,因此我们可以与cookies交互。
  2. 我们读取csrftoken cookie中的值。
  3. 我们定义csrfSafeMethod()函数,检查HTTP方法是否安全。安全的方法不需要CSRF保护,包括GETHEADOPTIONSTRACE
  4. 我们使用$.ajaxSetup()设置jQuery AJAX请求。每个AJAX请求执行之前,我们检查请求方法是否安全,以及当前请求是否跨域。如果请求不安全,我们用从cookie中获取的值设置X-CSRFToken头。这个设置会应用到jQuery执行的所有AJAX请求。

CSRF令牌会在所有使用不安全的HTTP方法的AJAX请求中引入,比如POSTPUT

5.5.3 使用jQuery执行AJAX请求

编辑images应用的images/image/detail.htmlt模板,把这一行代码:

{% with total_likes=image.users_like.count %}

替换为下面这行:

{% with total_likes=image.users_like.count users_like=image.users_like.all %}

然后修改classimage-info

元素,如下所示:

{{ image.description|linebreaks }}

首先,我们添加了另一个变量到{% with %}模板标签中,用于存储image.users_like.all的查询结果,避免执行两次查询。我们显示喜欢这张图片的用户总数,以及一个like/unlike链接:我们检查用户是否在users_like关联对象集中,根据当前用户跟这样图片的关系显示likeunlike。我们在元素中添加了以下属性:

  • data-id:显示的图片的ID。
  • data-action:用户点击链接时执行的操作。可能是likeunlike

我们将会在AJAX请求发送这两个属性的值给image_like视图。当用户点击like/unlike链接时,我们需要在客户端执行以下操作:

  1. 调用AJAX视图,并传递图片ID和action参数。
  2. 如果AJAX请求成功,用相反操作(like/unlike)更新元素的data-action属性,并相应的修改显示文本。
  3. 更新显示的喜欢总数。

images/image/detail.html模板底部添加包括以下代码的domready块:

{% block domready %}
    $('a.like').click(function(e) {
        e.preventDefault();
        $.post('{% url "images:like" %}', 
            {
                id: $(this).data('id'),
                action: $(this).data('action')
            },
            function(data) {
                if (data['status'] == 'ok') {
                    var previous_action = $('a.like').data('action');

                    // toggle data-action
                    $('a.like').data('action', previous_action == 'like' ? 'unlike' : 'like');
                    // toggle link text
                    $('a.like').text(previous_action == 'like' ? 'Unlike' : 'Like');
                    // update total likes
                    var previous_likes = parseInt($('span.count .total').text());
                    $('span.count .total').text(previous_action == 'like' ? previous_likes+1 : previous_likes-1);
                }
            }
        );
    });
{% endblock %}

这段代码是这样工作的:

  1. 我们使用$('a.like') jQuery选择器查找HTML文档中classlike元素。
  2. 我们为click事件定义了一个处理函数。用户每次点击like/unlike链接时,会执行这个函数。
  3. 在处理函数内部,我们使用e.preventDefault()阻止元素的默认行为。这会阻止链接调转到其它地方。
  4. 我们使用$.post()向服务器执行一个异步请求。jQuery还提供了执行GET请求的$.get()方法,以及一个底层的$.ajax()方法。
  5. 我们使用Django的{% url %}模板标签为AJAX请求构建URL。
  6. 我们构建在请求中发送的POST参数字典。我们的Django视图需要idaction参数。我们从元素的属性中获得这两个值。
  7. 我们定义了回调函数,当收到HTTP响应时,会执行这个函数。它的data属性包括响应的内容。
  8. 我们访问收到的datastatus属性,检查它是否等于ok。如果返回的data是期望的那样,我们切换链接的data-action属性和文本。这样就允许用户取消这个操作。
  9. 根据执行的操作,我们在喜欢的总数上加1或减1。

在浏览器中打开之前上传的图片详情页面。你会看到以下初始的喜欢总数和LIKE按钮:

点击LIKE按钮。你会看到喜欢总数加1,并且按钮的文本变为UNLIKE

当你点击UNLIKE按钮时,会执行这个操作,按钮的文本变回LIKE,总数也会相应的改变。

编写JavaScript代码时,尤其是执行AJAX请求时,推荐使用Firebug等调试工具。Firebug是一个Firefox插件,允许你调试JavaScript代码,并监控CSS和HTML的变化。你可以从这里下载Firebug。其它浏览器,比如Chrome或Safari,也有调试JavaScript的开发者工具。在这些浏览器中,右键网页中的任何一个地方,然后点击Inspect element访问开发者工具。

5.6 为视图创建自定义装饰器

我们将限制AJAX视图只允许由AJAX发起的请求。Django的Request对象提供一个is_ajax()方法,用于检查请求是否带有XMLHttpRequest,也就是说是否是一个AJAX请求。这个值在HTTP头的HTTP_X_REQUESTED_WITH中设置,绝大部分JavaScript库都会在AJAX请求中包括它。

我们将创建一个装饰器,用于在视图中检查HTTP_X_REQUESTED_WITH头。装饰器是一个接收另一个函数为参数的函数,并且不需要显式修改作为参数的函数,就能扩展它的行为。如果你忘了装饰器的概念,你可能需要先阅读这里。

因为这是一个通用的装饰器,可以作用于任何视图,所以我们会在项目中创建一个common包。在bookmarks项目目录中创建以下目录和文件:

common/
    __init__.py
    decrorators.py

编辑decrorators.py文件,添加以下代码:

from django.http import HttpResponseBadRequest

def ajax_required(f):
    def wrap(request, *args, **kwargs):
        if not request.is_ajax():
            return HttpResponseBadRequest()
        return f(request, *args, **kwargs)
    wrap.__doc__ = f.__doc__
    wrap.__name__ = f.__name__
    return wrap

这是我们自定义的ajax_required装饰器。它定义了一个wrap函数,如果不是AJAX请求,则返回HttpResponseBadRequest对象(HTTP 400)。否则返回被装饰的函数。

现在编辑images应用的views.py文件,添加这个装饰器到image_like视图中:

from common.decrorators import ajax_required

@ajax_required
@login_required
@require_POST
def image_like(request):
    # ...

如果你直接在浏览器中访问http://127.0.0.1:8000/images/like/,你会得到一个HTTP 400的响应。

如果你在多个视图中重复同样的验证,则可以为视图构建自定义装饰器。

5.7 为列表视图创建AJAX分页

我们需要在网站中列出所有标记过的图片。我们将使用AJAX分页构建一个无限滚动功能。当用户滚动到页面底部时,通过自动加载下一页的结果实现无限滚动。

我们将实现一个图片列表视图,同时处理标准浏览器请求和包括分页的AJAX请求。当用户首次加载图片列表页面,我们显示第一页的图片。当用户滚动到页面底部,我们通过AJAX加载下一页的项,并添加到主页面的底部。

我们用同一个视图处理标准和AJAX分页。编辑images应用的views.py文件,添加以下代码:

from django.http import HttpResponse
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger

@login_required
def image_list(request):
    images = Image.objects.all()
    paginator = Paginator(images, 8)
    page = request.GET.get('page')
    try:
        images = paginator.page(page)
    except PageNotAnInteger:
        # If page is not an integer deliver first page
        images = paginator.page(1)
    except EmptyPage:
        if request.is_ajax():
            # If the request is AJAX an the page is out of range
            # return an empty page
            return HttpResponse('')
        # If page is out of range deliver last page of results
        images = paginator.page(paginator.num_pages)

    if request.is_ajax():
        return render(request, 'images/image/list_ajax.html', {'section': 'images', 'images': images})
    
    return render(request, 'images/image/list.html', {'section': 'images', 'images': images})

我们在这个视图中创建了一个返回数据库中所有图片的QuerySet。然后我们构造了一个Paginator对象来分页查询结果,每页显示八张图片。如果请求的页数超出范围,则抛出EmptyPage异常。这种情况下,如果是通过AJAX发送请求,则返回一个空的HttpResponse对象,帮助我们在客户端停止AJAX分页。我们把结果渲染到两个不同的模板中:

  • 对于AJAX请求,我们渲染list_ajax.html模板。该模板只包括请求页的图片。
  • 对于标准请求,我们渲染list.html模板。该模板继承自base.html模板,并显示整个页面,同时还包括list_ajax.html模板,用来引入图片列表。

编辑images应用的urls.py文件,添加以下URL模式:

url(r'^$', views.image_list, name='list'),

最后,我们需要创建上面提到的模板。在images/image/模板目录中创建list_ajax.html模板,添加以下代码:

{% load thumbnail %}

{% for image in images %}
    s
{% endfor %}

这个模板显示图片的列表。我们将用它返回AJAX请求的结果。在同一个目录下创建list.html模板,添加以下代码:

{% extends "base.html" %}

{% block title %}Images bookmarked{% endblock %}

{% block content %}
    

Images bookmarked

{% include "images/image/list_ajax.html" %}
{% endblock %}

列表模板继承自base.html模板。为了避免重复代码,我们引入了list_ajax.html模板来显示图片。list.html模板会包括JavaScript代码,当用户滚动到页面底部时,负责加载额外的页面。

list.html模板中添加以下代码:

{% block domready %}
    var page = 1;
    var empty_page = false;
    var block_request = false;

    $(window).scroll(function() {
        var margin = $(document).height() - $(window).height() - 200;
        if ($(window).scrollTop() > margin && empty_page == false && block_request == false) {
            block_request = true;
            page += 1;
            $.get('?page=' + page, function(data) {
                if (data == '') {
                    empty_page = true;
                } else {
                    block_request = false;
                    $('#image-list').append(data);
                }
            });
        }
    });
{% endblock %}

这段代码提供了无限滚动功能。我们在base.html模板中定义的domready块中引入了JavaScript代码。这段代码是这样工作的:

  1. 我们定义了以下变量:
  • page:存储当前页码。
  • empty_page:让我们知道是否到了最后一页,如果是则接收一个空的页面。只要我们得到一个空的页面,就会停止发送额外的AJAX请求,因为我们假设此时没有结果了。
  • block_request:正在处理AJAX请求时,阻止发送另一个请求。
  1. 我们使用$(window).scroll()捕获滚动事件,并为它定义一个处理函数。
  2. 我们计算边距变量来获得文档总高度和窗口高度之间的差值,这是用户滚动的剩余内容的高度。我们从结果中减去200,因此,当用户距离页面底部小于200像素时,我们会加载下一页。
  3. 如果没有执行其它AJAX请求(block_request必须为false),并且用户没有获得最后一页的结果时(empty_page也为false),我们才发送AJAX请求。
  4. 我们设置block_requesttrue,避免滚动事件触发额外的AJAX请求,同时给page计算器加1来获取下一页。
  5. 我们使用$.get()执行AJAX GET请求,然后在名为data的变量中接收HTML响应。这里有两种情况:
  • 响应不包括内容:我们已经到了结果的末尾,没有更多页面需要加载。我们设置empty_pagetrue阻止额外的AJAX请求。
  • 响应包括内容:我们把数据添加到id为image-list的HTML元素中。当用户接近页面底部时,页面内容会垂直扩展附加的结果。

在浏览器中打开http://127.0.0.1:8000/images/,你会看到目前已经标记过的图片列表,如下图所示:

第五章 分享内容到你的网站_第6张图片

滚动到页面底部来加载下一页。确保你用书签工具标记了八张以上图片,因为我们每页显示八张图片。记住,你可以使用Firebug或类似工具追踪AJAX请求和调试JavaScript代码。

最后,编辑account应用的base.html模板,为主菜单的Images项添加URL:

  • Images
  • 现在你可以从主菜单中访问图片列表。

    5.8 总结

    在本章中,我们构建了一个JavaScript书签工具,可以分享其它网站的图片到我们的网站中。你用jQuery实现了AJAX视图,并添加了AJAX分页。

    下一章会教你如何构建关注系统和活动流。你会使用通用关系(generic relations),信号(signals)和反规范化(denormalization)。你还会学习如何在Django中使用Redis。

    你可能感兴趣的:(第五章 分享内容到你的网站)