Django Blog實(shí)戰(zhàn)

項(xiàng)目設(shè)置

? ? ? ?DEBUG 一個(gè)布爾型用來開啟或關(guān)閉項(xiàng)目的debug模式。如果設(shè)置為True,當(dāng)你的應(yīng)用拋出一個(gè)未被捕獲的異常時(shí)Django將會(huì)顯示一個(gè)詳細(xì)的錯(cuò)誤頁(yè)面。當(dāng)你準(zhǔn)備部署項(xiàng)目到生產(chǎn)環(huán)境,請(qǐng)記住一定要關(guān)閉debug模式。永遠(yuǎn)不要在生產(chǎn)環(huán)境中部署一個(gè)打開debug模式的站點(diǎn)因?yàn)槟菚?huì)暴露你的項(xiàng)目中的敏感數(shù)據(jù)。

? ? ?ALLOWED_HOSTS 當(dāng)debug模式開啟或者運(yùn)行測(cè)試的時(shí)候不會(huì)起作用(譯者注:最新的Django版本中,不管有沒有開啟debug模式該設(shè)置都會(huì)啟作用)。一旦你準(zhǔn)備部署你的項(xiàng)目到生產(chǎn)環(huán)境并且關(guān)閉了debug模式,為了允許訪問你的Django項(xiàng)目你就必須添加你的域或host在這個(gè)設(shè)置中。


slug:

????????這個(gè)字段將會(huì)在URLs中使用。slug就是一個(gè)短標(biāo)簽,該標(biāo)簽只包含字母,數(shù)字,下劃線或連接線。我們將通過使用slug字段給我們的blog帖子構(gòu)建漂亮的,友好的URLs。

ForeignKey:

? ? 我們通過related_name屬性指定了從UserPost的反向關(guān)系名。


定制models的展示形式

classPostAdmin(admin.ModelAdmin):

? ? ?list_display = ('title','slug','author','publish','status') ? ? #展示的字段

? ? ?list_filter = ('status','created','publish','author') ? ? #過濾返回結(jié)果

? ? ?search_fields = ('title','body') ? ? #搜索字段列

? ? ? prepopulated_fields = {'slug': ('title',)} ? ? #通過輸入的標(biāo)題來填充slug字段

? ? ? raw_id_fields = ('author',)?

? ? ? date_hierarchy ='publish' ? ?#通過時(shí)間層快速導(dǎo)航的欄

? ? ? ordering = ['status','publish']


查詢集(QuerySet)和管理器(managers)

查詢集(QuerySet)是從你的數(shù)據(jù)庫(kù)中根據(jù)一些過濾條件范圍取回的結(jié)果對(duì)象進(jìn)行的采集

每一個(gè)Django模型(model)至少有一個(gè)管理器(manager),默認(rèn)管理器(manager)叫做objects。你通過使用你的模型(models)的管理器(manager)就能獲得一個(gè)查詢集(QuerySet)對(duì)象。獲取一張表中的所有對(duì)象,你只需要在默認(rèn)的objects管理器(manager)上使用all()方法即可

Django的查詢集(QuerySets)是惰性(lazy)的,它們只會(huì)被動(dòng)的去執(zhí)行。這樣的行為可以保證查詢集(QuerySet)非常有效率。

我們之前提到過,objects是每一個(gè)模型(models)的默認(rèn)管理器(manager),它會(huì)返回?cái)?shù)據(jù)庫(kù)中所有的對(duì)象。但是我們也可以為我們的模型(models)定義一些定制的管理器(manager)。

有兩種方式可以為你的模型(models)添加管理器(managers):你可以添加額外的管理器(manager)方法或者繼承管理器(manager)的查詢集(QuerySets)進(jìn)行修改。第一種方法類似Post.objects.my_manager(),第二種方法類似Post.my_manager.all()。我們的管理器(manager)將會(huì)允許我們返回所有帖子通過使用Post.published。

例:

class PublishedManager(models.Manager):

? ? def get_queryset(self):

? ? ? ? ?return super(PublishedManager, self).get_queryset().filter(status='published')

get_queryset()是返回執(zhí)行過的查詢集(QuerySet)的方法


模型(models)的標(biāo)準(zhǔn)URLs

? ? ? ? ?Django的慣例是給模型(model)添加get_absolute_url()方法用來返回一個(gè)對(duì)象的標(biāo)準(zhǔn)URL。在這個(gè)方法中,我們使用reverse()方法允許你通過它們的名字和可選的參數(shù)來構(gòu)建URLS。


模版

????????Django有一個(gè)強(qiáng)大的模板(templates)語(yǔ)言允許你指定數(shù)據(jù)的如何進(jìn)行展示。它基于模板標(biāo)簽(templates tags),{% load staticfiles %}告訴Django去加載django.contrib.staticfiles應(yīng)用提供的staticfiles模板標(biāo)簽(temaplate tags)。通過加載它,你可以在這個(gè)模板(template)中使用{% static %}模板過濾器(template filter)。通過使用這個(gè)模板過濾器(template filter),你可以包含一些靜態(tài)文件比如說blog.css文件

truncatewords用來縮短內(nèi)容限制在一定的字?jǐn)?shù)內(nèi)

linebreaks用來轉(zhuǎn)換內(nèi)容中的換行符為HTML的換行符


分頁(yè)

Django有一個(gè)內(nèi)置的Paginator類允許你方便的管理分頁(yè)

例:

from django.core.paginator importPaginator, EmptyPage, PageNotAnInteger

def post_list(request):

? ? object_list = Post.published.all()?

? ? paginator = Paginator(object_list,3)# 3 posts in each page

? ? page = request.GET.get('page')

? ? try:?

? ? ? ? ? ?posts = paginator.page(page)

? ? except PageNotAnInteger:# If page is not an integer deliver the first page

? ? ? ? ? posts = paginator.page(1)

? ? except EmptyPage:# If page is out of range deliver last page of results

? ? ? ? ? posts = paginator.page(paginator.num_pages)

? ?return render(request,'blog/post/list.html', {'page': page,'posts': posts})


? ? ? ?現(xiàn)在,我們必須創(chuàng)建一個(gè)模板(template)來展示分頁(yè)處理,它可以被任意的模板(template)包含來使用分頁(yè)。在blog應(yīng)用的templates文件夾下創(chuàng)建一個(gè)新文件命名為pagination.html。在該文件中添加如下HTML代碼:

<div class="pagination">

? ? ? ? <span class="step-links">

????????????????{% if page.has_previous %}

? ? ? ? ? ? ? ? ? ? ? ? <a href="?page={{ page.previous_page_number }}">上一頁(yè)</a>

? ? ? ? ????????{% endif %}

? ? ? ? ? ? ? ? <span class="current">Page {{ page.number }} of {{ ????????????????????????????page.paginator_num_pages }}

????????????????</span>

????????????????{% if page.has_next %}

????????????????????????<a href="?page={{ page.next_page_number }}">下一頁(yè)</a>

????????????????{% endif %}

????????</span>

</div>


為了渲染上一頁(yè)與下一頁(yè)的鏈接并且展示當(dāng)前頁(yè)面和所有頁(yè)面的結(jié)果,這個(gè)分頁(yè)模板(template)期望一個(gè)Page對(duì)象。讓我們回到blog/post/list.html模板(tempalte)中將pagination.html模板(template)包含在{% content %}區(qū)塊(block)中,如下所示:

{% block content %}

{% include "pagination.html" with page=posts %}

{% endblock %}

我們傳遞給模板(template)的Page對(duì)象叫做posts,我們將分頁(yè)模板(tempalte)包含在帖子列模板(template)中指定參數(shù)來對(duì)它進(jìn)行正確的渲染。這種方法你可以反復(fù)使用,用你的分頁(yè)模板(template)對(duì)不同的模型(models)視圖(views)進(jìn)行分頁(yè)處理。


Paginator是如何工作的:

? ? 1.我們使用希望在每頁(yè)中顯示的對(duì)象的數(shù)量來實(shí)例化Paginator類。

? ? 2.我們獲取到page?GET參數(shù)來指明頁(yè)數(shù)

? ? 3.我們通過調(diào)用Paginatorpage()方法在期望的頁(yè)面中獲得了對(duì)象。

? ? 4.如果page參數(shù)不是一個(gè)整數(shù),我們就返回第一頁(yè)的結(jié)果。如果這個(gè)參數(shù)數(shù)字超出了最大的頁(yè)數(shù),我們就展示最后一頁(yè)的結(jié)果。

? ? 5.我們傳遞頁(yè)數(shù)并且獲取對(duì)象給這個(gè)模板(template)。


使用Django創(chuàng)建表單

Django提供了兩個(gè)可以創(chuàng)建表單的基本類:

????Form: 允許你創(chuàng)建一個(gè)標(biāo)準(zhǔn)表單

????ModelForm: 允許你創(chuàng)建一個(gè)可用于創(chuàng)建或者更新model實(shí)例的表單

is_valid()方法來驗(yàn)證提交的數(shù)據(jù)。這個(gè)方法會(huì)驗(yàn)證表單引進(jìn)的數(shù)據(jù),如果所有的字段都是有效數(shù)據(jù),將會(huì)返回True。一旦有任何一個(gè)字段是無效的數(shù)據(jù),is_valid()就會(huì)返回False。你可以通過訪問form.errors來查看所有驗(yàn)證錯(cuò)誤的列表。

如果表單數(shù)據(jù)驗(yàn)證通過,我們通過訪問form.cleaned_data獲取驗(yàn)證過的數(shù)據(jù)。這個(gè)屬性是一個(gè)表單字段和值的字典。


使用Django發(fā)送email

使用Django發(fā)送email非常簡(jiǎn)單。首先,你需要有一個(gè)本地的SMTP服務(wù)或者通過在你項(xiàng)目的settings.py文件中添加以下設(shè)置去定義一個(gè)外部SMTP服務(wù)器的配置:

EMAIL_HOST: SMTP服務(wù)地址。默認(rèn)本地。

EMAIL_POSR: SMATP服務(wù)端口,默認(rèn)25。

EMAIL_HOST_USER: SMTP服務(wù)的用戶名。

EMAIL_HOST_PASSWORD: SMTP服務(wù)的密碼。

EMAIL_USE_TLS: 是否使用TLS加密連接。

EMAIL_USE_SSL: 是否使用隱式的SSL加密連接。

from django.core.mail import send_mail

send_mail()方法需要這些參數(shù):郵件主題,內(nèi)容,發(fā)送人以及一個(gè)收件人的列表。通過設(shè)置可選參數(shù)fail_silently=False,我們告訴這個(gè)方法如果email沒有發(fā)送成功那么需要拋出一個(gè)異常。

由于我們需要在email中包含帖子的超鏈接,所以我們通過使用post.get_absolute_url()方法來獲取到帖子的絕對(duì)路徑。我們將這個(gè)絕對(duì)路徑作為request.build_absolute_uri()的輸入值來構(gòu)建一個(gè)完整的包含了HTTP schema和主機(jī)名的url。

post_url = request.build_absolute_url(post.get_absolute_url())


創(chuàng)建一個(gè)評(píng)論系統(tǒng)

{% with %}標(biāo)簽(tag)允許我們分配一個(gè)值給新的變量,這個(gè)變量可以一直使用直到遇到{% endwith %}標(biāo)簽(tag)。

{% with %}模板(template)標(biāo)簽(tag)是非常有用的,可以避免直接操作數(shù)據(jù)庫(kù)或避免多次調(diào)用花費(fèi)較多的方法。


創(chuàng)建自定義的模板標(biāo)簽(template tags)和過濾器(filters)

當(dāng)你需要在你的模板中添加功能而Django模板標(biāo)簽(template tags)的核心設(shè)置無法提供此功能的時(shí)候,自定義模板標(biāo)簽會(huì)非常方便。

創(chuàng)建自定義的模板標(biāo)簽(template tags)

Django提供了以下幫助函數(shù)(functions)來允許你以一種簡(jiǎn)單的方式創(chuàng)建自己的模板標(biāo)簽(template tags):

simple_tag:處理數(shù)據(jù)并返回一個(gè)字符串(string)

inclusion_tag:處理數(shù)據(jù)并返回一個(gè)渲染過的模板(template)

assignment_tag:處理數(shù)據(jù)并在上下文(context)中設(shè)置一個(gè)變量(variable)

模板標(biāo)簽(template tags)必須存在Django的應(yīng)用中。

from django import template

register = template.Library()

from ..models import Post

@register.simple_tag

def total_posts():

????????return Post.published.count()

在使用自定義的模板標(biāo)簽(template tags)之前,你必須使用{% load %}標(biāo)簽在模板(template)中來加載它們才能有效。

@register.inclusion_tag('blog/post/latest_posts.html')

def show_latest_posts(count=5):

latest_posts = Post.published.order_by('-publish')[:count]

return {'latest_posts': latest_posts}

我們通過裝飾器@register.inclusion_tag注冊(cè)模板標(biāo)簽(template tag),指定模板(template)必須被blog/post/latest_posts.html返回的值渲染

這個(gè)函數(shù)返回了一個(gè)字典變量而不是一個(gè)簡(jiǎn)單的值。包含標(biāo)簽(inclusion tags)必須返回一個(gè)字典值,作為上下文(context)來渲染特定的模板(template)。包含標(biāo)簽(inclusion tags)返回一個(gè)字典。

@register.assignment_tag

def get_most_commented_posts(count=5):

return Post.published.annotate(

total_comments=Count('comments')

).order_by('-total_comments')[:count]

聚合了每一個(gè)帖子的評(píng)論總數(shù)并保存在total_comments字段中

過濾器其實(shí)就是Python函數(shù)并提供了一個(gè)或兩個(gè)參數(shù)————一個(gè)是需要處理的變量值,一個(gè)是可選的參數(shù)。它們返回的值可以被展示或者被別的過濾器(filters)處理。

from django.utils.safestring import mark_safe

import markdown

@register.filter(name='markdown')

def markdown_format(text):

return mark_safe(markdown.markdown(text))


為你的blog帖子創(chuàng)建feeds

Django有一個(gè)內(nèi)置的syndication feed框架,可以動(dòng)態(tài)(dynamically)生成RSS或者Atom feeds。

from django.contrib.syndication.views import Feed

from django.template.defaultfilters import truncatewords

from .models import Post

class LatestPostsFeed(Feed):

????????title = 'My blog'

????????link = '/blog/'

????????description = 'New posts of my blog.'

????????def ?items(self):

????????????????return Post.published.all()[:5]

????????def item_title(self, item):

????????????????return item.title

????????def item_description(self, item):

????????????????return ?truncatewords (item.body, 30)


手工渲染字段

{{ form.subject.errors }}

{{ form.subject.label_tag }}

{{ form.subject }}

{{form.non_field_errors}}查找每個(gè)字段的錯(cuò)誤。

{{form.name_of_field.errors}}顯示表單錯(cuò)誤的一個(gè)清單,并渲染成一個(gè)ul


widget

widgets用于指定Django在HTML的<input>元素的表現(xiàn)形式

設(shè)置weidget實(shí)例樣式 利用widget.attrs


save(commit=False)

save()方法接受一個(gè)commit的參數(shù),其值為True或者False。默認(rèn)為True。

如果你聲明 save(commit=False),那么它就會(huì)返回一個(gè)還未保存至數(shù)據(jù)庫(kù)的對(duì)象,這樣的話 你可以用這個(gè)對(duì)象添加一些額外的數(shù)據(jù),然后在用save()保存到數(shù)據(jù)庫(kù)


自定義管理頁(yè)面

Django提供了admin.ModelAdmin類通過定義ModelAdmin的子類,來定義模型在Admin界面的顯示方式.

列表頁(yè)屬性

list_display:顯示字段,可以點(diǎn)擊列頭進(jìn)行排序

list_filter:過濾字段,過濾框會(huì)出現(xiàn)在右側(cè)

search_fields:搜索字段,搜索框會(huì)出現(xiàn)在上側(cè)

list_per_page:分頁(yè),分頁(yè)框會(huì)出現(xiàn)在下側(cè)


添加、修改頁(yè)屬性

fields:屬性的先后順序

fieldsets:屬性分組

fieldsets = [

? ? ? ('basic',{'fields': ['btitle']}),

? ? ? ('more', {'fields': ['bpub_date']}),

]


關(guān)聯(lián)對(duì)象

一對(duì)多的關(guān)系中,可以在一端的編輯頁(yè)面中編輯多端的對(duì)象,嵌入多端對(duì)象的方式包括表格、塊兩種。 類型InlineModelAdmin:表示在模型的編輯頁(yè)面嵌入關(guān)聯(lián)模型的編輯。子類TabularInline:以表格的形式嵌入。

子類StackedInline:以塊的形式嵌入。

1)打開booktest/admin.py文件,創(chuàng)建AreaStackedInline類。

class AreaStackedInline(admin.StackedInline):

? ? ?model=AreaInfo? ? #關(guān)聯(lián)子對(duì)象

? ? ?extra=2? ? #額外編輯2個(gè)子對(duì)象

2)打開booktest/admin.py文件,修改AreaAdmin類如下:

class? AreaAdmin(admin.ModelAdmin):

? ...

? ? inlines=[AreaStackedInline]


class AreaTabularInline(admin.TabularInline):

? ? ? model=AreaInfo? #關(guān)聯(lián)子對(duì)象

? ? ? extra=2? #額外編輯2個(gè)子對(duì)象


class? AreaAdmin(admin.ModelAdmin):

? ? ...

? ? inlines=[AreaTabularInline


布爾值的顯示

發(fā)布性別的顯示不是一個(gè)直觀的結(jié)果,可以使用方法進(jìn)行封裝

def gender(self):

? ? if self.hgender:

? ? ? ?return '男'

? ?else:

? ? ? return '女'

gender.short_description = '性別'

在admin注冊(cè)中使用gender代替hgender

class HeroInfoAdmin(admin.ModelAdmin):

list_display = ['id', 'hname', 'gender', 'hcontent']

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容