第五章 Django By Example

在网站中分享内容

此章内容:

  • 创建一个many-to-many(多对多)关系
  • 定制表单(form)的行为
  • 在 Django 中使用 jQuery
  • 创建一个 jQuery 书签
  • 通过使用 sorl.thumbnail 来生成缩略图
  • 实现 AJAX 视图(views)并且使这些视图(views)和 jQuery 融合
  • 为视图(views)创建定制化的装饰器 (decorators)
  • 创建 AJAX 分页

建立一个能为图片打标签的网站

我们将允许用户可以在我们网站中分享他们在其他网站发现的图片,并且他们还可以为这些图片打上标签。为了达到这个目的,我们将要做以下几个任务:

  • 定义一个模型来储存图片以及图片的信息
  • 新建一个表单(form)和视图(view)来控制图片的上传
  • 为用户创建一个可以上传他们在其他网站发现的图片的系统

创建图片模型

新建应用 django-admin startapp images
添加应用
编辑models文件

from django.db import models
from django.conf import settings
# Create your models here.

class Image(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name='images_created')#标记了这张图片 User 对象。
    title = models.CharField(max_length=200)#标题
    slug = models.SlugField(max_length=200,blank=True)# slug 表示的是只有字母、数字、下划线和连字符的标签
    url = models.URLField()# 图片的源url
    image = models.ImageField(upload_to='images/%Y/%m/%d')# 图片文件
    description = models.TextField(blank=True)#描述
    created = models.DateField(auto_now_add=True,db_index=True)#auto_now_add ,当对象被创建时候时间和日期将会被自动设置,我们使用了 db_index=True ,所以 Django 将会在数据库中为这个字段创建索引

数据库索引改善了查询的执行。考虑为这个字段设置 db_index=True 是因为你将要很频繁地使用 filter(),exclude(),order_by() 来执行查询。ForeignKey 字段或者带有unique=True的字段表明了一个索引的创建。你也可以使用Meta.index_together来为多个字段创建索引。

我们将要重写 Image 模型的 save()方法来自动的生成slug字段。这个 slug字段基于title字段的值。像下面这样导入slugify()函数, 然后在 Image 模型中添加一个 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)#使用了 Django 提供的slugify()函数在没有提供slug字段时根据给定的图片标题自动生slug,然后,我们保存了这个对象。我们自动生成slug,这样的话用户就不用自己输入slug字段了。
            super(Image, self).save(*args, **kwargs)

建立多对多关系

我们将要在 Image 模型中再添加一个字段来保存喜欢这张图片的用户。因此,我们需要一个多对多关系。因为一个用户可能喜欢很多张图片,一张图片也可能被很多用户喜欢。
在 Image 模型中添加以下字段:

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

当你定义一个ManyToMany字段时,Django 会用两张表主键(primary key)创建一个中介联接表(译者注:就是新建一张普通的表,只是这张表的内容是由多对多关系双方的主键构成的)。ManyToMany字段可以在任意两个相关联的表中创建。
同ForeignKey字段一样,ManyToMany字段的related_name属性使我们可以命名另模型回溯(或者是反查)到本模型对象的关系。ManyToMany字段提供了一个多对多管理器(manager),这个管理器使我们可以回溯相关联的对象比如:image.users_like.all()或者从一个user中回溯,比如:user.images_liked.all()。
再数据表建立迁移

在admin中注册

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)

从其他网站上传内容

我们将使用户可以给从他们在其他网站发现的图片打上标签。用户将要提供图片的 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,
        }

这个表单只包含了 title,url,description字段。我们的用户不会在表单中直接为图片添加 URL。相反的,他们将会使用一个 JavaScript 工具来从其他网站中选择一张图片然后我们的表单将会以参数的形式接收这张图片的 URL。我们覆写 url 字段的默认控件(widget)为一个HiddenInput控件,这个控件将会被渲染为属性是 type="hidden"的 HTML 元素。使用这个控件是因为我们不想让用户看见这个字段。

清洁表单字段

在form类中添加

    def clean_url(self):
        url = self.cleaned_data['url']
        valid_extensions = ['jpg', 'JPG']
        extension = url.rsplit('.', 1)[-1]
        if extension is not in valid_extensions:
            raise forms.ValidationError('图片url不合法')
        return url

验证(validation)url的结尾是不是jpg或者JPG

覆写模型表单中的save()方法

把save()方法加入ImageCreateForm中:

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

def save(self, force_insert=False,
         force_update=False,
         commit=True):
    image = super(ImageCreateForm, self).save(commit=False)
    image_url = self.cleaned_data['url']
    image_name = '{}.{}'.format(slugify(image.title),
    image_url.rsplit('.', 1)[1].lower())
# 从给定的 URL 中下载图片
    response = request.urlopen(image_url)
    image.image.save(image_name,
                    ContentFile(response.read()),
                    save=False)#image是Image对象,image.image是一个image字段,这个字段是一个文件对象,保存需要使用这样的方式来
    if commit:
        image.save()
    return image

创建views

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

@login_required
def image_create(request):
    """
    View for creating an Image using the JavaScript Bookmarklet.
    """
    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()
            messages.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})

创建urls

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

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

主urls中导入

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

创建html文件

{% extends "base.html" %}

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

{% block content %}
    

Bookmark an image

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

用 jQuery 创建一个书签

你可能感兴趣的:(第五章 Django By Example)