// 原生ajax,XMLHttpRequest
如下兩函數功能同樣。
function Ajax1()
{
$.ajax({
url:'index.html',
type:'GET',
data:{'p':123},
success:function(arg){
console.log(arg);
}
})
}
function Ajax2()
{
var xhr = new XMLHttpRequest();
xhr.onreadystatechange=function(){
if(xhr.readyState==4){
//狀態‘4’表示將服務器的返回的數據所有接收完畢
console.log(xhr.responseText); //xhr.responseText表示服務器端返回的數據
}
}
xhr.open('GET','/index.html?p=123'). //GET請求只能將data數據寫在url中
xhr.send(null)
}
而POST請求而只要改動如下
xhr.open('POST','/index.html')
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
xhr.send('p=123')
//send中字符串參數是post請求,並且必須帶請求頭,不然django不會解析,即request.POST會無數據,
//可是request.body中能夠查看到數據確實發過去了。jquery的ajax已經幫咱們封裝好請求頭了。而且可能有其餘框架會指定要特有的請求頭。
//注意,若前端使用Formdata封裝數據(由於不須要頭,jquery的ajax中也要作特殊處理),則不用請求頭也可
HTTP請求頭
首先理解一下HTTP協議
1.創建在tcp協議之上,屬於應用層的協議
2.我把它理解爲一個規範的傳輸數據格式,即請求和響應(請求頭\r\n\r\n請求體、響應頭\r\n\r\n響應體)
3.無狀態,短連接(一次請求一次響應,而後斷開連接)
經常使用請求頭php
content-type :服務器端給客戶端發送的數據類型
accept :服務器端告訴客戶端要接收數據類型
User-agent
COOKIE
django的request.POST裏面有值要求有
1.請求頭要求:
若是請求頭中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值,django會去request.body中解析數據
2.數據格式要求:
即便請求頭中的 Content-Type爲 application/x-www-form-urlencoded。request.POST也不必定有值,還要request.body要知足name=alex&age=18&gender=男 這種格式
form表單提交默認的 Content-Type爲 application/x-www-form-urlencoded
ajax能夠序列化和定製請求頭css
// 默認:(和form表單同樣的頭)
$.ajax({
url:...
type:POST,
data:{name:alex,age=18} # 內部轉化 name=alex&age=18&gender=男
})
// 狀況二:(定製了請求頭,Content-Type 再也不是 application/x-www-form-urlencoded)
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:{name:alex,age=18} # 內部轉化 name=alex&age=18&gender=男
})
# body有值;POST無
// 狀況三:(序列化,數據格式不知足,並且頭也不知足)
$.ajax({
url:...
type:POST,
headers:{'Content-Type':"application/json"}
data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...}
})
// body有值;POST無
// json.loads(request.body)
ajax上傳文件
使用ajax文件上傳,ajax配置必須帶如下參數
1.processData:false
2.contentType:false
3.data必須是formData()html
// #avatar是文件上傳按鈕
$('#avatar').click(function () {
if (!$('#avatar-img').val()) {
// $('#avatar-img').val() 是文件路徑名
// $('#avatar-img')[0].files[0] 是文件對象,後臺從request.FILES裏取出就是它
}
var formData = new FormData();
formData.append('csrfmiddlewaretoken', $('[name = "csrfmiddlewaretoken"]').val());
formData.append('img', $('#avatar-img')[0].files[0]);
$.ajax({
url: '/account/upload_avatar/',
type: 'post',
processData: false,
contentType: false,
data: formData,
dataType: 'json',
success: function (data) {
console.log(typeof(data), data);
}
})
});
jsonp跨域
同源策略是檢測網頁js腳本是否是向同一個域的url發送請求,ajax恰好受到同源策略限制,而script標籤則沒有。
而script標籤的工做原理是向src指向的url發送GET請求,而後將請求到的數據包裹在標籤裏面。
因此咱們能夠建立一個標籤<script src=‘http://xxx.com/fuck'> <script>
例如:
<script> egon </script>
#return HttpResponse('egon')
可是,因爲數據是直接被包裹在標籤裏面,至關於聲明一個變量名,而js不支持因此報錯
因此常見的作法是,本地聲明一個回調函數,jsonp返回該函數包裹的跨域數據字符串。
例如:
HttpResponse("func({'a':1,'b':2})")
更深一層,咱們能夠經過傳遞GET參數告訴服務器毀掉函數,實現動態的返回指定包裹函數
例如:127.0.0.1:9999?callback=myfunc前端
jquery版小技巧:不寫JsonpCallback,這樣作jquery會隨機生成一串字符串做爲callback函數名,這樣jquery會直接將返回的數據取出做爲success函數的參數
視圖:
HttpResponse("%s('%s')"%(callback,data))
java
URL
設計
多個url對應一個視圖
利用是‘子類’在前,‘父類’在後,多餘的參數使用**kwargs接收node
url(r'^(?P<type>type_[1-3])', views.index),
url(r'^$', views.index),
在URL傳遞搜索條件
通常是以GET參數傳遞,這個瞭解一下也好python
# 該正則表示按標籤或者日期或者種類過濾
url(r'^(?P<site>\w+)/(?P<condition>((tag)|(date)|(category))/(?P<val>\w+).html$',home.filter)
分發
url匹配有兩種,一種是路徑對視圖,另外一種則是分發
- url分發的格式mysql
url('^yuan',([],None,None))
正則對應再也不是一個視圖函數,而是一個元組。
元組的第一個元素是列表,裏面有多個url,通常url對象裏面是路徑對視圖,可是你若喜歡能夠再嵌套分發
第二元素爲App名,第三元素爲名字空間
jquery
url參數編碼
將空格等特殊字符編碼,經常使用於處理GET傳參的特別參數
from django.utils.http import urlquote
def parse_filter_kwargs(kwargs):
# kwargs: {'title':1,'price':12000}
# return: title=1&price=12000
l = []
for k, v in kwargs.items():
l.append('%s=%s' % (k, urlquote(v)))
return '&'.join(l)
反向生成url
視圖
- CBV裝飾器使用
要在CBV視圖中使用咱們上面的check_login裝飾器,有如下三種方式:
1. 加在CBV視圖的get或post方法上
from django.utils.decorators import method_decorator
class HomeView(View):
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
@method_decorator(check_login)
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
2. 加在dispatch方法上,由於CBV中首先執行的就是dispatch方法,因此這麼寫至關於給get和post方法都加上了登陸校驗。
from django.utils.decorators import method_decorator
class HomeView(View):
@method_decorator(check_login)
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
3. 直接加在視圖類上,但method_decorator必須傳 name 關鍵字參數,若是get方法和post方法都須要登陸校驗的話就寫兩個裝飾器。
from django.utils.decorators import method_decorator
@method_decorator(check_login, name="get")
@method_decorator(check_login, name="post")
class HomeView(View):
def dispatch(self, request, *args, **kwargs):
return super(HomeView, self).dispatch(request, *args, **kwargs)
def get(self, request):
return render(request, "home.html")
def post(self, request):
print("Home View POST method...")
return redirect("/index/")
cookie與session
詳細
# cookie
request.COOKIES.get('username111')
res = render(request,'index.html')
res.set_cookie('key',"value",max_age=10) # 設置cookie, N秒後失效
#session
x = request.session['xx']
request.session['xx'] = 1
request.session.set_expiry(value)
# * 若是value是個整數,session會在些秒數後失效。
# * 若是value是個datatime或timedelta,session就會在這個時間後失效。
# * 若是value是0,用戶關閉瀏覽器session就會失效。
# * 若是value是None,session會依賴全局session失效策略。
自定義404頁面
1.全局urls.py上
# 指明函數路徑
handler404 = 'users.views.page_not_found'
2.編寫函數
def page_not_found(request):
from django.shortcuts import render_to_response
response = render_to_response('404.html',{})
response.status_code = 404
return response
3.設置settings.py的DEBUG=false,ALLOW_HOSTS=['*']
4.注意:DEBUG=false以後,django的static靜態文件處理會實現。因此,要跟處理media同樣,本身編寫url路由
url(r'^static/(?P<path>.*)$,serve,{"document_root":STATIC_ROOT})
ORM
詳細
# django setting裏面的database的初始化命令
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'imoocc',
'USER': 'imoocc',
'PASSWORD': 'imoocccom',
'HOST': '127.0.0.1',
'PORT': '',
'OPTIONS': {},
'init_command': 'SET storage_engine=INNODB,'
'SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED, autocommit=1, names "utf8";',
}
}
字段
注意:定義數據庫字段時,當心那些衝突的關鍵字,例如date, datetime這些就不要取了
- obj.get_xxx_display能夠顯示將原本數據庫存儲的數字按照choice的定義轉換爲相應字符串
status = models.IntegerField(choices=[(-1, '未處理'), (0, '待處理'), (1, '已處理')])
# obj.get_status_choice
- default參數能夠傳入一個無參函數,當對象建立時便會調用
send_time = models.DateTimeFIeld(default=datetime.now)
-一對一
和ForeignKey相似,可是多了一個不能重複的約束
OnetoOne經常使用於分表
即當一張表的某一些字段查詢的比較頻繁,另一些字段查詢的不是特別頻繁,把不怎麼經常使用的字段 單獨拿出來作成一張表 而後用過一對一關聯起來,
好處是既保證數據都完整的保存下來,又能保證大部分的檢索更快
- 多對多
注意:ManytoMany 一般設置在正向查詢多的那邊
兩種方式混合定義多對多
# 若是有額外的字段的時候
tags=models.ManyToManyField(
to='Tag',
through = 'Article2Tag',
through_fields = ('article','tag'),
)
注意:使用此種方式建立多對多表的時候,沒有 add() remove() 等方法
即建立時候就須要直接使用第三張表
models.Author2Book.objects.get(author_id=1,book_id=1).delete()
- 文件字段
文件字段都設置了upload_to參數,upload_to指向的字符串,若存在'/',則表示文件夾,若文件夾不存在則會自動建立
例如:upload_to = 'head'表示放在settings配置uploads文件下的head文件夾裏面
也能夠指向一個函數,該函數返回一個動態的文件夾路徑
# 例如:爲每一個用戶建立一個指定的文件夾
def upload_to(instance, filename):
import uuid
filename = '%s-%s' % (str(uuid.uuid4())[:8], filename)
return '/'.join(['head', instance.username, filename])
這個參數可讓django內部幫助咱們處理文件上傳,只要將數據庫的文件字段指向文件對象便可
UserInfo.objects.create(img=request.FILES)
# 或者:
username = form.cleaned_data['username']
headImg = form.cleaned_data['headImg']
#寫入數據庫
user = User()
user.username = username
user.headImg = headImg
user.save()
查詢
QuerySet的查詢時惰性的,即當咱們去執行all,filter,get等時是不會去執行sql的,只有當咱們調用查詢結果集的時候纔會真正執行sql
管理器自帶api
- get()
返回model對象,若沒有符合條件的對象,觸發DoesNotExist異常
try:
aricle = Article.objects.get(pk=id)
except Article.DoesNotExist:
return render(request,'failure.html',{'reason':'沒有找到對應的文章'})
- 跨表
‘__’能夠在filter和values或者相關函數中使用,而且是雙向的,只要存在關係便可
# 注:兩種用法,能夠作條件,也能夠取值
BookInfo.object.filter(heroinfo__hgender__exact = False)
#取值
v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
- values與values_list
values 能夠提取反向的關聯對象
注意:反向查詢的參數是小寫的orm類名,能夠看做是關聯表id另外一種表示法
a = CategoryInfo.objects.values('articleinfo')
# a = CategoryInfo.objects.filter(articleinfo=1) #能夠看做articleinfo__id
values 能夠提取外鍵字段
注意:經過雙下劃線連接
ArticleInfo.objects.values('category__title') #即便是manytomany也行
- 複雜查詢extra()函數
extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
select和select_params是一組,用於子查詢
where和params是一組,用於附加過濾條件
tables表示要引入哪些新表(會有笛卡爾積),order_by表示數據條目按本表的哪一個字段排序
# 附加where條件
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
# 先filter篩選出指定博客掉文章,再按指定年月過濾
article_list = modles.Article.objects.filter(blog=blog).extra(where=['strftime("%%Y-%%m",create_time)=%s'],params=[val,])
# 自定義一個「子查詢」字段
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
# 查詢語句對比:
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
"""
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
"""
- 統計函數 aggregate
沒有groupby效果,而且查出來以後就不死queryset對象了,是一個字典。適用於沒有groupby的統計
TableInfo.objects.aggregate(c=Count('category_id'))
- 統計聚合函數annotate
注意:annotate前面必須有個的value來指定group by後面的字段,默認是以本身的id分組
from django.db.models import Sum, Count
Comment.objects.values(‘article’).annotate(comment_count=Count(‘article’)).order_by(‘-comment_count)。
# select count(article) as c from Evainfo group by aricle ordey by c
# annotate前面的value指定group by後面的字段,默認是以本身的id分組
# 注:comment_count是自定義查詢字典的key
annotate統計操做默認以發起表的id進行group by,配合反向查詢很是好用
# 查詢不一樣種類對應的書籍數目
CategoryInfo.objects.filter(blog_id=1).values(c=Count('articleinfo')).values('title', 'c')
# 按日期歸檔
archive_list = models.Article.objects.filter(user=user).extra(
select={"archive_ym": "date_format(create_time,'%%Y-%%m')"}).values("archive_ym").annotate(c=Count("nid")).values("archive_ym", "c")
# 標籤,種類歸檔
category_list = models.Category.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
tag_list = models.Tag.objects.filter(blog=blog).annotate(c=Count("article")).values("title", "c")
- 其餘不經常使用
# like查詢
filter(sname__contains='黃') #也適用於整型等其餘類型,而且要配合比較運算符使用 等同於select * from table where sname like "黃%"
#字段排序
order_by('-price') #支持多字段排序 order_by('id','-price','title')
#判斷查詢集中是否有數據,返回bool值
exists()
#返回查詢集的總條數
count()
# 去重
distinct()
# 排除指定條件,經常使用做不等於
exclude()
obj = Permission2Action.objects.filter().exclude(p__menu_isnull =True)
#根據字段排序,支持多字段排序
order_by('id','-price','title')
# 附:查看執行的sql原生語句
#obj.query
django提供api(get_object_or_404 與 get_list_or_404)
通常都會有這麼一個需求,若查找不出指定對象,返回404頁面
from django.http import Http404
try:
product = Product.objects.get(pk=1)
except MyModel.DoesNotExist:
raise Http404
get_object_or_404 會簡化這個操做
from django.shortcuts import get_object_or_404
product = get_object_or_404(Product, pk=1)
原生查詢
- Manger.extra()
經常使用於附加複雜條件,子查詢
# 查詢語句對比:
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
"""
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
"""
- Manager.raw()
注意,raw查詢,查詢結果必定要包含主鍵
Topic.objects.raw('select * form forum_topic')
# 異常,由於返回的結果沒有包含主鍵
blog_article.objects.raw(‘select distinct date_format(r‘blog_article’.’date_publist’,’%Y-%m’) from blog_article’)
- DB-API
直接使用了MySQLbd的api,使用原生語句查詢 )
from django.db import connection, transaction
cursor = connection.cursor()
# cursor = connections['default'].cursor()
# 數據修改操做——提交要求
cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
transaction.commit_unless_managed()
# 數據檢索操做,不須要提交
cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
row = cursor.fetchone()
連表查詢
注意的是:selected_related不支持ManytoMany,而prefetch_related支持全部關係
![](http://static.javashuo.com/static/loading.gif)
用於查詢的對象
Q對象
from django.db.models import Q
Q(Q(account='fuck_man') & Q(status=0) | Q(birthday_lte=ctime))
# 多個Q對象組裝複雜條件
q = Q()
q1 = Q()
q1.connector = 'AND'
q1.children.append('account', 'fuck_man')
q1.children.append('status', 0)
q1.children.append('birthday_lte', ctime)
q2 = Q()
q2.connector = 'AND'
q2.children.append('title', 'fuck')
q.add(q1, 'OR')
q.add(q2, 'OR')
- filter 的 != 條件應該使用Q查詢
from django.db.models.query import Q
queryset = UserInfo.objects.filter(~Q(name=""))
- 條件的「或」操做- 動態實現Q條件
好比簡單的搜索功能(搜索一個文章的標題或內容或做者名稱包含某個關鍵字):
import operator
from django.db.models.query import Q
# 用戶要查詢的值
q = request.GET.get('q', '').strip()
# 可能的查詢鍵值列表
key_list = ['title__contains', 'content__contains','author__name__contains']
# 實現Q(title__contains=q)|Q(content__contains=q)|Q(author__name__contains=q)
l = [Q(**{key:q}) for key in key_list]
#reduce至關於c++的accumulate,累加函數, operator.or_(x,y)是二進制的'或'操做, 從列表裏將全部Q對象一塊兒'或'起來,獲得最終的結果
queryset = Entry.objects.filter(reduce(operator.or_, l))
F對象
- 字段間比較
例如:查詢書id小於價格的書籍
models.Book.objects.filter(id__lt=F("price"))
- django 支持 F() 對象之間以及 F() 對象和常數之間的加減乘除和取模的操做
models.Book.objects.filter(id__lt=F("price")/2)
- 將對象的一個字段通過修改(加減乘除),賦值給另外一個字段,以此進行更新
models.Book.objects.all().update(price=F("price")+30)
序列化
一、serializers
from django.core import serializers
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)
二、json.dumps
import json
#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
ret=list(ret)
result = json.dumps(ret)
因爲json.dumps時沒法處理datetime日期,因此能夠經過自定義處理器來作擴展,如:
import json
from datetime import date
from datetime import datetime
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, date):
return o.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, field)
# ds = json.dumps(d, cls=JsonCustomEncoder)
自定義Manager
class MyCustomManager(models.Manager):
# 鏈接查詢時也生效
use_for_related_fields = True
def get_queryset(self):
return super(MyCustomManager, self).get_queryset().filter(isDelete=False)
# 配置 use_for_related_fields = True,必定要寫在外面。不知道是否是bug,必定要再類的外面的實例化
custom_manager = MyCustomManager()
class BaseModel(models.Model):
create_time = models.DateTimeField(verbose_name='建立時間', auto_now=True)
isDelete = models.BooleanField(verbose_name='是否已經刪除', default=False)
default_objects = models.Manager() # objects = BaseManger會被覆蓋
objects = custom_manager
class Meta:
abstract = True
def delete(self, using=None, keep_parents=False):
"""重寫數據庫刪除方法實現邏輯刪除"""
self.isDelete = True
self.save()
print('isDelete = false')
模型繼承
詳細
數據庫事務
詳細
資源共享問題
數據資源共享問題,須要藉助返回值(受影響的行數)
row = Trouble.objects.filter(id=nid,status=1).update(**form.cleaned_data)
if not row:
return HttpResponse('來晚一步,被人搶走了')
# 須要考慮多個用戶搶同一個資源的問題,update返回的是數字,若返回是0,表示單子被搶走了。
else:
return redirect('/backend/trouble-list.html')
分庫
分庫的方法
- 手動路由
# 查詢,使用using函數,參數就是要查詢的數據庫
User.objects.using('user1').all()
# 保存或者更新,使用save的using參數,值就是要使用的數據庫
my_object.save(using='user1')
#刪除,使用delete的using參數
user_obj.delete(using='user1')
- 自動路由
詳細
分庫的思路
- 垂直分庫
即一個app對應一個數據庫,上面自動路由的例子就是一個垂直分庫的例子,auth1使用user1數據庫,auth2使用user2數據庫。固然也可使用手動路由。
- 水平分庫
水平分庫建議使用手動路由,由於每一個model的分庫機制可能都不同,自動路由實現起來有些麻煩會形成性能不高,而手動路由,每一個model根據本身的規則來得到不一樣的數據庫。
模板
- 注意點
1. 函數在模版裏面不能加括號
2. 模版裏面統計反向集合數據條數 要用{{ article.commentinfo_set.count }} #{{article.commentinfo_set|length}}不行
3. 默認request對象已經傳入模版裏面,例如:
{% if request.session.user|default:'' != '' %}
4.
在模版語言中,變量類型不會由於輸出語句,例如{{name}},而改變類型,好比說,int類型仍是int類型,不回變爲字符串
-
模板html文件加載順序
Django會依次到如下目錄查找模板文件,若是都找不到,則報錯:
1.項目配置的模板目錄
2.admin應用的templates模板目錄
3.auth應用的templates模板目錄
4. 應用自己的templates模板目錄
內置過濾器
# 注意的是,模版的傳入的參數都是爲字符串,而且傳參時都要帶‘:’
{{courses | length}} #集合的長度
{{ time_obj|date:"Y-m-d H:i:s"}} # 對傳入轉爲日期時間格式
{{ str_obj|truncatewords:"30" }} # 截取前三十個字符,沒法截取中文
{{str_obj| slice:"30"}} #截取中文
{{ my_list|first|upper }} # 把第一個字符大寫
{{ article.summary |default:'' }} #這個能夠保障模版不輸出None,由於從數據庫取出的值若沒有則爲None
{{ name|lower }} # 把傳入值小寫
{{ article.date_publish | date:"Y-m-d" }}
{{html_str|safe}} #讓html_str給瀏覽器解析,默認出於安全考慮不解析
{% autoescape off %} #批量轉義
{{ course.detail }}
{% endautoescape %}
注:還有另外一種方式可讓瀏覽器解析,
from django.utils.safestring import mark_safe
html_str=mark_safe(html_str)
- 循環計數
{% for row in v1 %} # 假設3條數據
{{ forloop.counter}} # 依次爲:一、二、3
{{ forloop.counter0}} # 依次爲:0、一、2
{{ forloop.revcounter}} # 依次爲:三、二、1
{{ forloop.revcounter0}} # 依次爲:二、一、0
{{ forloop.last }} # 是否最後一個,是爲True,其餘False
{{ forloop.first }} # 是否第一個
{{ forloop.parentloop }} # 嵌套循環,表示上層循環的上面的六個值別是多少。
{% endfor %}
自定義過濾器
from django import template
register = template.Library()
# 定義一個將日期中月份轉換爲大寫的過濾器,如8轉換爲八
@register.filter
def month_to_upper(key):
return ['一','二','三','四','五','六','七','八','九','十','十一','十二'][key]
# 使用,模版文件要load一下才能使用,filter是filter.py文件的文件名
{%load filter%}
<div class='month'>{{article.date_publish | month_to_upper}}</div> # article.date_publish會被當作month_to_upper的參數
{{ cls_verbose_name |test:app_name }} # 若是過濾器有參數時,必定要肯定沒有空格,即過濾器test後面必定要緊貼':'
# 配置
注意的是:必定要在指定App目錄下創建templatetags目錄,而且要在setting.py設置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
'libraries': {
'my_tags': 'web.templatetags.filter', #my_tags爲自定義鍵值, web.templatyetags.filter爲路徑 }
},
},
自定義simple_tag
過濾器最多隻能帶1個額外參數只能帶1個額外參數,而自定義simple_tag能夠帶無數個
# 能夠接收多個參數
@register.simple_tag
def create_filter_str(order, q, filter_dict, field, value=''):
d = copy.copy(filter_dict)
remove_previous_field(d, field)
d[field] = value
order_str = 'order=%s' % order
q_str = 'q=%s' % q
filter_str = parse_filter_kwargs(d)
return '?' + '&'.join([order_str, q_str, filter_str])
#調用
<a href="{% create_filter_str args.order args.q args.filter data_dict.field %}">所有</a>
#或者{% create_filter_str args.order args.q args.filter data_dict.field as filter_str%}
{{filter_str}}
頁面分離與導入
傳入參數通過計算渲染裝飾器所指定的頁面,這個頁面能夠被其餘模板經過調用引入
@register.inclusion_tag('web/sub/common_bar.html')
def get_common_bar(blog):
tag_statistics = TagInfo.objects.filter(blog=blog). \
annotate(article_count=Count('articleinfo')).values('id', 'article_count', 'title')
category_statistics = CategoryInfo.objects.filter(blog=blog). \
annotate(article_count=Count('articleinfo')).values('id', 'article_count', 'title')
date_statistics = ArticleInfo.objects.filter(blog_id=1). \
extra(select={'create_date': 'date_format(time,"%%Y-%%m")'}). \
values('create_date').annotate(article_count=Count('id')).values('article_count', 'create_date')
# 粉絲數
fans_count = Star2Fans.objects.filter(star_user=blog.user).count()
# 關注數
star_count = Star2Fans.objects.filter(fans_user=blog.user).count()
# 模版只會根據這個函數返回的上下文進行渲染
return locals()
- 使用
{% get_common_bar blog %}
settings.py配置
多個setting文件
詳細
靜態文件與上傳文件配置
# 原理就是url拼配成功後,截取剩下的路徑去指定目錄下查找
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
MEDIA_URL = '/uploads/'
MEDIA_ROOT = os.path.join(BASE_DIR,'uploads')
# 注意:upload_to 設置的路徑是相對於MEDIA_ROOT下建立
# 模板中引用靜態文件
<link href ="{% static 'css/base.css' %}" rel="stylesheet"}
#配置能訪問upload的文件
from django.views.static import serve
from django.conf import settings
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^uploads/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
]
#配置在模版中使用{{MEDIA_URL}} 表明 ‘/uploads/'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.template.context_processors.media',
],
日誌配置
BASE_LOG_DIR = os.path.join(BASE_DIR, "log")
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
'[%(levelname)s][%(message)s]'
},
'simple': {
'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
},
'collect': {
'format': '%(message)s'
}
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'], # 只有在Django debug爲True時纔在屏幕打印日誌
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'SF': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,根據文件大小自動切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日誌文件
'maxBytes': 1024 * 1024 * 50, # 日誌大小 50M
'backupCount': 3, # 備份數爲3 xx.log --> xx.log.1 --> xx.log.2 --> xx.log.3
'formatter': 'standard',
'encoding': 'utf-8',
},
'TF': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler', # 保存到文件,根據時間自動切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日誌文件
'backupCount': 3, # 備份數爲3 xx.log --> xx.log.2018-08-23_00-00-00 --> xx.log.2018-08-24_00-00-00 --> ...
'when': 'D', # 天天一切, 可選值有S/秒 M/分 H/小時 D/天 W0-W6/周(0=週一) midnight/若是沒指定時間就默認在午夜
'formatter': 'standard',
'encoding': 'utf-8',
},
'error': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切
'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"), # 日誌文件
'maxBytes': 1024 * 1024 * 5, # 日誌大小 50M
'backupCount': 5,
'formatter': 'standard',
'encoding': 'utf-8',
},
'collect': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切
'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
'maxBytes': 1024 * 1024 * 50, # 日誌大小 50M
'backupCount': 5,
'formatter': 'collect',
'encoding': "utf-8"
}
},
'loggers': {
'': { # 默認的logger應用以下配置
'handlers': ['SF', 'console', 'error'], # 上線以後能夠把'console'移除
'level': 'DEBUG',
'propagate': True,
},
'collect': { # 名爲 'collect'的logger還單獨處理
'handlers': ['console', 'collect'],
'level': 'INFO',
}
},
}
# 用戶使用logging.getLogger([name])獲取logger實例,若是沒有名字,返回logger層級中的根logger(root logger)
# django名字的logger多是默認終端的輸出,未驗證
logger = logging.getLogger(‘django’)
logger.info(「This is an error msg」)
sql查詢語句顯示配置
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
django內置組件
詳細
字段
# 經常使用字段
# CharField
# IntegerField
# DeimalField
# DateField
# DateTimeField
# EmailField
# ChoiceField
# FileField
# RegexField
# GenericIPAddressField
from django import forms
from django.forms import fields
#forms的單選框
auto = forms.CharField(
label='一個月內自動登陸',
widget=widgets.CheckboxInput(attrs={'id': "auto", 'name': 'auto'}),
required=False,
)
例如:手機號碼定義
phone = fields.CharField(
min_length=11,
max_length=12,
error_messages={'required': 'server:手機號碼不能爲空',
'min_length': 'server:號碼必須爲11位或者12位',
'max_length': 'server:號碼必須爲11位或者12位',
},
validators=[RegexValidator(r'^1[0-9]{10,11}$', '手機號碼格式錯誤'), ],
label='電話號碼',
widget=widgets.TextInput(attrs={'name': 'phone', 'placeholder': "請輸入手機號碼", \
'class': 'form-control', 'id': 'phone'}),
)
例如:根據model的choices字段生產選項組框
city = forms.ChoiceField(
choices =models.CityInfo.objects.all().values_list('id','name'),
# choices = [(1,"東莞"),(2,"廣州"),(3,"深圳")],
label = "城市",
initial = 1,
widget = forms.widgets.Select
)
#注意:以上的作法,當數據庫更新時,不會實時更新。這時候須要自定義form的構造方法
class MyForm(Form):
user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].choices = ((1, '上海'), (2, '北京'),)
self.fields['user'].choices = models.Classes.objects.all().values_list('id','caption')
驗證順序
先驗證了min_length那些參數字段以後再執行clean_xx鉤子函數,最後執行總體驗證clean()
- 不管以前定義的min_length那些驗證成功與否,都會執行clean_xx函數,因此在裏面操做時應當使用request.POST,而不是self.clean_data。(由於當驗證失敗時會取不到值)
- 若是驗證成功須要return self.clean_data['xx']回去,否則clean_data裏面那操做過的那個字段會是None
#單一字段的驗證,clean_xxx()
def clean_verify(self):
user_verify_code = self.request.POST.get('verify', '').upper()
verify_code = self.request.session.get('verify_code').upper()
if user_verify_code != verify_code:
raise ValidationError(message='驗證碼錯誤', code='invalid')
# 總體驗證例子
def clean(self):
value_dict = self.clean_data
v1 = value_dict.get('username')
v2 = value_dict.get('user_id')
if v1!='root' and v2 !=1:
raise ValidationError('總體錯誤信息')
return self.cleaned_data
後續增長錯誤信息
# form.errors['username'] = ['用戶名不存在或者密碼錯誤', ]
form.add_error(field, error)
只讀字段處理
#form.fields['title'].widget.attr['readonly']=True
#form.fields['title'].widget.attr.update(['disabled':'true'})
form.fields['title'].disabled = True
使用接口
form = UserForm(request.POST,request.FILE)
if form.is_valid():
form.cleaned_data #驗證過的數據,字典形式,鍵是name屬性
else:
# 錯誤數據,字典形式,鍵是name屬性,不過它的__str__默認返回errors.as_ul(),也就是字符串。html的ul代碼
form.errors
注意:使用字典參數做爲自定義form的data參數,傳入的的值會被本身定義的規則所驗證。若不想驗證,則傳入參數爲initial
form = TroubleMaker(initial={'title':'標題1','detail':'XXXX'})
模板中使用
errors內部格式
{'username:['錯誤信息1','錯誤信息2'],'password':['錯誤信息1,]}
取錯誤列表裏面的第一個
{{ form.errors.username.0}}
在模板中顯示Django’__all__’形式的錯誤,即總體錯誤信息
{{ form.non_field_errors }}
驗證器
Django將驗證器定義爲一個可調用的對象,它接受一個值,並在不符合一些規則時拋出ValidationError
異常。
換句話說,知足以上定義的都是驗證器,那咱們就能夠歡快地來自定義驗證器吧!
例如,這個驗證器只容許偶數:
from django.core.exceptions import ValidationError
def validate_even(value):
if value % 2 != 0:
raise ValidationError('%s is not an even number' % value)
你能夠經過字段的validators
參數將它添加到模型字段中:
from django.db import models
class MyModel(models.Model):
even_field = models.IntegerField(validators=[validate_even])
因爲值在驗證器運行以前會轉化爲Python,你能夠在表單上使用相同的驗證器:
from django import forms
class MyForm(forms.Form):
even_field = forms.IntegerField(validators=[validate_even])
你也可使用帶有 __call__()
方法的類,來實現更復雜或可配置的驗證器。例如,RegexValidator
就用了這種技巧。
RegexValidator
class RegexValidator([regex=None, message=None, code=None, _inversematch=None, flags=0])[source]
regex
用於搜索提供的value的正則表達式,或者是預編譯的正則表達式對象。一般在找不到匹配時拋出帶有 message 和code的 ValidationError異常。這一標準行爲能夠經過設置inverse_match 爲True來反轉,這種狀況下,若是找到匹配則拋出 ValidationError異常。一般它會匹配任何字符串(包括空字符串)。
message
驗證失敗時ValidationError所使用的錯誤信息。默認爲"Enter a valid value"。
code
驗證失敗時ValidationError所使用的錯誤代碼。默認爲"invalid"。
inverse_match
New in Django 1.7.regex的匹配模式。默認爲False。
flags
New in Django 1.7.編譯正則表達式字符串regex時所用的標識。若是regex是預編譯的正則表達式,而且覆寫了flags,會產生TypeError異常。默認爲 0
modelform
class UserCreationForm(forms.ModelForm):
"""A form for creating new users. Includes all the required
fields, plus a repeated password."""
password1 = forms.CharField(label='密碼', widget=forms.PasswordInput(attrs={'class': 'form-control'}), \
max_length=32, min_length=8)
password2 = forms.CharField(label='重複密碼', widget=forms.PasswordInput(attrs={'class': 'form-control'}))
class Meta:
model = UserInfo
fields = ('email', 'username')
labels = {
'username': '用戶名',
'email': '郵箱',
}
error_messages = {
'__all__': {},
'email': {
'required': '郵箱不能爲空',
'invalid': '郵箱格式錯誤',
},
'username': {
'required': '用戶名不能爲空',
'invalid': '用戶名格式錯誤..',
}
}
widgets = {
'username': widgets.TextInput(attrs={'class': 'form-control'}),
'email': widgets.TextInput(attrs={'class': 'form-control'})
}
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get("password1")
password2 = self.cleaned_data.get("password2")
if password1 and password2 and password1 != password2:
raise forms.ValidationError("兩次密碼輸入不一致")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
視圖中使用
# instance參數表示和data參數共同融合
form = form_cls(instance=obj, data=request.POST)
if form.is_valid():
obj = form.save() #存儲對象,返回model對象
模板中使用
{%for field in form %}
{{ field.label }} # verbose_name
{{ field.name}} # modelinfo原始定義的字段名,例如price
{{ field }} # input組件
{{ field.errors.0 }} #錯誤信息
{%endfor%}
動態modelform
class FormClassFactory(object):
@classmethod
def from_admin(cls, admin):
class Meta:
model = admin.cls_info
fields = '__all__'
exclude = admin.readonly_fields
def __new__(cls, *args, **kwargs):
for field_name in cls.base_fields:
field_obj = cls.base_fields[field_name]
field_obj.widget.attrs.update({'class': 'form-control'}) # 傳入組件屬性
return ModelForm.__new__(cls)
form_cls = type('DynamicModelForm', (ModelForm,), {'Meta': Meta, '__new__': __new__})
return form_cls
緩存
from django.core.cache import cache
is_had_read = cache.get(article_id)
if not is_had_read:
article.read_num += 1
article.save()
cache.set(article_id, 1, 60 * 60 * 24 * 30) #一個月過時
中間件
詳細
-中間件執行流程。
a. - 請求到來走wsgi,wsgi負責按照HTTP協議的規則解析請求成請求頭
b. 先執行全部安裝中間件的process_request
c. - 繞回去匹配url
d. 執行全部中間件的process_view #這裏和process_request的區別是,這裏已經找到了要執行的視圖函數
e. -執行視圖函數返回
f. 執行全部中間件的process_response
g. 若報錯,執行全部的process_excepiton
h. 若用render返回,則執行全部的process_render方法
異常