原文鏈接
Django教程
Python一直是我最喜歡的語言,在這個寒假打算認真學習一下Python的Web框架。在Django和Tornado之間我選擇了前者,沒有特別的原因,網上人云亦云的,肯定不會有一方離另一方差很遠,我就直接去看了看Github上兩個項目的活躍度,所以選擇了前者。
應該說Django堅持自己造輪子,確實為開發者節約了不少的時間,我很看重它的擴展功能,packages數量十分豐富。Django采用的是最流行也是我最熟悉的MVC設計模式,雖然在之前的一個PHP(Laravel)項目中也是采用的MVC模式,但一直都沒怎么吃透,始終在各層分離的時候不是很清晰,所以也可趁學習Django對MVC的概念進行強化。
Django另一個我特別喜歡的特性就是Application,它與Project的概念不同,一個APP就相當于一個功能模塊,一個Project可以包含多個APP,一個APP可以同時被多個Project引用,App增加了代碼的復用機會,提高了擴展性和松耦合性,Django中很多的packages都是以APP的形式存在的。
另外,我學習主要參考的是開源書籍 Django搭建簡易博客教程
下面是搭建一個Django環境的基本步驟:
-
新建項目
django-admin startproject 項目名
這樣會在當前目錄新建一個目錄,里面已經有一些基本的配置文件:django_test ├── db.sqlite3 ├── django_test │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py
-
新建APP
django-admin startapp APP名稱
如果是搭建一個非常簡單的應用,那么不使用APP也行,只需要吧路由指向目標view就可以了,但是如果要搭建復雜的應用并且需要良好的隔離性,那最好使用APP。同樣,使用該命令也會在當前目錄下新建一個目錄,里面已經包含一些配置文件:django_test/testapp ├── admin.py # 注冊models,用于admin管理 ├── __init__.py ├── migrations # 數據庫遷移 ├── models.py # 定義models ├── tests.py # 單元測試 ├── apps.py # App的配置類,AppConfig用于存儲應用程序的元數據 └── views.py # 視圖文件
如過添加了APP,那么需要在主配置文件
settings.py
里面的INSTALLED_APPS
里面添加該APP的名稱 -
Hello World! 所有入門教程都必須要有一個Hello World! 首先,在APP的視圖文件views.py里添加函數,該函數直接返回一個字符串的響應:
from django.shortcuts import render from django.http import HttpResponse def hello(request): return HttpResponse('Hello World!')
然后添加URL,在Project目錄里的
urls.py
里進行管理,添加hello的url如下:from django.conf.urls import patterns, include, url
from django.contrib import adminurlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^hello/', 'testapp.views.hello'),
] 運行
python manage.py runserver
如果要以daemon的方式在后臺運行,可以使用nohup命令nohup python manage.py runserver 0.0.0.0:8000 &
使用它可以打開Django自帶的默認Web引擎,可以在 http://127.0.0.1:8000中查看 在測試的時候可以使用該引擎,它不僅輕量,而且在打開后還會自動檢測代碼的更改,進行自動更新,這樣就不用每次對代碼變動了都來重啟一次
配置項
全局配置
需要注意的是,Django官方并沒有默認的分離配置文件的方案,我覺得最佳的方式是,建立多個配置文件,然后在默認的配置文件里面進行導入即可。例如:
env = 'local'
if env == 'local':
from settings_local import *
else:
from settings_prod import *
配置文件內容
DEBUG = True # DEBUG模式
TEMPLATE_DEBUG = True # TEMPLATE的DEBUG模式
ALLOWED_HOSTS = [] # 設置哪些域名可以訪問,當debug為false時必須為其指定一個值,['*']表示允許所有的訪問
INSTALLED_APPS = [默認APP+自己的APP]
MIDDLEWARE_CLASSES = [中間件]
ROOT_URLCONF = 'admin.urls' # 讀取的默認的url文件
# Database 數據庫的配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # MySQL/Mariadb數據庫設置
'NAME': 'admin',
'USER': 'root',
'PASSWORD': 'mysql',
'HOST': '127.0.0.1',
'PORT': 3307
}
}
LANGUAGE_CODE = 'en-us' # 語言,中文可用zh-Hans、zh-CN,完整列表見:http://www.i18nguy.com/unicode/language-identifiers.html
TIME_ZONE = 'Asia/Chongqing' # 時區
USE_TZ = False # 數據庫中要使用時間戳就應該關閉這個
# Static files (CSS, JavaScript, Images) 靜態文件目錄
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 這個選項默認是沒有的,在編碼時將靜態文件放在APP中的static目錄下,部署時用python manage.py collectstatic就可以把靜態文件收集到STATIDC_ROOT目錄
在其他文件訪問全局配置項,可以這樣訪問:
from django.conf import settings
settings.DEBUG
應用配置
在上面新建的app的目錄結構里面又一個apps.py
文件,它存儲了應用的元數據,通過繼承AppConfig
來配置其屬性,可配置的選項如下:
可配置的屬性
AppConfig.name # 應用的完整Python路徑,例如django.crontrib.admin,在整個Django項目中必須是唯一的
AppConfig.label # 應用的縮寫,例如admin,
AppConfig.verbose_name # 應用的適合閱讀的名稱
AppConfig.path # 應用目錄的文件系統路徑,例如/usr/lib/python3.4/dist-packages/django/contrib/admin
可配置的方法
AppConfig.get_models() # 返回可迭代的Model類
AppConfig.get_model(model_name) # 返回具體的Model
AppConfig.ready() # 執行初始化任務
請求與響應
HttpResponse('字符串', content_type="text/plain") # 指定content_type的響應
HttpRequest.method # 請求種類
HttpRequest.GET # 獲取所有的GET參數(字典)
HttpRequest.POST # 獲取POST的參數(字典)
HttpRequest.scheme # 表示請求的模式,是http還是https
HttpRequest.cookies # 包含了所有的cookie信息
HttpRequest.session # session信息
HttpRequest.FILES # 包含了上傳的文件
HttpRequest.meta # 包含了http請求的各種headers
HttpRequest.user # 當前的登錄的用戶,配合著auth使用
get_host() #不解釋了吧
get_full_path() # 獲取路徑,不包含域名
build_absolute_uri() # 獲取完整路徑
is_secure() # 如果是https返回true,否則false
is_ajax() # 是否是ajax請求
return JsonResponse(error, status = 422) # 返回指定狀態碼
ip = request.META.get('REMOTE_ADDR') # 獲取用戶IP
return HttpResponseRedirect('/') # 重定向
路由與視圖
url: web訪問請求的入口(相當于Laravel里的路由)
view:應用的邏輯部分,從客戶端接收請求,處理并返回數據,一般返回到template模板進行渲染(相當于Laravel里的控制器)
將/test
定位到article這個APP里面的views里面的home方法來處理的形式
url(r'^test$', 'article.views.home')
url傳遞參數
Django的路由是采用正則表達式來匹配的,同樣能使用命名組,比如(?P<name>)
,這樣就可以通過URL給views傳遞參數了,例如:
# 有如下視圖
def hello(request, name):
return HttpResponse('name is %s', % name)
# 在url中可以這樣寫
url(r'^(?P<name>\d+)/$', 'testapp.views.hello)
url命名
url(r'^add/$', 'app.views.add', name='add')
給url命名可以方便我們進行統一修改url樣式,比如之前用a-and-b
的url可以訪問到add這個方法,但現在如果想改成a/b
的方式來訪問,那么由于后端的模板渲染等都是用的其命名add
,就無需修改后端邏輯了
路由按照app分組
首先主urls.py里面使用include包含應用下的urls.py文件
from django.conf.urls import include
urlpatterns = [
url(r'^app1', include('app1.urls')),
]
然后在app1下的urls.py文件里面,進行如下設置:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^/$', views.hello),
]
這樣就可以通過/app1/
,來訪問app1下的hello方法了
數據庫
Django同很多框架一樣使用了ORM(Object Relational Mapping,對象關系映射)的方式,每個model類代表一張數據庫的表,每一個屬性代表其一個字段(這種特性的實現依賴于python的元類)。
數據表定義
定義model的文件是project/app/models.py
里面,例如,要定義一張用戶表:
fromo django.db import models
class User(models.Model):
username = models.CharField(max_length = 20) # 用戶名字段
create_time = models.DateTimeField(auto_now_add = True) # 注冊日期字段,如果同時有兩個字段對應著同一個外鍵,那么久得重命名字段名了,比如:
receiver = models.ForeignKey(Users, null=True, related_name='receiver')
poster = models.ForeignKey(Users, null=True, related_name='poster')
def __str__(self):
'''這個函數可以用于str(obj)函數來輸出該對象的信息,默認是表名'''
return self.username
class Meta:
db_table = '自定義表名'
unique_together = ('column_1', 'column_2') # 聯合唯一鍵
當建立好models過后,執行如下命令就可以在數據庫中新建或更新數據表了:
python manage.py makemigrations
python manage.py migrate
注:如果是有修改的,那么新添加的數據必須要允許null或者設置默認值,否則會報錯,這其實是為了保護已經存在了的數據,當然在添加完該字段后把null去掉再更新數據庫就可以了。
字段類型
注:Django默認為每張表設置了一個int(11)的自增主鍵,不需要自己去定義了。
字段通用參數
primary_key = False/True # 是否設置為主鍵
blank = False/True # 是否可為空,這其實是用于Field的判斷
null = False/True # 是否可為空,這才是真正的數據庫里面是否可以為null
max_length = 3 # 最大長度,對整型無效
default = '' # 設置默認值
verbose_name = # 相當于備注,如果沒給出那么就是該字段,當然,要指定的話,可以直接第一個參數一個字符串就可以指定了
editable = False/True # 是否可編輯
unique = False/True # 是否唯一
auto_now = False/True # 用于時間,每次更新記錄的時候更新該字段
auto_now_add = False/True # 用于時間,創建新紀錄的時候自動更新該字段
choices # 很實用的一個功能,相當于存儲一個枚舉列表,其中左邊的key是實際存儲在數據庫中的值,例如,可以這樣定義一個字段:
YEAR_IN_SCHOOL_CHOICES= (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
)
然后在定義字段的時候給個參數choices=YEAR_IN_SCHOOL_CHOICES,在插入字段的時候,使用'RF'這樣的,在獲取字段值的時候這樣:p.get_year_in_school_display()即可顯示'Freshman'
verbose_name # 定義字段的注釋
常用類型
# 數字類型:
AutoField # 自增長字段
IntegerField # 長度為11的整數
PositiveIntegerField:
SmallIntegerField
PositiveSmallIntegerField
BigIntegerField:
BinaryField:
BooleanField:
NullBooleanField:
DecimalField(max_digits = None, decimal_places = None)
FloatField
# 字符類型
CharField # 字符串類型,可用max_length指定長度,枚舉類型也使用該方式,只需要指定枚舉枚舉元組即可,例如type = models.CharField('類型', choices=CONTENT_TYPE),其中CONTENT_TYPE=(('a', 'abc'))
TextField:text類型
CommaSeparatedIntegerField:用逗號分隔的整數,我擦,這有用
# 時間類型
DateField # DATE類型
TimeField # datetime.time,時間
DateTimeField() # DATETIME類型,包括了日期和時間,需要注意的是Django默認的TIME_ZONE是UTC,在初始化的時候,格式如"2015-04-27T15:01:00Z",它屬于python里面的datetime.datetime類型,可分別用year/month/day等獲取時間。另外Django如果要實用MySQL里面的TIMESTAMP類型也是用該字段表示,并且在插入的時候不能直接插入一個整數,依然只能插入一個datetime.datetime對象,用時間戳的時候USE_TZ必須為False
unique_for_date屬性:比如,如果有一個title字段,其有一個參數unique_for_date = "pub_date",那么該表就不會出現title和pub_date同時相同的情況,其它的還有unique_for_month,unique_for_year
其它很有用的類型:
EmailField:Email郵箱類型
FileField:文件類型,不過不能設置為primary_key和unique,要使用該字段還有很多需要注意的地方,具體的見官方文檔
FilePathField:同上
ImageField
IPAddressField:從1.7開始已經不建議使用了,應該使用下面這個
GenericIPAddressField:
URLField
UUIDField
數據庫SQL操作
要使用model,必須先導入它,例如from app.models import Blog
,一條記錄就是一個model類的實例,要獲取其字段值,直接用點號訪問即可,例如有Blog對象blog,那么可以直接用blog.userName訪問其值。
如果想要在執行數據庫操作的時候查看數據庫的實際的SQL語句,可以這樣
from django.db import connection
print(Blogobjects.filter(name="").query) # 這樣可以將SQL語句打印出來
connection.queries # 會返回一個所有執行過的SQL的列表,并且每條時一個字典,包含了SQL語句以及SQL所執行的時間
查詢記錄
Blog.objects.all() # 獲取該表的所有記錄,返回的是記錄對象組成的列表
Blog.objects.get(pk=1) # 根據主鍵獲取數據
Blog.objects.get(name="") # 只會找到第一個匹配的數據
Blog.objects.filter(name="")# 這個就會找到匹配的多個數據
Blog.objects.filter(name__contains="") # 模糊查找name字段的值,返回列表
Blog.objects.order_by("字段1", "字段2") # 排序,order_by不加任何參數表示不需要排序
Blog.objects.all().order_by("字段")
Blog.objects.count() # 返回記錄總數
Blog.objects.values('id', 'name') # 相當于select id name from Blog,返回的事是一個字典
Blog.objects.values('name').distinct() # distinct在django的mysql引擎中無法對field進行distinct操作,所以需要這樣做
Blog.objects.values_list('id', flat=True)# 查詢該字段的所有值并且返回的是id的列表,而不是包括了名字的字典
Blog.objects.all().defer('title') # 僅僅取某個字段,這里返回是一個model對象
Blog.objects.all().only('title') # 僅僅取某個字段,也是返回一個model對戲那個
Blog.objects.all().values_list('title') # 僅僅取某個字段,這里返回一個數組
Blog.objects.latest('id') # 根據某個字段查找其最后一條記錄,返回的是一個對戲那個,不是id
Blog.objects.filter(time__gte = '2015-07-23', time__lte = '2015-07-24') # 大于等于并且小于等于,不加e表示不能等于
Blog.objects.filter(time__isnull = True)# 判斷某個字段是否為空
Blog.objects.all().exclude(id=7) # 排除,即不等于
Blog.objects.filter('time__year': '2015', 'time__month': '08', 'time__day': '17'):按年月日查詢日期,可僅查詢其中某一個條件
# Q查詢,可以對關鍵字參數進行封裝,可以使用&,|,~等操作
from django.db.models import Q
Blog.objects.filter( Q(name__startswith='wang') | ~Q(name__startswith='hao') )
Blog.objects.get( Q(name__startswith='wang'), Q(name__startswith='hao')) # 逗號就可以直接表示and了
print(People.objects.filter(
(Q(birth_lunar__month=old_month) & Q(birth_lunar__day=old_day)) |
(Q(birth_new__month=new_month) & Q(birth_new__day=new_day))
).query)
新增記錄
post = Blog(userName="wanghao", userId=12)
post.save()
# 批量插入/新增
posts = []
for i in title:
posts.append(Posts(title = title))
Posts.objects.bulk_create(posts)
更新記錄
post = Blog.objects.filter(id=1).update(userName="new") #1.7之前更新單條記錄如果要用字典的話就只能這樣了
Blog.objects.all().update(userName="new") # 還可以批量更新
obj, created = Posts.objects.update_or_create(pk = 3, title='wang', defaults = updated_values) # 1.7之后可以用這種方法來更新或者創建一個,如果沒找到對象,那么就新建,新建或者更新的字典是defaults的值,返回值中,obj表示該對象,created是一個布爾值
get_or_create(title='wang', defaults=\{\}):獲取或者新建
刪除記錄
Blog.objects.get(userName="xiao").delete()
Blog.objects.all().delete()
Blog.author.through.objects.filter(author = author.id).delete() # 刪除多對多關系,僅僅是刪除關系,而不是刪除對象
數據約束
ForeignKey
例如:
# modles.py
class System(models.Model):
name = models.CharField(max_length = 20)
class Server(models.Model):
ip = models.GenericIPAddressField(default = '127.0.0.1')
system = models.ForeignKey(System)
# views.py里面這樣子使用
server = Server.objects.get(id=1)
server_system = server.system.name # 這樣就可以獲取到那個name了
OneToMany(hasMany)
一對多關系,同樣使用ForeighKey實現,例如
# 在models.py中定義
class Posts(models.Model):
title = models.CharField('標題', max_length = 50)
class Comments(models.Model):
post = models.ForeignKey(Posts, related_name = 'comments_set')
# 在views.py中這么用
post = Posts.objects.get(pk = 1) # 獲取一篇文章
comments = post.comments_set.all() # 獲取該文章的所有評論,是一個列表
ManyToManyField
多對多關系,有一種特殊情況,如果需要對這種關系添加額外的字段,可以使用through,添加額外的表來表示,例如,用戶一張表,被使用的物品一張表,用戶與物品是多對多的關系,但是有時候我們需要記錄下用戶使用該物品的一些其他屬性,比如使用了多少次什么的,這時候就需要給這個多對多關系添加額外的字段來表示,那就需要添加額外的表了,示例如下:
class User(models.Model):
username = models.CharField(max_length = 20)
goods = models.ManyToManyField('物品', 'Goods', through='user_goods')
class Goods(models.Model):
goodsname = models.CharField(max_length = 20)
class user_goods(models.Model):
user = models.ForeignKey(User)
goods = models.ForeignKey(Goods)
clicks = models.IntegerField('點擊量', default=0)
OneToOneField
必須是一對一,而不是多對一或一對多
分頁
Django使用內建的paginator模塊進行分頁的操作,十分方便。使用方法見例子:
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger # 導入模塊
def listing(request):
contact_list = Contacts.objects.all() # 獲取所有model對象
paginator = Paginator(contact_list, 25) # 第二個參數是每頁顯示的數量
page = request.GET.get('page') # 獲取URL參數中的page number
try:
contacts = paginator.page(page)
except PageNotAnInteger: # 若不是整數則跳到第一頁
contacts = paginator.page(1)
except EmptyPage: # 若超過了則最后一頁
contacts = paginator.page(paginator.num_pages)
return render_to_response('list.html', {"contacts": contacts})
雖然contacts是一個Page對象,但是在模板中仍然可以使用for循環對其進行遍歷,它其實是一個對象所組成的list。下面是分頁按鈕html模板例子:
<nav>
<ul class="pagination">
<li class="{% if current_page == 1 %}disabled{% endif %}"><a href="#" aria-label="Previous"><span aria-hidden="true"></span></a></li>
{% for index in page_index %}
{% if index == current_page %}class="active"{% endif %}<a href="#";{{ index }}<span class="sr-only"(current)/</span></a></li>
{% endfor %}
<li class="{% if current_page == num_pages %}disabled{% endif %}<a href="#" aria-label="Previous"><span aria-hidden="true"></span></a></li>
</ul>
</nav>
初始化數據
為了方便遷移,讓別人使用你的APP,有時候需要為APP里面的表提供demo數據,這時候就需要預先填充一些數據.這里使用Django的fixtures方式填充(Django提供兩種填充方式)。使用JSON格式,我們可以首先使用manage.py dumpdata data.json
方式到處原來數據庫中內容看看該格式,類似如下:
[
{
"fields": {
"userName": "小豪",
"title": "第一篇文章",
"userId": 1,
"update": "2015-04-27T15:01:03Z",
"datetime": "2015-04-27T15:01:00Z",
"content": "這是文章的內容"
},
"model": "digital.blog",
"pk": 1
},
{
"fields": {
"userName": "笑總",
"title": "第二篇文章",
"userId": 2,
"update": "2015-04-28T15:01:03Z",
"datetime": "2015-04-28T15:01:03Z",
"content": "這是文章的內容嗎"
},
"model": "digital.blog",
"pk": 2
},
]
我們可以自己按照這個模板新建填充數據,其中pk指的是主鍵值。當建立好json文件過后,執行python manage.py loaddata data.json
即可導入數據。
Template: Django模板
和所有的MVC框架一樣,模板功能是必須有的。這里介紹一下Django模板的使用方法。
模板定義
為了方便管理,最好在app的目錄下新建templates文件夾用于存放模板文件,然后在project的配置文件settings.py中指明模板文件夾的位置:
TEMPLATES['DIRS']這個變量中添加即可,比如
'DIRS': [
os.path.join(BASE_DIR, 'dashboard/templates').replace('\\', '/'),
]
這樣,在該app的view中就可以這樣使用templates下的test.html模板文件了。例如:
def test(request):
return render(request, 'test.html')
參數傳遞
要向模板中傳遞參數,可以給render添加第三個參數,該參數其實是一個字典,在模板中可以直接使用該字典的key,例如:
return render(request, 'test.html', {'name1': value1, 'name2': value2} )
這樣,在模板文件test.html中就可以直接{{ name1 }}
來使用name1
的值了。
繼承與引用
模板方便之處就是可以使用繼承將代碼分塊并且將重復的地方都寫在一個base.html里。當要實現繼承的時候在html文件第一行寫上{% extends 'base.html' %}
,然后分別實現其區塊即可。 在base模板中一般這樣定義區塊:
{% block 塊名 %}
這里直接寫html代碼
{% endblock %}
如果子模塊沒有定義某個block的內容,那么就采用父模板的,如果需要使用父模板的內容可以用{{ block.super }}
模板也可以通過引用其它模板的代碼,例如,在要引用的地方使用:
{% include 'nav.html' %}
{% include 'includes/nav.html' %}
靜態文件css、js、img
靜態文件一般當然是要存放在自己的app里面,這時候,就應該指定靜態文件的路徑,在project的配置文件settings.py中添加如下配置:
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
'f:/project/app/static', # 無論是windows還是linux都要用左斜杠喲
)
在模板中使用靜態文件就這樣:
{% load staticfiles %}
<link href="{% static 'css/style.css' %}" rel="stylesheet">
模板標簽
Django內置了一些比較常用又實用的標簽:
# 變量
## 通過下標獲取列表變量的值
{{ names.0 }}
# 注釋
{# 單行注釋 #}
{% comment %}多行注釋{% endcomment %}
# url路由
有這樣一個路由
url(r'^blog/', 'myapp.views.blog'), # 博客頁面
那么就這樣使用
{% url 'digital.views.blog' %}
或者使用別名,例如
url(r'^blog/', 'myapp.views.blog', name='blog'),
{% url 'blog' %}
如果是其他app的url,那么需要帶上該app的namespace,首先在定義的時候需要添加namespace,如
url(r'^oauth/', include('oauth.urls', namespace='oauth'))
然后在實用url的時候:
{% url 'oauth:hello' %}
# for循環
{% for <element> in <list> %}{% endfor %}
{% for <element> in <list> reversed%}{% endfor %} 反向迭代列表
{% for <element> in <list> %}{% empty %}{% endfor %} 列表為空時的輸出內容
{{ forloop.counter }} # 獲取當前索引,默認從1開始
{{ forloop.counter0 }} # 獲取當前索引,從0開始
# if語句
{% if <element> %}
{% elif <element> %}
{% else %}
{% endif %}
{% ifequal 變量1 變量2 %}
比較值
{% endifequal %}
ifnotequal同上
過濾器
可以直接格式化輸出,是一種最便捷的轉換變量輸出格式的方式。
{{ today | data: "F j, Y" }}
這里可以將today這個變量直接按照規定格式輸出。
這里是常見的過濾器:
add:將該數字加上一個數字,例如 `{{ value|add:"2" }}`,如果原來的值為4,那么新的值就為6,不僅進可以作用與int,還能作用與列表,將列表中每個值都加
addslashes:添加反斜杠到需要轉義的地方前
capfirst:第一個字母大寫
center:在字符串前后加空格,并讓該字符串位于中間,例如 `{{ value|center: "5" }}`,那么輸出時前后都是5個空格
cut:去除字符串中的指定字符,例如`{{ value|cut:" " }}`,表示去除字符串中的所有空格
date:按指定的格式字符串參數格式化date或者datetime對象,參數太多了,看[文檔](https://docs.djangoproject.com/en/1.8/ref/templates/builtins/)
default:參數沒賦值的情況下給定默認值,例如`{{ value|default: "nothing" }}`
default_if_none:如果它是none就給定默認值
dictsort:將一個字典組成的列表排序,例如`{{ value|dictsort:"name" }}`意思是將value這個列表里面的字典按照字典里的name的順序來排序
dictsortreversed:與上面順序相反
divisibleby:是否能被整除,返回True/False,例如`{{ value|divisibleby:"3" }}`
filesizeformat:-h方式輸出文件的大小,例如`{{ value|filesizeformat}}`,如果value=123456789,那么輸出將是117.7MB
first:返回列表的第一個值
floatformat:設置浮點數的顯示形式
get_digit:獲取一個整數的倒數第幾個數字,例如`{{ value|get_digit:"2" }}`,那么123456789的值為8
join:將一個列表的值添加一個分隔符并以字符串形式輸出,例如`{{ value|join:"http://"}}`那么['a', 'b', 'c']輸出將是"a//b//c"
last:返回列表的最后yield值
length:返回變量的長度,也可以在if語句里面使用,例如 {% if messages|length >= 100%} ...{% endif %}
length_is:判斷一長度是否是某個值,例如`{{ vlaue|length_is:"4" }}`如果value長度是4那么就返回True
linebreaks:替換換行符,例如如果value的值是Joel\nis a slug,那么輸出就是<<p>Joel<br /> is a slug</p>
linenumbers:在輸出的tex前加上標號
ljust:在字符串后面加上指定長度的寬度,例如`{{ value|ljust:"10" }}`
make_list:將整型或字符串轉換為單個單個的列表元素組成一個列表
random:在列表里面隨機選取一個元素
lower:轉換為小寫
rjust:在字符串前面加上指定長度的寬度
slice:列表分片,例如`{{ some_list|slice:":2"}}`就表示前面兩個元素媽
slugify:
stringformat:
striptags:取出HTML中的tag,只去內容
time:同date
timesince:
timeuntil
title:將一個字符串轉換為title的形式,即一般的第一個字母大寫那種標題
truncatewords: "30":表示只顯示前面30個字符
truncatechars_html:
truncatewords:顯示前面多少個字符,單位是詞,而不是字符
truncatewords_html:
unordered_list:
upper:轉換為大寫
urlencode:將url進行編碼,例如"http://www.example.org/foo?a=b&c=d"被編碼為“http\%3A//www.example.org/foo\%3Fa\%3Db\%26c\%3Dd”
urlize:
urlizetrunc:
wordcount:統計字符串中單詞的數量
wordwrap:指定特定的長度來分隔字符串
yesno:
其他標簽
# autoescape標簽
{% autoescape on %} # 去掉自動轉義
{{ body }}
{% endautoescape %}
# cycle標簽:每次使用該標簽,標簽中的值就會變化,比如下面這個,第一次該值為row1,第二次則為row2,第三次又變為了1,感覺可以用于循環里面的奇偶什么的
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row'2 %}
...
</tr>
{% endfor %}
# now 標簽,直接將當前時間按指定格式輸出:
{% now "jS F Y H:i" %}
# spaceless標簽:移出HTML tags之間的空白
{% spaceless %}
<p>
...
</p>
{% endspaceless %}
# verbatim:停止模版引擎,一般用于在模板里面寫Javascript什么的
{% verbatim %}
...
{% endverbatim %}
# with標簽:和語法里面的with類似
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}
用戶認證系統
Django項目默認添加了用戶認證系統的,可以通過
python manage.py makemigrations
python manage.py migrate
將認證系統的數據表添加到數據庫中去.
創建用戶
from django.contrib.auth.models import User
user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
user.save()
創建超級用戶
$ python manage.py createsuperuser --email=896499825@qq.com --username=wanghao
Password:
Password (again):
Superuser created successfully.
驗證用戶登錄
需要注意的是,為了不與login沖突,views最好不要寫成login
from django.contrib.auth import authenticate, login
user = authenticate(username='hao', password='test')
if user is not None:
if user.is_activate:
print('用戶驗證并登錄成功')
login(request, user) # 這才是登錄,才會寫入session
else:
print('密碼正確,但是用戶無法登錄')
return HttpResponse('居然有這個用戶')
else:
return HttpResponse('用戶不存在')
若登錄成功,則會返回一個user對象,否則返回None,但是Django默認的認證系統只能認證username和password,卻不能認證其它的字段,比如
email字段,看似非常糟糕,但是Django卻提供了十分方便的功能來擴展默認的驗證組件。查看文檔:Customizing authentication
in Django
如果要驗證用戶名或email可以這樣做:首先,在myapp目錄下新建一個文件,姑且取名叫backends.py
,其內容如下:
from django.conf import settings
from django.contrib.auth.models import User
class EmailOrUsernameModelBackend(object):
def authenticate(self, username=None, password=None):
if '@' in username:
kwargs = {'email': username}
else:
kwargs = {'username': username}
try:
user = User.objects.get(**kwargs)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None</pre>
然后在settings.py中添加如下代碼:
# 該字段指定了默認的驗證后臺,從上到下順序驗證,如果上面驗證不成功就驗證下面的
AUTHENTICATION_BACKENDS = (
'myapp.backends.EmailOrUsernameModelBackend', # 自定義的認證后臺
'django.contrib.auth.backends.ModelBackend', # 這是默認的認證后臺
)
這樣,就可以依然使用剛才的代碼對用戶登錄進行驗證了。
退出登錄
from django.contrib.auth import logout
def logout_view(request):
logout(request)
限制登錄用戶訪問路由
某些路由只能登錄用戶才能訪問,那么只需要添加這個裝飾器:
from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
......
未登錄的用戶將會重定向到settings.LOGIN_URL
去
Channels
用于與websockets通信
signal
參考django中signal與操作系統的signal是完全不一樣的.Django的signal是一種同步的消息隊列.通常在以下情況進行使用:
- signal的receiver需要同時修改對多個model時
- 將多個app的相同signal引到同一receiver中處理時
- 在某一model保存之后將cache清除時
- 無法使用其他方法, 但需要一個被調函數來處理某些問題時
- 作為網站的通知
django-crontab插件
Django下的定時任務插件,依賴于Linux的cron服務。
安裝
pip install django-crontab
進行安裝,然后在django的配置文件中添加一個APP
INSTALLED_APPS = (
'django_crontab',
...
)
使用
編寫完定時任務邏輯以后,需要在配置文件中添加上,例如
CRONJOBS = [
('*/5 * * * *', 'myapp.cron.my_scheduled_job')
('*/5 * * * *', 'myapp.cron.other_scheduled_job', ['arg1', 'arg2'], {'verbose': 0}),
('0 4 * * *', 'django.core.management.call_command', ['clearsessions']),
]
最后,將其添加到系統cron
服務中去
python manage.py crontab add # 將當前配置文件中的定時任務添加到cron中去,當add以后,會在crontab -e里面出現類似這樣的一條記錄
# */5 * * * * /usr/local/bin/python /usr/src/app/manage.py crontab run e0418752956c4dd997212171486888ff # django-cronjobs for admin
# 前面是命令部分,后面是根據定義的函數計算出來的hash值,最后面則是自帶的注釋
python manage.py crontab show # 列出當前已經添加到cron中的定時任務
python manage.py crontab remove # 移除所有的定時任務