第五章 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 %}
    <h1>Bookmark an image</h1>
    ![]({{ request.GET.url }})
    <form action="." method="post">
        {{ form.as_p }}
        {% csrf_token %}
        <input type="submit" value="Bookmark it!">
    </form>
{% endblock %}

用 jQuery 創建一個書簽

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容