Django 開發相關知識 整理

 

前言

旨在記錄Django web經常使用的開發技術,幫助開發者更加快速開發javascript

前端ajax

詳細
注意:post提交時,url對後綴必定要補齊'/'
$.ajax({
  url: "/cookie_ajax/",
  type: "POST",
  data: {
    "username": "Q1mi",
    "password": 123456,
    "csrfmiddlewaretoken": $("[name = 'csrfmiddlewaretoken']").val()  // 使用jQuery取出csrfmiddlewaretoken的值,拼接到data中
  },
  success: function (data) {
    console.log(data);
  }
})
// 原生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

視圖
from django.core.urlresolvers import reverse
#kwargs參數接收 一個字典,表明命名正則的名 和 對應的值
    if kwargs:
            base_url = reverse('index', kwargs=kwargs)
        else:
            base_url = '/'
html
# 默認版
url('all/(\d+).html$',home.index,name='index')
{%url "index" 1%} # 在html中
reverse('index',kwargs=(1,)) # 在view中

# 命名正則版
url('all/(?<my_id>\d+).html$',home.index,name='index')
{%url "index" my_id=1%} # 在html中:
reverse('index',kwargs={"my_id":1}) #在view中

# 命名空間的url反轉
{% url 'org:org_list'%}

視圖

request對象

POST

1. input 類型像checkbox這種多值的,要用values = request.POST.getlist('hobby',None)
2. requst.body #請求體,若要去這裏拿值時,必定是post方式進來的

url信息

視圖返回值

HttpResponse

1. input 類型像checkbox這種多值的,要用values = request.POST.getlist('hobby',None)
2. requst.body #請求體,若要去這裏拿值時,必定是post方式進來的

JsonRespone 

from django.http import JsonRespone
#注意:默認JsonRespone只能返回字典,若必定要返回列表,則須要加參數
    return JsonRespone([11,22,33],safe=False)

shortcuts

- render

1.render的context參數能夠傳遞locals()函數,將當前做用域全部的變量傳遞到模板裏面去

- redirect

詳細
1.redirect函數對於ajax提交沒用
2.redirect('/index') 和 redirect('index')的區別:前者跳轉到127.0.0.1:8000/index,後者在現有url的網址後面追加index
 例如:現訪問的是127.0.0.1:8000/backend,追加爲:127.0.0.1:8000/backend/index

返回值響應頭和狀態設置

# HttpResponse能夠帶幾個參數:

"""

content: 返回的內容
content_type: 返回內容的類型
status

"""

def index(request):
    return HttpResponse('<p>this is a test</p>', content_type="text/html", status="201")

- 響應頭設置

 res = HttpRespone('xxxxx')
 res['Access-Control-Allow-Origin']='*'
 return res

CBV

- 更好對代碼複用

例如:自定義cbv的login驗證mixin

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views import View


class LoginRequiredMixin(object):
    @method_decorator(login_required(login_url='/url/'))
    def dispatch(self, request, *args, **kwargs):
        return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)


# 注意繼承順序
class CommentView(LoginRequiredMixin, View):
    pass
 
- 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()
    
upload_to能夠支持%Y%m表示年月,換句話說,他會自動將上傳時間傳入
image = models.ImageField(upload_to="courses/%Y/%m")

 

 
- content-type
通常而言,表數量能夠動態變化,表結構不能變化。注意表結構設計時,表結構不隨着其有關係的表的數量增長而改變結構。有時候須要一個定義一個外鍵關聯不一樣的表,就顯得有點麻煩。
爲此,Django提供了ContentType,ContenType django給咱們寫好的處理一張表關聯混合類型外鍵的組件。所謂混合關聯,指的是這個外鍵字段可能關聯到不一樣的表
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType


class Course(models.Model):
    """
    普通課程
    """
    title = models.CharField(max_length=32)
    # 不生成表列,僅用於反向查找
    price_policy_list = GenericRelation("PricePolicy")


# 用於反向查找的特殊字段(一個課程能夠對應多個價格策略),因爲不能用xxx_set。
# 注意:GenericRelation("PricePolicy")要定義在一對多的‘一’處
# course = models.Course.objects.filter(id=1).first()
# price_policys = course.price_policy_list.all() # 反向查找出課程全部的價格策略


class DegreeCourse(models.Model):
    """
    學位課程
    """
    title = models.CharField(max_length=32)

    price_policy_list = GenericRelation("PricePolicy")


class PricePolicy(models.Model):
    """
    價格策略
    """
    price = models.IntegerField()
    period = models.IntegerField()

    # ContentType是django生成一張管理全部表的表,會自動將全部表的名稱存進去進行管理
    content_type = models.ForeignKey(ContentType, verbose_name='關聯的表')  #
    object_id = models.IntegerField(verbose_name='關聯的表中的數據行的ID')

    # 幫助你快速實現帶contenttype關聯字段的表對象建立工做,不會數據庫中生成列
    content_object = GenericForeignKey('content_type', 'object_id')


# 1. 爲學位課「Python全棧」添加一個價格策略:一個月 9.9
"""
obj = DegreeCourse.objects.filter(title='Python全棧').first()
# obj.id
cobj = ContentType.objects.filter(model='course').first()
# cobj.id
PricePolicy.objects.create(price='9.9',period='30',content_type_id=cobj.id,object_id=obj.id)
"""

# 增長GenericForeignKey("conten_type","object_id")以後,會自動識別obj的表的名稱,並將表在contenType中的id賦予GenericForeignKey第二個參數
# obj = DegreeCourse.objects.filter(title='Python全棧').first()
# PricePolicy.objects.create(price='9.9',period='30',content_object=obj)

查詢

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支持全部關係

用於查詢的對象

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內置組件

Form組件

詳細

字段

# 經常使用字段
# 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方法

異常

 

 

腳本運行環境配置

import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project_name.settings")  # project_name 改爲你本身的項目名稱
django.setup()

APP

自定義AppConfig

方法1
# 第一步在app的apps.py中
class AccountConfig(AppConfig):
    name = 'account'
    verbose_name = u'用戶信息'  # 更改app的在admin中的顯示 

# 第二步,在該app的__init__.py中
default_app_config = 'account.apps.AccountConfig'
方法2

直接在settings的INSTALLED_APPS中後面加上.apps.UsersConfig
例如:'users.apps.AppConfig'

- ready方法

app一被加載就自動調用的方法
 from django.apps import AppConfig
    class App01Config(AppConfig):
        name = 'app01'
    
        def ready(self):
            from django.utils.module_loading import autodiscover_modules
            # 在項目全部的目錄下查找並調用aaaa.py文件
            autodiscover_modules("aaaa")

app設計

通常咱們經過「名詞+動詞分析」進行數據庫設計,但進行app設計時,models可能會有循環導入問題。
通常解決的方法是,定義一個動做appoperation,operation能夠根據角色的動做去設計
),導入其餘名詞appuserorganizationcourse

- 整理app文件夾
即將全部app存放於一個文件夾中

1.新建包apps
2.移動app至apps,注意不要勾選search on xx這個選項,要的不是from apps.course.models import * 這種效果
3.改掉編輯器的錯誤提示,右鍵-->Mark Directory as--->Sources Root,這樣會使得編輯器查找模塊時,也會將apps做爲跟文件夾開始搜索
4.settings.py文件增長
sys.path.insert(0,os.path.join(BASE_DIR,'apps')
5.注意:當要重新migration時會報錯,由於django默認會將外鍵的路徑加上apps這個文件夾,致使錯誤。因此搜索出來刪掉(ctrl+H全局搜索)


信號

Django中提供了「信號調度」,用於在框架執行操做時解耦。通俗來說,就是一些動做發生的時候,信號容許特定的發送者去提醒一些接受者。

一、Django內置信號

    Model signals
        pre_init                    # django的modal執行其構造方法前,自動觸發
        post_init                   # django的modal執行其構造方法後,自動觸發
        pre_save                    # django的modal對象保存前,自動觸發
        post_save                   # django的modal對象保存後,自動觸發
        pre_delete                  # django的modal對象刪除前,自動觸發
        post_delete                 # django的modal對象刪除後,自動觸發
        m2m_changed                 # django的modal中使用m2m字段操做第三張表(add,remove,clear)先後,自動觸發
        class_prepared              # 程序啓動時,檢測已註冊的app中modal類,對於每個類,自動觸發
    Management signals
        pre_migrate                 # 執行migrate命令前,自動觸發
        post_migrate                # 執行migrate命令後,自動觸發
    Request/response signals
        request_started             # 請求到來前,自動觸發
        request_finished            # 請求結束後,自動觸發
        got_request_exception       # 請求異常後,自動觸發
    Test signals
        setting_changed             # 使用test測試修改配置文件時,自動觸發
        template_rendered           # 使用test測試渲染模板時,自動觸發
    Database Wrappers
        connection_created          # 建立數據庫鏈接時,自動觸發

對於Django內置的信號,僅需註冊指定信號,當程序執行相應操做時,自動觸發註冊函數:

    from django.core.signals import request_finished
    from django.core.signals import request_started
    from django.core.signals import got_request_exception

    from django.db.models.signals import class_prepared
    from django.db.models.signals import pre_init, post_init
    from django.db.models.signals import pre_save, post_save
    from django.db.models.signals import pre_delete, post_delete
    from django.db.models.signals import m2m_changed
    from django.db.models.signals import pre_migrate, post_migrate

    from django.test.signals import setting_changed
    from django.test.signals import template_rendered

    from django.db.backends.signals import connection_created


    def callback(sender, **kwargs):
        print("xxoo_callback")
        print(sender,kwargs)

    xxoo.connect(callback)
    # xxoo指上述導入的內容
View Code
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")
View Code

二、自定義信號

a. 定義信號

            import django.dispatch
            pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

b. 註冊信號

        def callback(sender, **kwargs):
            print("callback")
            print(sender,kwargs)

        pizza_done.connect(callback)

c. 觸發信號

        from 路徑 import pizza_done

        pizza_done.send(sender='seven',toppings=123, size=456)

因爲內置信號的觸發者已經集成到Django中,因此其會自動調用,而對於自定義信號則須要開發者在任意位置觸發。


時區

 

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai' 
USE_TZ = False # 防止django存儲數據庫時採用utc時間,而use_tz爲false則表示使用本地時間

- 注意
1. 若是直接用標準庫的datetime,可能會出現如下警告,由於你使用的是一個不帶時區的時間

 這個問題的隱患是,假如你的django若是用了時區,而標準庫是當前系統時區,那麼就極可能出現時間查
  解決辦法:安裝django的指定的時區來
  from django.utils.timezone import datetime

閃現

詳細

 csrf

跨站請求僞造,重點在跨站二字
釣魚網站的頁面和正經網站的頁面對瀏覽器來講有什麼區別?
- 釣魚網站的頁面是由 釣魚網站的服務端給你返回的
- 正經網站的網頁是由 正經網站的服務端給你返回的
釣魚網站的目的是:真正的目的是你要交互的合法網站所存儲的cookie,這是你的身份憑證。
手段是:引導你生成不合法的數據,或者直接藉由本身雙手提交一個已經設計好的非法表單數據。

csrf_protect,爲當前函數強制設置防跨站請求僞造功能,即使settings中沒有設置全局中間件。
csrf_exempt,取消當前函數防跨站請求僞造功能,即使settings中設置了全局中間件。

from django.views.decorators.csrf import csrf_exempt, csrf_protect

auth組件

詳細
 
自定義authenticate,實現用戶能夠經過郵箱或者用戶名登陸
    # settings.py文件中配置,指明路徑
        AUTHENTICATION_BACKENDS = (
            'users.views.CustomBackend',
        )
        
    from django.contrib.auth.backends import ModelBackend
    from django.db.models import Q
    class CustomBackend(ModelBackend):
        def authenticate(self, username=None, password=None, **kwargs):
            try:
                user = UserProfile.objects.get(Q(username=username)|Q(email=username))
                if user.check_password(password):
                    return user
            except Exception as e:
                return None

admin

詳細

郵件

# Email settings smtp/pop3
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = "smtp.163.com"  # smtp加上指定的域名
EMAIL_PORT = 25
EMAIL_HOST_USER = "jdecree@163.com"
EMAIL_HOST_PASSWORD = "xxxxx"  # 受權碼
# EMAIL_USE_TLS = True
EMAIL_FROM = "jdecree@163.com"
class EmailHelper(object):
    def __init__(self):
        self.email_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        self.email_from = settings.EMAIL_FROM  # 發送人

    def send_captcha(self, receive_email):
        """
        發送驗證碼
        """
        captcha_helper = CaptchaHelper()
        captcha = captcha_helper.create_and_save_captcha(email=receive_email)
        # 郵件發送參數
        email_title = '修仔科技'
        email_body = '%s' % (captcha,)
        status = send_mail(email_title, email_body, self.email_from, [self.email_from, receive_email])
        return status

    def set_user_activate_link(self):
        """
        發送註冊後的激活郵件
        :return: 
        """
        pass

    def send_password_reset_link(self):
        """
        發送密碼重置連接
        :return: 
        """

    def send_email_reset_link(self):
        """
        發送郵箱重置連接
        :return: 
        """
# 附上郵箱model


class EmailVerifyRecord(models.Model):
    code = models.CharField(max_length=20, verbose_name=u"驗證碼")
    email = models.EmailField(max_length=50, verbose_name=u"郵箱")
    send_type = models.CharField(verbose_name=u"驗證碼類型",
                                 choices=(("register", u"註冊"), ("forget", u"找回密碼"), ("update_email", u"修改郵箱")),
                                 max_length=30)
    send_time = models.DateTimeField(verbose_name=u"發送時間", default=datetime.now)

    class Meta:
        verbose_name = u"郵箱驗證碼"
        verbose_name_plural = verbose_name

# 點擊郵箱連接激活
# 經過隨機字符串找出郵箱,再經過郵箱找出用戶

class AciveUserView(View):
    def get(self, request, active_code):
        all_records = EmailVerifyRecord.objects.filter(code=active_code)
        if all_records:
            for record in all_records:
                email = record.email
                user = UserProfile.objects.get(email=email)
                user.is_active = True
                user.save()
        else:
            return render(request, "active_fail.html")
        return render(request, "login.html")

第三方庫

二級域名組件(django-hosts)

詳細

cors跨域處理包(django-cors-headers)

安裝
pip install django-cors-headers
添加應用
在settings裏面配置

INSTALLED_APPS = (
    ...
    'corsheaders',
    ...
)
中間層設置
MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    ...
]
添加白名單
# CORS
CORS_ORIGIN_WHITELIST = (
    '127.0.0.1:8080',
    'localhost:8080', #凡是出如今白名單中的域名,均可以訪問後端接口
)
CORS_ALLOW_CREDENTIALS = True  # 指明在跨域訪問中,後端是否支持對cookie的操做 

調試工具(django-pdb)

1. pip install django-pdb
2. INSTALLED_APPS = (
  # 放在第一位置
  'django_pdb',)
3.MIDDLEWARE = [
  # 放在第一位置
  'django_pdb.middleware.PdbMiddleware',]
4.settings.DEBUG = True
5. 再pdb中輸入c,到視圖中執行

- pdb命令


命令 解釋
break 或 b 設置斷點
continue 或 c 繼續執行程序
list 或 l 查看當前行的代碼段
step 或 s 進入函數
return 或 r 執行代碼直到從當前函數返回
exit 或 q 停止並退出
next 或 n 執行下一行
pp 打印變量的值
help 幫助

redis(django-redis)

g. Redis緩存(依賴:pip3 install django-redis)

CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://127.0.0.1:6379",
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "CONNECTION_POOL_KWARGS": {"max_connections": 100,'decode_responses': True}
            # "PASSWORD": "密碼",
        }
    }
}
View Code
from django_redis import get_redis_connection
conn = get_redis_connection("default")
視圖中連接並操做

二、應用

a. 全站使用

   使用中間件,通過一系列的認證等操做,若是內容在緩存中存在,則使用FetchFromCacheMiddleware獲取內容並返回給用戶,當返回給用戶以前,判斷緩存中是否已經存在,若是不存在則UpdateCacheMiddleware會將緩存保存至緩存,從而實現全站緩存

    MIDDLEWARE = [
        'django.middleware.cache.UpdateCacheMiddleware',
        # 其餘中間件...
        'django.middleware.cache.FetchFromCacheMiddleware',
    ]

    CACHE_MIDDLEWARE_ALIAS = ""
    CACHE_MIDDLEWARE_SECONDS = ""
    CACHE_MIDDLEWARE_KEY_PREFIX = ""
View Code

b. 單獨視圖緩存

    方式一:
        from django.views.decorators.cache import cache_page

        @cache_page(60 * 15)
        def my_view(request):
            ...

    方式二:
        from django.views.decorators.cache import cache_page

        urlpatterns = [
            url(r'^foo/([0-9]{1,2})/$', cache_page(60 * 15)(my_view)),
        ]
View Code

c、局部視圖使用

    a. 引入TemplateTag

        {% load cache %}

    b. 使用緩存

        {% cache 5000 緩存key %}
            緩存內容
        {% endcache %}
View Code

- redis API
詳細

celery(django-celery)

詳細
# celery
# pip install redis==2.10.6
djcelery.setup_loader()
BROKER_BACKEND = 'redis'
BROKER_URL = 'redis://127.0.0.1:6379/0'
CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/1'
# 設置路徑讓celery能找到tasks文件
CELERY_IMPORTS = ['web_chat.tasks', ]
# 設置時區
CELERY_TIMEZONE = TIME_ZONE
# 防止死鎖
CELERYD_FORCE_EXECV = True
# 併發數
CELERYD_CONCURRENCY = 4
# 容許重試
CELERYD_ACKS_LATE = True
# 一個worker最多執行任務數
CELERYD_MAX_TASKS_PER_CHILD = 100
# 單個任務最大運行時間
CELERY_TASK_TIME_LIMIT = 60

 

驗證碼(django-simple-captcha)

詳細

第三方分頁組件(django-pure-pagination)

詳細

富文本編輯器(DjangoUeditor)

詳細

xadmin

詳細1詳細2
後臺管理三大要素(權限管理,少前端樣式,快速開發)
1. pip install xadmin
2. pip uninstall xadmin
3. gitclone https://github.com/J-Decree/mxonline_resources
4. 新建一個extra_apps存放,跟整理app的步驟同樣
5. INSTALL_APP加上xadmin
6. urls.py中(和django-admin統一文件)
  urlpatterns=[url(r'^xadmin/',xadmin.site.urls),]

注意:
- model顯示

class Meta:
    verbose_name = u"郵箱驗證碼" 
    verbose_name_plural = verbose_name  #這個是複數形式的顯示,默認會加個s

 

- 將UserProfile這個表移動到本身定義應用account下管理

1.UserAdmin在xadmin的auth.py下,裏面有個UserAdmin
2.仍是再plugin/auth.py下替換User,否則一些修改密碼的連接會報錯

3.仍是auth.py的最下面,修改url,默認是users/user,改密碼時會找不到

 

- 權限說明

針對於表,django爲每一個表都生成增刪改查4個權限,擁有的權限差異會致使操做的不一樣甚至可能連表都看不到

- modeladmin方法:保存對象時,能夠所作的操做

 

 - 怎麼寫插件
需求1:以xadmin遇到UeditorField字段能夠顯示富文本

1.在xadmin/plugin文件夾中新增ueditor.py(點我)
2.修改xadmin/plugin/__init__.py中的PLUGIN列表增長ueditor,注意要跟文件名同樣
3.adminx的modeladmin添加style_fields = {'detail':'ueditor'} 指明哪一個字段生效,detail是字段名

需求2: excel導入插件

1.在xadmin/plugin文件夾中新增excel.py(點我)
2.在xadmin/templates增長上面的藍色標記的excel.html(點我)
3.在須要按鈕的modeladmin下定義import_excel = True,並增長post方法,導入按鈕的方法的邏輯就是在這裏編寫

def post(self, request, *args, **kwargs):
    if 'excel' in request.FILES:
        pass
    return super(CourseAdmin, self).post(request, args, kwargs)

4.xadmin/__init__.py的PLUGIN列表中增長excel

支付寶

詳細

易錯點:
1. 運行報錯,注意看有沒有安裝pycryptodope:pip install pycryptodope
2. 支付使用的是商戶的私鑰+支付寶的公鑰,而不是你我的的公鑰(注意:支付寶公鑰是根據公鑰自動生成的)
3. 支付寶支付成功後,首先頁面作了跳轉,支付寶會往你指定的url裏發了一個post請求
4. 支付寶要求處理post請求的視圖函數返回HttpResponse('success') #若是沒有返回success,一天之內一直給你發相同post請求

  

開發環境

pip

# 批量安裝
pip install -r requirements.txt  #注,若是有報錯,先註釋掉報錯模塊,而後單獨安裝
# 生成requirement.txt文件
pip freeze > requirements.txt
# 查找指定模塊
pip frzeeze | grep 'pexpect'

虛擬環境

詳細

pycharm斷點調試

詳細  

Django推薦項目佈局

詳細

生產環境

uWSGI

#啓動
uwsgi --ini uwsgi.ini
#查看
ps ajx|grep uwsgi
#中止
uwsgi --stop uwsgi.pid 

nginx

詳細

- nginx經常使用命令

啓動
# 啓動nginx,能夠瀏覽器訪問一下本地地址 127.0.0.1:8080
nginx -c /path/to/nginx.conf
 # 測試nginx配置文件是否正確
nginx -t -c /path/to/nginx.conf

 查看
 lsof -i:80
 ps aux | grep nginx


更新
# 平滑重啓nginx kill -HUP 主進程號 #修改配置後從新加載生效
nginx -s reload 
#從新打開日誌文件
nginx -s reopen  

關閉 # 快速中止nginx
nginx -s stop  
# 完整有序的中止nginx
nginx -s quit

kill關閉
ps -ef | grep nginx
# 從容中止Nginx
kill -QUIT 主進程號     
# 快速中止Nginx
kill -TERM 主進程號     
# 強制中止Nginx
pkill -9   主進程號             

- 配置

首先,簡單地複習一下配置語法
若配置語法忘了,點擊這裏,簡潔明瞭
基礎的場景配置,點擊這裏

一些指令(坑)
index指令
alias和root指令對比
error_page指令(例子說明,自定義404頁面)
location指令(順序)

部署流程

一.代碼準備

第一步,setting.py更改變量

DEBUG = False 
ALLOWED_HOSTS = ['*']

第二步,指明靜態文件路徑
注:  1.DEBUG爲False時,django的開發環境的自動靜態文件代理失效,要使用STATIC_ROOT來指明你的靜態文件在哪裏,就像MEDIA_ROOT同樣。
  2.STATIC_ROOT還跟django的python manage.py collectstatic命令聯繫,指定的文件夾會指定爲用於靜態文件收集的文件夾
  3.若使用uwsgi和nginx均可以代理靜態文件,其實指不指明沒什麼影響

#setting.py:
STATIC_ROOT = os.path.join(BASE_DIR, "static")
#ErrorReport/urls.py:
url(r'^static/(?P<path>.*)$', serve, {'document_root': settings.STATIC_ROOT}),

第三步,收集靜態文件
1.STATIC_ROOT = os.path.join(BASE_DIR, "static1")
2.python manage.py collectstatic
3.刪除原有的static,將static1重命名爲static

二.環境準備

第一步,pip freeze > plist.txt
第二步,將項目代碼和plist.txt上傳
第三步,虛擬環境準備

pip install virtualenv
pip install virtualenvwrapper
mkvirtualenv -p python3.6 env36

三.配置uWSGI

第一步,安裝uwsgi

pip install uwsgi

第二步,命令行啓動測試

uwsgi --http 127.0.0.1:8080 --file ErrorReport/wsgi.py --static-map=/static=static

注意:若出現異常,cant not import django 解決辦法
  第一步:先重裝
  pip install django==1.11.12
  第二步:若django已經存在,則在wsgi.py中
  sys.path.append('/Users/yi/.virtualenvs/yi3/lib/python3.6/site-packages')  

第三步:新建配置文件wsgi.ini進行配置(若瀏覽器訪問127.0.0.1:8080 正常,則進行第三步)
 項目目錄下建立script文件夾,裏面新建wsgi.ini

[uwsgi]
# 項目目錄
chdir = /Users/yi/PycharmProjects/djangos/ErrorReport/
# 指定虛擬環境。防止虛擬virtualenv環境的包,而在真實環境沒有安裝相關包的致使錯誤
home = /Users/yi/.virtualenvs/yi3
# 指定項目的application
module = ErrorReport.wsgi:application
# 指定sock的文件路徑
# socket = /Users/yi/PycharmProjects/djangos/ErrorReport/script/uwsgi.sock
# 啓用主進程
master = true
# 進程個數
process=4
pidfile = /Users/yi/PycharmProjects/djangos/ErrorReport/script/uwsgi.pid
# 指定HTTP IP端口,如果有上游服務器,則這裏爲socket
 http = 127.0.0.1:9000
# socket = 127.0.0.1:9000

# 使用靜態文件掛載點
#「映射」指定請求前綴到你的文件系統上的物理目錄,--static-map mountpoint=path
# 例如:--static-map /images=/var/www/img
 static-map = /static=static
# 啓動uwsgi的用戶名和用戶組
uid = root
gid = root
# 自動移除unix Socket和pid文件當服務中止的時候
vacuum = true
# 序列化接受的內容,若是可能的話
thunder-lock = true
# 啓用線程
enable-threads = true
# 設置自中斷時間
harakiri = 30
# 設置緩衝
post-buffering = 4096
# 設置日誌目錄
daemonize = /Users/yi/PycharmProjects/djangos/ErrorReport/script/uwsgi.log
#每一個進程能夠接受的最大請求
max-requests = 5000
# buffer-size,代表收到的最大請求size,默認是4096,能夠將其改爲65535
buffer-size = 65535

第四步:更改uwsgi爲上游服務器(若瀏覽器測試127.0.0.1:9000訪問成功)
注: 當單獨測試uwsgi,http = 127.0.0.1:9000。當nginx作前端服務器時,要改爲socket = 127.0.0.1:9000
uwsgi.ini中:
http = 127.0.0.1:9000 => socket = 127.0.0.1:9000

四.配置nginx

第一步,安裝
這裏我用的是macOS電腦,主要用於開發測試,因此使用brew install nginx安裝便可。
若到真正的生產環境上,對nginx的模塊定製有更加嚴格的要求,則須要編譯安裝

macOS中使用brew安裝後,nginx的配置文件所在目錄爲

/usr/local/etc/nginx

第二步,更改目錄
macOS本來的nginx.conf很是不科學,咱們按照ubuntu的目錄結構來改造
在 /usr/local/etc/nginx中建立如下文件夾

conf.d # 通用通常配置
sites-available # 存放當前的server配置, 在這裏修改
sites-enabled # 激活並使用的server配置(從sites_available的文件建立快捷方式到sites-enabled)

第三步,更改nginx.conf文件
macOS的nginx.conf配置成ubuntu的默認nginx.conf。

worker_processes auto;

events {
    worker_connections 768;
    # multi_accept on;
}

http {

    ##
    # Basic Settings
    ##

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    # server_tokens off;

    # server_names_hash_bucket_size 64;
    # server_name_in_redirect off;

    include mime.types;
    default_type application/octet-stream;

    ##
    # SSL Settings
    ##

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
    ssl_prefer_server_ciphers on;

    ##
    # Logging Settings
    ##

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    ##
    # Gzip Settings
    ##

    gzip on;
    gzip_disable "msie6";

    # gzip_vary on;
    # gzip_proxied any;
    # gzip_comp_level 6;
    # gzip_buffers 16 8k;
    # gzip_http_version 1.1;
    # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    ##
    # Virtual Host Configs
    ##

    include conf.d/*.conf;
    include sites-enabled/*.conf;
}

#mail {
#    # See sample authentication script at:
#    # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# 
#    # auth_http localhost/auth.php;
#    # pop3_capabilities "TOP" "USER";
#    # imap_capabilities "IMAP4rev1" "UIDPLUS";
# 
#    server {
#        listen     localhost:110;
#        protocol   pop3;
#        proxy      on;
#    }
# 
#    server {
#        listen     localhost:143;
#        protocol   imap;
#        proxy      on;
#    }
#}

第四步,在sites-available下新增要部署項目的server配置
  例如:新建ErrorReport.conf

server {
        listen       80;
        server_name  myblog;
        client_max_body_size 75m;
        charset utf-8;
        
        # 靜態文件代理
        location /static{
            alias /Users/yi/PycharmProjects/djangos/ErrorReport/static;
        }
        
        # 上傳的靜態文件代理
        location /uploads{
            alias /Users/yi/PycharmProjects/djangos/ErrorReport/uploads;
        }
       
        # uwsgi轉發配置
        location /{
            include  /usr/local/etc/nginx/uwsgi_params;
            uwsgi_pass  127.0.0.1:9000;         
        }

        #location / {
         #   root /usr/local/etc/nginx/html;
          #  index index.html;
        #}
    }

第五步,在sites-enabled新建一個軟鏈接指向上一步新建的配置文件

從/usr/local/etc/nginx能夠看到,http塊下用include導入conf.d,sites-enabled的配置文件

 ln -s sites-available/ErrorReport.conf sites-enabled/ErrorReport.conf 

第六步,啓動nginx測試

sudo nginx -t 
sudo nginx
sudo nginx -s stop
sudo nginx -s reload

若訪問127.0.0.1成功,則部署成功
注:也能夠按照你所配置第server_name來測試訪問,可是要更改本地第host文件

經過supervisor管理進程

上面咱們已經用到了uwsgi,後面可能還會用到redis、celery,都須要開啓守護進程,其中celery自身還不支持守護進程。那麼如何管理這麼多進程呢,這時候能夠考慮下supervisor

pip install supervisor

配置

咱們能夠配置redis,celery,uwsgi進去,好比向下面同樣

[program:redis]
;指定運行目錄
directory=%(here)s/
;執行命令(redis-server redis配置文件路徑)
command=redis-server /etc/redis.conf

;啓動設置
numprocs=1          ;進程數
autostart=true      ;當supervisor啓動時,程序將會自動啓動
autorestart=true    ;自動重啓

;中止信號
stopsignal=INT


[program:celery.worker.default]
;指定運行目錄
directory=%(here)s/
;運行目錄下執行命令
command=celery -A DjangoProject worker --loglevel info --logfile log/celery_worker.log -Q default -n %%h-%(program_name)s-%(process_num)02d
process_name=%(process_num)02d

;啓動設置 
numprocs=2          ;進程數
autostart=true      ;當supervisor啓動時,程序將會自動啓動 
autorestart=true    ;自動重啓
 
;中止信號,默認TERM 
;中斷:INT (相似於Ctrl+C)(kill -INT pid),退出後會將寫文件或日誌(推薦) 
;終止:TERM (kill -TERM pid) 
;掛起:HUP (kill -HUP pid),注意與Ctrl+Z/kill -stop pid不一樣 
;從容中止:QUIT (kill -QUIT pid) 
stopsignal=INT

[program:uwsgi]
;指定運行目錄
directory=%(here)s/
;運行目錄下執行命令
command=uwsgi -i conf/uwsgi/uwsgi9090.ini

;啓動設置
numprocs=1          ;進程數
autostart=true      ;當supervisor啓動時,程序將會自動啓動
autorestart=true    ;自動重啓

;中止信號,默認TERM
;中斷:INT (相似於Ctrl+C)(kill -INT pid),退出後會將寫文件或日誌(推薦)
;終止:TERM (kill -TERM pid)
;掛起:HUP (kill -HUP pid),注意與Ctrl+Z/kill -stop pid不一樣
;從容中止:QUIT (kill -QUIT pid)
stopsignal=INT

使用

啓動supervisor輸入以下命令,使用具體的配置文件執行:

supervisord -c supervisord.conf

關閉supervisord須要經過supervisor的控制器:

supervisorctl -c supervisord.conf shutdown

重啓supervisord也是經過supervisor的控制器:

supervisorctl -c supervisord.conf reload

一些特殊的變量

%(here)s   配置文件所在路徑
(program_name)s   program的名字
%(process_num)02d  多進程時的進程號

注意:1.command中若是含有%,須要進行轉義%%
   2.多進程時若是不指定process_name會遇到以下錯誤

Error: Format string 'celery -A INTProject worker --loglevel info --logfile log/celery_worker.log -Q diff_task,caller_task -n %h' for 'program:celery.worker.mac.command' is badly formatted: incomplete format in section 'program:celery.worker.mac' (file: 'supervisord.conf')
相關文章
相關標籤/搜索