ORM概念
對象關系映射(Object Relational Mapping,簡稱ORM)模式是一種為了解決面向對象與關系數據庫存在的互不匹配的現象的技術。
簡單的說,ORM是通過使用描述對象和數據庫之間映射的元數據,將程序中的對象自動持久化到關系數據庫中。
ORM在業務邏輯層和數據庫層之間充當了橋梁的作用。
ORM由來
讓我們從O/R開始。字母O起源於"對象"(Object),而R則來自於"關系"(Relational)。
幾乎所有的軟件開發過程中都會涉及到對象和關系數據庫。在用戶層面和業務邏輯層面,我們是面向對象的。當對象的信息發生變化的時候,我們就需要把對象的信息保存在關系數據庫中。
按照之前的方式來進行開發就會出現程序員會在自己的業務邏輯代碼中夾雜很多SQL語句用來增加、讀取、修改、刪除相關數據,而這些代碼通常都是重復的。
ORM的優勢
1.移植性好,如果我們要把mysql改成orocal,只需要改變setting配置,不需要改orm的語句,隔離數據庫和數據庫版本之間的差異。
2.讓軟件開發人員專注於業務邏輯的處理,提高了開發效率。便於維護
3.orm會提供了防止sql注入的功能。
默認utf8編碼,Django默認所有的數據庫是utf8編碼
ORM解決的主要問題是對象和關系的映射。它通常把
- 一個類和一個表一一對應,
- 類的每個實例對應表中的一條記錄,
- 類的每個屬性對應表中的每個字段。
ORM的劣勢
ORM的缺點是會在一定程度上犧牲程序的執行效率。多了一個翻譯的過程
ORM用多了SQL語句就不會寫了,關系數據庫相關技能退化...
ORM總結
ORM只是一種工具,工具確實能解決一些重復,簡單的勞動。這是不可否認的。
但我們不能指望某個工具能一勞永逸地解決所有問題,一些特殊問題還是需要特殊處理的。
但是在整個軟件開發過程中需要特殊處理的情況應該都是很少的,否則所謂的工具也就失去了它存在的意義。
1 django默認支持sqlite,mysql, oracle,postgresql數據庫。
<1> sqlite
django默認使用sqlite的數據庫,默認自帶sqlite的數據庫驅動 。
引擎名稱:django.db.backends.sqlite3
<2> mysql
引擎名稱:django.db.backends.mysql
2 mysql驅動程序
- MySQLdb(mysql python) # django默認的驅動, 這就是為什么我們在init 文件中導入import pymysql pymysql.install_as_MySQLdb() 的原因了
- mysqlclient
- MySQL
- PyMySQL(純python的mysql驅動程序) #所以我們要修改init文件
3.常用的orm有:
- django的orm 功能最齊全,只適合django項目
- sqlalchemy 文檔不友好,適合大的項目
- peewee 輕量級
Django中的ORM
Model
在Django中model是你數據的單一、明確的信息來源。它包含了你存儲的數據的重要字段和行為。通常,一個模型(model)映射到一個數據庫表,
基本情況:
- 每個模型都是一個Python類,它是django.db.models.Model的子類。
- 模型的每個屬性都代表一個數據庫字段。
- 綜上所述,Django為您提供了一個自動生成的數據庫訪問API,詳詢官方文檔鏈接。
步驟:
1. 手動創建數據庫 mysite
2. 在app/models.py里面寫上一個類,必須繼承models.Model這個類 (注意啟動Django項目)
from django.db import models #Create your models here. class Person(models.Model): #注意后邊這個model要大寫 id=models.AutoField(primary_key=True) #默認不為空 name=models.CharField(max_length=32) hobby=models.CharField(max_length=32) def__str__(self): #這步是為了顯示對象時知道對象的名字 return self.name class Meta: db_table='user' #如果沒有這個表的名字為app_user,有了它就變為user了
3. 在Django 項目的settings.py 里面 配置上數據庫的相關信息
DATABASES={ "default":{ "ENGINE":"django.db.backends.mysql", "NAME":"mysite", "USER":"root", "PASSWORD":"123456", "HOST":"127.0.0.1", #一個空字符串是默認的localhost ,這里的值可以 填寫 "127.0.0.1" or"" or"localhost
如果這個值以向前斜杠(“/”)開始,而您使用的是MySQL,MySQL將通過UNIX(windows不支持)套接字連接到指定的套接字。 "PORT":3306 #空字符串表示默認端口,也就是3306 }
}
4. 在Django項目里的__init__.py里面寫上 兩句話 import pymysql pymysql.install_as_MySQLdb()
import pymysql pymysql.install_as_MySQLdb() #因為django默認的是mysqldb()驅動連接mysql,但是mysqldb不支持python3,所以我們就用pymysql替換mysqldb
5.在python.console中 給Django發布命令
1. python manage.py makemigrations # 告訴Django我的model.py中做了一些改動, 這時其實是在該app下建立 migrations目錄,
並記錄下你所有的關於modes.py的改動,比如0001_initial.py,數據庫文件數據庫沒有增加新的表"但是這個改動還沒有作用到數據庫文件,數據庫沒有增加新的表
2. python manage.py migrate # 這時候才真的把作用到數據庫文件,產生對應的表
數據類型:
數字 Int
字符: CharField() 相當於MySQL中的varchar() ,在這里並沒有char類型
時間: DateField() #只顯示年月日
DatetimeField() # 顯示年月日時分秒
關於時間:dateField():
如果你只在前端這樣寫:
<td>{{ class.first_day}}</td>
它得到的結果是這樣的:
那么怎么才能得到正常的年月日呢? 比如:2017-12-26,你需要這樣寫
<td>{{ class.first_day|date:'Y-m-d' }}</td>
對orm的操作
查:
首先在views 中導入 models模塊
from django.shortcuts import render,HttpResponse from . import models # Create your views here. def index(request): #查詢 student_list=models.Person.objects.all()[0].name #model.Person.objects.all得到的是一個queryset列表,列表中包含着所有對象,然后指定對象,
然后再指定它的屬性 print(student_list) return HttpResponse("ok")
2.models.Person.objects.first() 得到第一個對象
3. model.Person.objects.last() 得到最后一個對象
4.model.Person.objects.filter(name="韓信") 根據條件查詢 得到是一個列表包含着對象,取值時要按列表的規則來,
比如model.Person.objects.filter(name="韓信")[0].id
5.我們可以把條件寫成字典,傳到filter()中去, model.Person.objects.filter(**字典) #一定要把字典打散傳進去
6.model.Person.objects.get(id=1) 它會得到一個對象,但是get找不到或得到2個結果會報錯,filter找不到不會報錯,所以不推薦使用get
7.models.Tb1.objects.exclude(name='seven') # 排除指定條件的數據
增:
第一種方法:生成數據並提交到數據庫 def index(request): one=models.Person.objects.create(name="李白",hobby="女") #one 得到的僅是一個object. student_list=models.Person.objects.all() #student_list得到的才是全部的對象 print(one) # return HttpResponse("ok") return render(request,"student_list.html",{"student_list":student_list}) 第二種方法:只提交數據並不直接提交到數據庫,然后手動提交的數據庫 new_user2=models.Person(name="李白",hobby="nv) #先存到緩存中, new_user2.save()#這步才是提交到數據庫中
刪:
models.Person.objects.filter(name="曹蓋").delete()
改:
第一種方法
models.Person.objects.filter(name="李白").update(name="張白") #注意單個對象沒有update()方法
#第二種方法:
user48=models.Person.objects.filter(age=48).first()
user48.uername="一枝花"
#提交一下"
user48.save()
#單個對象如果要修改數據怎么辦?
obj=models.Person.object.get(id=2)
obj.name="韓信"
obj.save()
批量插入
def bulk_create(self, objs, batch_size=None): # 批量插入 # batch_size表示一次插入的個數 objs = [ models.DDD(name='r11'), models.DDD(name='r22') ] models.DDD.objects.bulk_create(objs, 10)
完整的文章參見
https://www.cnblogs.com/liwenzhou/p/8276526.html
表結構
class Class(models.Model): id = models.AutoField(primary_key=True) # 主鍵 cname = models.CharField(max_length=32) # 班級名稱 first_day = models.DateField() # 開班時間
查詢班級
URL部分:
url(r'^class_list/$', views.class_list, name="class_list"),
視圖部分:
def class_list(request): class_list = models.Class.objects.all() return render(request, "class_list.html", {"class_list": class_list})
HTML部分:
<table border="1"> {% for class in class_list %} <tr> <td>{{ forloop.counter }}</td> <td>{{ class.id }}</td> <td>{{ class.cname }}</td> <td>{{ class.first_day|date:'Y-m-d' }}</td> </tr> {% endfor %} </table>
新增班級
URL部分:
url(r'^add_class/$', views.add_class, name="add_class"),
視圖部分:
def add_class(request): # 前端POST填好的新班級信息 if request.method == "POST": cname = request.POST.get("cname") first_day = request.POST.get("first_day") # 還可以這么獲取提交的數據,但不推薦這么寫 # data = request.POST.dict() # del data["csrfmiddlewaretoken"] # 創建新數據的兩種方式 # new_class = models.Class(cname=cname, first_day=first_day) # new_class.save() models.Class.objects.create(cname=cname, first_day=first_day) # 跳轉到class_list return redirect(reverse('class_list')) # 返回添加班級的頁面 return render(request, "add_class.html")
HTML部分:
在班級列表頁面添加一個a標簽:
<a href="{% url 'add_class' %}">新頁面添加</a>
新添加頁面:
注意 {% csrf_token %} 和 date類型的input標簽。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>添加班級</title> </head> <body> <form action="{% url 'add_class' %}" method="post"> {% csrf_token %} <p>班級名稱:<input type="text" name="cname"></p> <p>開班日期:<input type="date" name="first_day"></p> <p>提交<input type="submit"></p> </form> </body> </html>
刪除班級
URL部分:
url(r'^delete_class/$', views.delete_class, name="delete_class"),
視圖部分:
def delete_class(request): class_id = request.GET.get("class_id") models.Class.objects.filter(id=class_id).delete() return redirect(reverse("class_list"))
HTML部分:
在班級列表頁面的表格中添加刪除。
<a href="{% url 'delete_class' %}?class_id={{ class.id }}">刪除</a>
編輯班級
URL部分:
url(r'^edit_class/$', views.edit_class, name="edit_class"),
視圖部分:
def edit_class(request): if request.method == "POST": class_id = request.POST.get("id") cname = request.POST.get("cname") first_day = request.POST.get("first_day") models.Class.objects.create(id=class_id, cname=cname, first_day=first_day) return redirect(reverse("class_list")) class_id = request.GET.get("class_id") class_obj = models.Class.objects.filter(id=class_id) if class_obj: class_obj = class_obj[0] return render(request, "edit_class.html", {"class": class_obj}) # 找不到該條記錄 else: return redirect(reverse("class_list"))
HTML部分:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>編輯班級</title> </head> <body> <form action="{% url 'edit_class' %}" method="post"> {% csrf_token %} <input type="text" value="{{ class.id }}" style="display: none"> <p>班級名稱:<input type="text" name="cname" value="{{ class.cname }}"></p> <p>開班日期:<input type="date" name="first_day" value="{{ class.first_day|date:'Y-m-d' }}"></p> <p>提交<input type="submit"></p> </form> </body> </html>
補充
如果將之前的URL由 /edit_class/?class_id=n修改為 /edit_class/n/ ,視圖函數和HTML部分分別應該如何修改?
URL部分:
url(r'^edit_class/(\d+)$', views.edit_class, name="edit_class"),
視圖部分:
def edit_class(request, class_id): if request.method == "POST": cname = request.POST.get("cname") first_day = request.POST.get("first_day") models.Class.objects.create(id=class_id, cname=cname, first_day=first_day) return redirect(reverse("class_list")) class_obj = models.Class.objects.filter(id=class_id) if class_obj: class_obj = class_obj[0] return render(request, "edit_class.html", {"class": class_obj}) # 找不到該條記錄 else: print("沒有該班級") return redirect(reverse("class_list"))
HTML部分:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="x-ua-compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>編輯班級</title> </head> <body> <form action="{% url 'edit_class' class.id %}" method="post"> {% csrf_token %} <input type="text" value="{{ class.id }}" style="display: none"> <p>班級名稱:<input type="text" name="cname" value="{{ class.cname }}"></p> <p>開班日期:<input type="date" name="first_day" value="{{ class.first_day|date:'Y-m-d' }}"></p> <p>提交<input type="submit"></p> </form> </body> </html>
queryset的特點:
1.可切片: 但是不支持負數的索引.
可切片 使用Python 的切片語法來限制查詢集記錄的數目 。它等同於SQL 的LIMIT 和OFFSET 子句。 1 >>> Entry.objects.all()[:5] # (LIMIT 5) >>> Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5) 不支持負的索引(例如Entry.objects.all()[-1])。通常,查詢集 的切片返回一個新的查詢集 —— 它不會執行查詢。
2. 可迭代
articleList=models.Article.objects.all() for article in articleList: print(article.title)
3.惰性查詢
查詢集 是惰性執行的 —— 創建查詢集不會帶來任何數據庫的訪問。你可以將過濾器保持一整天,直到查詢集 需要求值時,Django 才會真正運行這個查詢。
queryResult=models.Article.objects.all() # not hits database print(queryResult) # hits database for article in queryResult: print(article.title) # hits database
4.緩存機制
每個查詢集都包含一個緩存來最小化對數據庫的訪問。理解它是如何工作的將讓你編寫最高效的代碼。
在一個新創建的查詢集中,緩存為空。首次對查詢集進行求值 —— 同時發生數據庫查詢 ——Django 將保存查詢的結果到查詢集的緩存中並返回明確請求的結果(例如,如果正在迭代查詢集,則返回下一個結果)。接下來對該查詢集 的求值將重用緩存的結果。
請牢記這個緩存行為,因為對查詢集使用不當的話,它會坑你的。例如,下面的語句創建兩個查詢集,對它們求值,然后扔掉它們:
print([a.title for a in models.Article.objects.all()]) print([a.create_time for a in models.Article.objects.all()])
這意味着相同的數據庫查詢將執行兩次,顯然倍增了你的數據庫負載。同時,還有可能兩個結果列表並不包含相同的數據庫記錄,因為在兩次請求期間有可能有Article被添加進來或刪除掉。為了避免這個問題,只需保存查詢集並重新使用它:
queryResult=models.Article.objects.all() print([a.title for a in queryResult]) print([a.create_time for a in queryResult])
何時查詢集不會被緩存?
查詢集不會永遠緩存它們的結果。當只對查詢集的部分進行求值時會檢查緩存, 如果這個部分不在緩存中,那么接下來查詢返回的記錄都將不會被緩存。所以,這意味着使用切片或索引來限制查詢集將不會填充緩存。
例如,重復獲取查詢集對象中一個特定的索引將每次都查詢數據庫:
>>> queryset = Entry.objects.all() >>> print queryset[5] # Queries the database >>> print queryset[5] # Queries the database again
然而,如果已經對全部查詢集求值過,則將檢查緩存:
>>> queryset
=
Entry.objects.
all
()
>>> [entry
for
entry
in
queryset]
# Queries the database
>>>
print
queryset[
5
]
# Uses cache
>>>
print
queryset[
5
]
# Uses cache
下面是一些其它例子,它們會使得全部的查詢集被求值並填充到緩存中:
>>> [entry
for
entry
in
queryset]
>>>
bool
(queryset)
>>> entry
in
queryset
>>>
list
(queryset)
queryResult=models.Article.objects.all()
print(queryResult) # hits database
print(queryResult) # hits database
5.不能直接被序列化
queryset不能直接序列化為json字符串,這個知識點在rest_framework中利用到了.詳情請看這篇博客
單表查詢api匯總
Queryset:實際上是一些對象組成的列表
object是表示單個對象 注意:單個對象可以.字段名得到該字段下的內容 ,Queryset只能Queryset.values("字段名")才能得到該內容, 因為類中的對象只有.屬性名才能得到屬性值
Queryset常見的方法匯總:
1.filter()返回的是一個Queryset對象
2.all(): 返回的是一個Queryset對象
3.exclude(): 它包含了與所選條件不匹配的對象 返回的也是一個Queryset對象
models.Class_table.objects.exclude(cname="全棧10期")
結果:
(0.001) SELECT `app01_class_table`.`id`, `app01_class_table`.`cname` FROM `app01_class_table` WHERE NOT (`app01_class_table`.`cname` = '全棧10期') LIMIT 21; args=('全棧10期',) <QuerySet [<Class_table: Class_table object>, <Class_table: Class_table object>, <Class_table: Class_table object>, <Class_table: Class_table object>]>
4. values(*field):根據條件查找表中某一列的內容 ,返回一個ValueQuerySet——一個特殊的QuerySet,運行后得到的並不是一系列model的實例化對象,而是一個可迭代的字典列表 ,每一條記錄就是一個字典
>>>models.Class_table.objects.values("cname","id") #注意查詢時列名要加引號
結果: (0.001) SELECT `app01_class_table`.`cname` FROM `app01_class_table` LIMIT 21; args=() <QuerySet [{'cname': '全棧126期ffsdf',"id":5}, {'cname': '全棧100期',"id":6}, {'cname': '全棧129期',"id":7}, {'cname': '發鬼地方',"id"=8}]>
5.values_list(*field) :和values的作用差不多,返回一個ValueQuerySet——一個特殊的QuerySet, 形式為一個元組列表,列表中包含這元組,每一條信息就是一個元組
models.Class_table.objects.values_list("cname","id") <QuerySet [('全棧126期ffsdf', 5), ('全棧100期', 6), ('全棧129期', 7), ('發鬼地方', 8)]>
6.order_by(*field): 對查詢結果進行正向排序, order_by(*-field) 對結果進行反向排序
7.get() 它會得到一個對象,但是get找不到會報錯,
8.reverse(): 對查詢結果反向排序
9.distinct(): 從返回結果中去除重復紀錄,對Queryset用沒有意義,因為主鍵唯一,一般對values去重
10.count(): 返回數據庫中匹配查詢(QuerySet)的對象數量。
11.first(): 返回第一條記錄 ,返回的是一個對象
12.last(): 返回最后一條記錄.返回的是一個對象
13.exists(): 如果QuerySet包含數據,就返回True,否則返回False ,看下邊的原生SQL語句,只查一條數據,而不像count要查所有的數據,這樣提高了工作效率
學了rest-framework來補充幾個api
14 only(*field):根據條件返回queryset數據類型,列表中套對象(只包含*field的對象),這就是和values的不同
text=DegreeCourse.objects.all().only('id','name') print(">>>>",text)
結果:
>>>> <QuerySet [<DegreeCourse: linx雲計算運維>, <DegreeCourse: python 全棧中期>, <DegreeCourse: python全棧高級>]>
15. defer(*field):返回不含條件中字段的Queryset對象列表注意和exclude不同,defer只過濾字段,exclude過濾的是對象
tect=DegreeCourse.objects.all().defer('id','name') print(tect)
結果:
<QuerySet [<DegreeCourse: python 全棧中期>, <DegreeCourse: python全棧高級>, <DegreeCourse: linx雲計算運維>]>
返回的是QuerySet對象:
1. all() 2. filter() 3. exclude() 4. valus() 5. values_list() 6. order_by() 7. reverse() 8. distinct() 9.only() 10.defer()
返回數字的:
1. count()
返回布爾值的:
1. exists()
返回具體的對象的:
1. get()
2. first()
3. last()
單表查詢之神奇的雙下划線
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 獲取id大於1 且 小於10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 獲取id等於11、22、33的數據 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") # 獲取name字段包含"ven"的 相當於SQL中的 like %ven% models.Tb1.objects.filter(name__icontains="ven") # icontains大小寫不敏感 models.Tb1.objects.filter(id__range=[1, 3]) # id范圍是1到3的,等價於SQL的bettwen and 類似的還有:name__startswith == "abc #相當於 like abc% istartswith, name__endswith="cd", #相當於SQL中的 like %cd iendswith date字段還可以: models.Class.objects.filter(first_day__year=2017)
在Django的日志設置中,配置上一個名為django.db.backends的logger實例即可查看翻譯后的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項目完整版LOGGING配置:
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'
},
'default': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切
'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件
'maxBytes': 1024 * 1024 * 50, # 日志大小 50M
'backupCount': 3,
'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 * 50, # 日志大小 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': ['default', 'console', 'error'], # 上線之后可以把'console'移除
'level': 'DEBUG',
'propagate': True,
},
# 名為 'collect'的logger還單獨處理
'collect': {
'handlers': ['console', 'collect'],
'level': 'INFO',
}
},
}
Django項目常用LOGGING配置
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' }, 'default': { 'level': 'INFO', 'class': 'logging.handlers.RotatingFileHandler', # 保存到文件,自動切 'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"), # 日志文件 'maxBytes': 1024 * 1024 * 50, # 日志大小 50M 'backupCount': 3, '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 * 50, # 日志大小 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': ['default', 'console', 'error'], # 上線之后可以把'console'移除 'level': 'DEBUG', 'propagate': True, }, # 名為 'collect'的logger還單獨處理 'collect': { 'handlers': ['console', 'collect'], 'level': 'INFO', } }, }