折騰躯肌,是對(duì)夢(mèng)想的尊重缭贡。
一炉擅、ORM簡(jiǎn)介
??MVC或者M(jìn)VC框架中包括一個(gè)重要的部分辉懒,就是ORM,它實(shí)現(xiàn)了數(shù)據(jù)模型與數(shù)據(jù)庫(kù)的解耦谍失,即數(shù)據(jù)模型的設(shè)計(jì)不需要依賴于特定的數(shù)據(jù)庫(kù)眶俩,在業(yè)務(wù)邏輯層和數(shù)據(jù)庫(kù)層之間起了橋梁的作用。
??ORM(Object Relational Mapping)是“對(duì)象—關(guān)系—映射”的簡(jiǎn)稱快鱼。
二颠印、前期準(zhǔn)備
1、在settings中配置數(shù)據(jù)庫(kù)
首先想將模型轉(zhuǎn)為mysql數(shù)據(jù)庫(kù)中的表抹竹,要在settings中配置线罕。Django默認(rèn)使用的是sqlite數(shù)據(jù)庫(kù),這里換成mysql窃判。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_name', # 要連接的數(shù)據(jù)庫(kù)钞楼,連接前需要?jiǎng)?chuàng)建好
'HOST': '127.0.0.1', # 連接主機(jī),默認(rèn)本機(jī)
'USER': 'root', # 連接數(shù)據(jù)庫(kù)的用戶名
'PASSWORD': ' ', # 連接數(shù)據(jù)庫(kù)的密碼
'PORT': 3306 # 端口 默認(rèn)3306
}
}
?? tips: NAME即數(shù)據(jù)庫(kù)的名字袄琳,在mysql連接前該數(shù)據(jù)庫(kù)必須已經(jīng)創(chuàng)建询件,而上面的sqlite數(shù)據(jù)庫(kù)下的db.sqlite3則是項(xiàng)目自動(dòng)創(chuàng)建 USER和PASSWORD分別是數(shù)據(jù)庫(kù)的用戶名和密碼。
??設(shè)置完后啟動(dòng)項(xiàng)目會(huì)報(bào)錯(cuò):no module named MySQLdb唆樊。這是因?yàn)閐jango默認(rèn)你導(dǎo)入的驅(qū)動(dòng)是MySQLdb雳殊,可是MySQLdb 對(duì)于py3有很大問(wèn)題,所以我們需要的驅(qū)動(dòng)是PyMySQL窗轩,接下來(lái)去激活我們的mysql
我們只需要找到項(xiàng)目名文件下的init,在里面寫入:
import pymysql
pymysql.install_as_MySQLdb()
2夯秃、在settings中配置日志Logging
日志在程序開(kāi)發(fā)中是少不了的,通過(guò)日志我們可以分析到錯(cuò)誤在什么地方痢艺,有什么異常仓洼。在生產(chǎn)環(huán)境下有很大的用途,而且一旦涉及到對(duì)數(shù)據(jù)庫(kù)的操作堤舒,更應(yīng)該記錄下來(lái)色建,數(shù)據(jù)是很重要的。在Java開(kāi)發(fā)中通常用log4j,logback等第三方組件舌缤。那么在django中是怎么處理日志箕戳?django利用的就是Python提供的logging模塊,但django中要用logging国撵,還得有一定的配置規(guī)則陵吸,需要在setting中設(shè)置。
(1) logging 模塊
logging模塊為應(yīng)用程序提供了靈活的手段記錄事件介牙、錯(cuò)誤壮虫、警告和調(diào)試信息。對(duì)這些信息可以進(jìn)行收集环础、篩選囚似、寫入文件剩拢、發(fā)送給系統(tǒng)日志等操作,甚至還可以通過(guò)網(wǎng)絡(luò)發(fā)送給遠(yuǎn)程計(jì)算機(jī)饶唤。
a徐伐、 日志記錄級(jí)別
logging模塊的重點(diǎn)在于生成和處理日志消息。每條消息由一些文本和指示其嚴(yán)重性的相關(guān)級(jí)別組成募狂。級(jí)別包含符號(hào)名稱和數(shù)字值呵晨。
級(jí)別 | 值 | 描述 |
---|---|---|
CRITICAL | 50 | 關(guān)鍵錯(cuò)誤/消息 |
ERROR | 40 | 錯(cuò)誤 |
WARNING | 30 | 警告消息 |
INFO | 20 | 通知消息 |
DEBUG | 10 | 調(diào)試 |
NOTSET | 0 | 無(wú)級(jí)別 |
b、記錄器
記錄器負(fù)責(zé)管理日志消息的默認(rèn)行為熬尺,包括日志記錄級(jí)別、輸出目標(biāo)位置谓罗、消息格式以及其它基本細(xì)節(jié)粱哼。
關(guān)鍵字參數(shù) | 描述 |
---|---|
filename | 將日志消息附加到指定文件名的文件 |
filemode | 指定用于打開(kāi)文件模式 |
format | 用于生成日志消息的格式字符串 |
datefmt | 用于輸出日期和時(shí)間的格式字符串 |
level | 設(shè)置記錄器的級(jí)別 |
stream | 提供打開(kāi)的文件,用于把日志消息發(fā)送到文件 |
c檩咱、 format日志消息格式
格式 | 描述 |
---|---|
%(name)s | 記錄器的名稱 |
%(levelno)s | 數(shù)字形式的日志記錄級(jí)別 |
%(levelname)s | 日志記錄級(jí)別的文本名稱 |
%(filename)s | 執(zhí)行日志記錄調(diào)用的源文件的文件名稱 |
%(pathname)s | 執(zhí)行日志記錄調(diào)用的源文件的路徑名稱 |
%(funcName)s | 執(zhí)行日志記錄調(diào)用的函數(shù)名稱 |
%(module)s | 執(zhí)行日志記錄調(diào)用的模塊名稱 |
%(lineno)s | 執(zhí)行日志記錄調(diào)用的行號(hào) |
%(created)s | 執(zhí)行日志記錄的時(shí)間 |
%(asctime)s | 日期和時(shí)間 |
%(msecs)s | 毫秒部分 |
%(thread)d | 線程ID |
%(threadName)s | 線程名稱 |
%(process)d | 進(jìn)程ID |
%(message)s | 記錄的消息 |
d揭措、內(nèi)置處理器
logging模塊提供了一些處理器,可以通過(guò)各種方式處理日志消息刻蚯。使用addHandler()方法將這些處理器添加給Logger對(duì)象绊含。另外還可以為每個(gè)處理器配置它自己的篩選和級(jí)別。
- handlers.DatagramHandler(host炊汹,port)?發(fā)送日志消息給位于制定host和port上的UDP服務(wù)器躬充。
- handlers.FileHandler(filename)?將日志消息寫入文件filename。
- handlers.HTTPHandler(host, url)?使用HTTP的GET或POST方法將日志消息上傳到一臺(tái)HTTP 服務(wù)器讨便。
- handlers.RotatingFileHandler(filename)?將日志消息寫入文件filename充甚。如果文件的大小超出maxBytes制定的值,那么它將被備份為filename1霸褒。
ps: 由于內(nèi)置處理器還有很多伴找,如果想更深入了解,可以查看官方手冊(cè)废菱。
(2) Django中使用logging記錄日志
a技矮、在settings.py中進(jìn)行配置
- 完整版
?? 先導(dǎo)入模塊
import logging
import django.utils.log
import logging.handlers
LOGGING = {
'version': 1, # 解析配置,目前為止殊轴,這是dictConfig 格式唯一的版本
'disable_existing_loggers': True, # 這是一個(gè)布爾型值呵曹,默認(rèn)值為True(為了向后兼容)表示禁用已經(jīng)存在的logger,除非它們或者它們的祖先明確的出現(xiàn)在日志配置中侈咕;如果值為False則對(duì)已存在的loggers保持啟動(dòng)狀態(tài)疙教。
'formatters': {
'standard': {
'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'} #日志格式
},
'filters': {
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'class': 'django.utils.log.AdminEmailHandler',
'include_html': True,
},
'default': {
'level':'DEBUG',
'class':'logging.handlers.RotatingFileHandler',
'filename': '/sourceDns/log/all.log', #日志輸出文件
'maxBytes': 1024*1024*5, #文件大小
'backupCount': 5, #備份份數(shù)
'formatter':'standard', #使用哪種formatters日志格式
},
'error': {
'level':'ERROR',
'class':'logging.handlers.RotatingFileHandler',
'filename': '/sourceDns/log/error.log',
'maxBytes':1024*1024*5,
'backupCount': 5,
'formatter':'standard',
},
'console':{
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'standard'
},
'request_handler': {
'level':'DEBUG',
'class':'logging.handlers.RotatingFileHandler',
'filename': '/sourceDns/log/script.log',
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter':'standard',
},
'scprits_handler': {
'level':'DEBUG',
'class':'logging.handlers.RotatingFileHandler',
'filename':'/sourceDns/log/script.log',
'maxBytes': 1024*1024*5,
'backupCount': 5,
'formatter':'standard',
}
},
'loggers': {
'django': {
'handlers': ['default', 'console'],
'level': 'DEBUG',
'propagate': False
},
'django.request': {
'handlers': ['request_handler'],
'level': 'DEBUG',
'propagate': False,
},
'scripts': {
'handlers': ['scprits_handler'],
'level': 'INFO',
'propagate': False
},
'sourceDns.webdns.views': {
'handlers': ['default', 'error'],
'level': 'DEBUG',
'propagate': True
},
'sourceDns.webdns.util':{
'handlers': ['error'],
'level': 'ERROR',
'propagate': True
}
}
}
??tips:
loggers類型為"django"這將處理所有類型日志;
sourceDns.webdns.views 應(yīng)用的py文件
- 簡(jiǎn)化版
import logging
import django.utils.log
import logging.handlers
import logging.StreamHandler
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'standard': {
'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'} #日志格式
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': { # 數(shù)據(jù)庫(kù)引擎
'handlers': ['console'],
'propagate': True,
'level': 'DEBUG',
},
}
}
參數(shù)解析:
【a】formatters:配置打印日志格式
【b】handlers:用來(lái)定義具體處理日志的方式韧拒,可以定義多種淹接,"default"就是默認(rèn)方式十性,"console"就是打印到控制臺(tái)方式,"導(dǎo)入StreamHandler",日志信息會(huì)輸出到指定的stream中,如果stream為空則默認(rèn)輸出到sys.stderr塑悼。
【c】loggers:用來(lái)配置用那種handlers來(lái)處理日志劲适,比如你同時(shí)需要輸出日志到文件、控制臺(tái)厢蒜。
(3) 在settings中注冊(cè)APP
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book'
]
?? tips:如果出現(xiàn)如下報(bào)錯(cuò)
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.3 or newer is required; you have 0.7.11.None
因?yàn)镸ySQLclient目前只支持到python3.4霞势,如果使用的更高版本的python,需要修改如下:
通過(guò)查找路徑C:\Programs\Python\Python36-32\Lib\site-packages\Django-2.0-py3.6.egg\django\db\backends\mysql
把這個(gè)路徑文件中下面這兩句話注釋掉:
if version < (1, 3, 3):
raise ImproperlyConfigured("mysqlclient 1.3.3 or newer is required; you have %s" % Database.__version__)
?? tips: 如果想打印orm轉(zhuǎn)換過(guò)程中的sql斑鸦,需要在settings中進(jìn)行如下配置:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
3愕贡、在models中創(chuàng)建表
(1) 創(chuàng)建模型表
from django.db import models
class Book(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
state=models.BooleanField()
pub_date=models.DateField()
price=models.DecimalField(max_digits=8,decimal_places=2)
publish=models.CharField(max_length=32)
(2) ORM字段介紹
Django提供了很多字段類型,比如URL/Email/IP/ 但是mysql數(shù)據(jù)沒(méi)有這些類型巷屿,這類型存儲(chǔ)到數(shù)據(jù)庫(kù)上本質(zhì)是字符串?dāng)?shù)據(jù)類型,其主要目的是為了封裝底層SQL語(yǔ)句固以。
每個(gè)字段有一些特有的參數(shù),例如嘱巾,CharField需要max_length參數(shù)來(lái)指定VARCHAR數(shù)據(jù)庫(kù)字段的大小憨琳。還有一些適用于所有字段的通用參數(shù)。 這些參數(shù)在文檔中有詳細(xì)定義旬昭,這里我們只簡(jiǎn)單介紹一些最常用的:
【常用字段】
AutoField
?int自增列篙螟,必須填入?yún)?shù) primary_key=True。當(dāng)model中如果沒(méi)有自增列问拘,則自動(dòng)會(huì)創(chuàng)建一個(gè)列名為id的列( 如果你不指定主鍵的話,系統(tǒng)會(huì)自動(dòng)添加一個(gè)主鍵字段到你的 model)遍略。
?自定義一個(gè)主鍵:my_id=models.AutoField(primary_key=True)
CharField
?字符串類型,必須提供max_length參數(shù)骤坐,用于從數(shù)據(jù)庫(kù)層和Django校驗(yàn)層限制該字段所允許的最大字符數(shù)墅冷。IntegerField
?整數(shù)類型,范圍在 -2147483648 to 2147483647FloatField
?浮點(diǎn)數(shù)類型. 必須提供兩個(gè)參數(shù):
??max_digits?總位數(shù)(不包括小數(shù)點(diǎn)和符號(hào))
??decimal_places?小數(shù)位數(shù)
??舉例來(lái)說(shuō), 要保存最大值為 999 (小數(shù)點(diǎn)后保存2位),你要這樣定義字段:models.FloatField(..., max_digits=5, decimal_places=2)
??要保存最大值一百萬(wàn)(小數(shù)點(diǎn)后保存10位)的話,你要這樣定義:models.FloatField(..., max_digits=19, decimal_places=10)
??admin 用一個(gè)文本框(<input type="text">)表示該字段保存的數(shù)據(jù).
DateField
?日期字段或油,日期格式 YYYY-MM-DD 年月日. 共有下列額外的可選參數(shù):
??Argument?描述
??auto_now?當(dāng)對(duì)象被保存時(shí),自動(dòng)將該字段的值設(shè)置為當(dāng)前時(shí)間寞忿,每次操作該數(shù)據(jù)都會(huì)自動(dòng)更新時(shí)間,通常用于表示 "last-modified" 時(shí)間戳
??auto_now_add?當(dāng)對(duì)象首次被創(chuàng)建時(shí),自動(dòng)將該字段的值設(shè)置為當(dāng)前時(shí)間顶岸,此后不再更新腔彰,通常用于表示對(duì)象創(chuàng)建時(shí)間
??(僅僅在admin中有意義...)DateTimeField
?日期時(shí)間字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 年月日時(shí)分秒辖佣。參數(shù)和datefield一樣霹抛。BooleanField
?A true/false field。admin 用 checkbox 來(lái)表示此類字段卷谈。TextField
?一個(gè)容量很大的文本字段
?admin 用一個(gè) <textarea> (文本區(qū)域)表示該字段數(shù)據(jù)(一個(gè)多行編輯框)杯拐。EmailField
?一個(gè)帶有檢查Email合法性的 CharField,不接受 maxlength 參數(shù)。FileField
?一個(gè)文件上傳字段
? 要求一個(gè)必須有的參數(shù): upload_to, 一個(gè)用于保存上載文件的本地文件系統(tǒng)路徑。這個(gè)路徑必須包含 strftime 端逼,formatting,該格式將被上載文件的 date/time替換(so that uploaded files don't fill up the given directory)朗兵。
?admin 用一個(gè)<input type="file">部件表示該字段保存的數(shù)據(jù)(一個(gè)文件上傳部件) 。
?? 注意:在一個(gè)model中使用FileField或ImageField需要以下步驟:
?(1) 在你的settings文件中顶滩,定義一個(gè)完整路徑給MEDIA_ROOT以便讓Django在此處保存上傳文件(出于性能考慮余掖,這些文件并不保存到數(shù)據(jù)庫(kù)),定義MEDIA_URL作為該目錄的公共URL,要確保該目錄對(duì)web服務(wù)器用戶賬號(hào)是可寫的礁鲁。
?(2) 在你的model中添加FileField或ImageField盐欺,并確保定義了upload_to選項(xiàng),以告訴Django使用MEDIA_ROOT的哪個(gè)子目錄保存上傳文件仅醇,你的數(shù)據(jù)庫(kù)中要保存的只是文件的路徑(相對(duì)于MEDIA_ROOT)冗美,出于習(xí)慣你一定很想使用Django提供的get_<fieldname>_url函數(shù),舉例來(lái)說(shuō)析二,如果你的ImageField叫做mug_shot粉洼,你就可以在模板中以{{ object.get_mug_shot_url }}這樣的方式得到圖像的絕對(duì)路徑。FilePathField
?可選項(xiàng)目為某個(gè)特定目錄下的文件名甲抖。 支持三個(gè)特殊的參數(shù), 其中第一個(gè)是必須提供的。
參數(shù)?|?描述
path?|?必需參數(shù)心铃,一個(gè)目錄的絕對(duì)文件系統(tǒng)路徑FilePathField 據(jù)此得到可選項(xiàng)目准谚。例如"/home/images"
match?|?可選參數(shù),一個(gè)正則表達(dá)式, 作為一個(gè)字符串, FilePathField 將使用它過(guò)濾文件名去扣。注意這個(gè)正則表達(dá)式只會(huì)應(yīng)用到 base filename 而不是路徑全名柱衔。例如"foo.*\.txt^"
,將匹配文件foo23.txt愉棱,卻不匹配bar.txt或foo23.gif唆铐。
recursive?|?可選參數(shù),要么True,要么False,默認(rèn)是False,是否包括path下面的全部子目錄奔滑。
match?|?僅應(yīng)用于 base filename, 而不是路徑全名艾岂。 那么,這個(gè)例子:FilePathField(path="/home/images", match="foo.*", recursive=True)...
會(huì)匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif。ImageField
?類似 FileField, 不過(guò)要校驗(yàn)上傳對(duì)象是否是一個(gè)合法圖片朋其。它有兩個(gè)可選參數(shù):height_field和width_field,如果提供這兩個(gè)參數(shù),則圖片將按提供的高度和寬度規(guī)格保存王浴。URLField
?用于保存 URL, 若 verify_exists 參數(shù)為 True (默認(rèn)), 給定的 URL 會(huì)預(yù)先檢查是否存在( 即URL是否被有效裝入且沒(méi)有返回404響應(yīng))梅猿。
?admin 用一個(gè) <input type="text"> 文本框表示該字段保存的數(shù)據(jù)(一個(gè)單行編輯框)氓辣。NullBooleanField
?類似 BooleanField, 不過(guò)允許 NULL 作為其中一個(gè)選項(xiàng),推薦使用這個(gè)字段而不要用 BooleanField 加 null=True
? admin 用一個(gè)選擇框 <select> (三個(gè)可選擇的值:"Unknown", "Yes" 和 "No" ) 來(lái)表示這種字段數(shù)據(jù)袱蚓。SlugField
? "Slug" 是一個(gè)報(bào)紙術(shù)語(yǔ). slug 是某個(gè)東西的小小標(biāo)記(短簽), 只包含字母,數(shù)字,下劃線和連字符钞啸。它們通常用于URLs。
? 若你使用 Django 開(kāi)發(fā)版本,你可以指定 maxlength.。若 maxlength 未指定, Django 會(huì)使用默認(rèn)長(zhǎng)度: 50体斩。在以前的 Django 版本,沒(méi)有任何辦法改變50 這個(gè)長(zhǎng)度梭稚。這暗示了 db_index=True。
? 它接受一個(gè)額外的參數(shù): prepopulate_from, which is a list of fields from which to auto-#populate the slug, via JavaScript,in the object's admin form: models.SlugField (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受DateTimeFields硕勿。XMLField
?一個(gè)校驗(yàn)值是否為合法XML的 TextField,必須提供參數(shù): schema_path, 它是一個(gè)用來(lái)校驗(yàn)文本的 RelaxNG schema 的文件系統(tǒng)路徑哨毁。IPAddressField
?一個(gè)字符串形式的 IP 地址, (i.e. "24.124.1.30")CommaSeparatedIntegerField
?用于存放逗號(hào)分隔的整數(shù)值,類似 CharField, 必須要有maxlength參數(shù)源武。
詳解
(1) 字符串類(以下在數(shù)據(jù)庫(kù)中本質(zhì)都是字符串?dāng)?shù)據(jù)類型扼褪,此類字段只是在Django自帶的admin中生效)
??models.CharField 對(duì)應(yīng)的是MySQL的varchar數(shù)據(jù)類型
??tips:char 和 varchar的區(qū)別 :
?char和varchar的共同點(diǎn)是存儲(chǔ)數(shù)據(jù)的長(zhǎng)度,不能超過(guò)max_length限制粱栖;
?不同點(diǎn)是varchar根據(jù)數(shù)據(jù)實(shí)際長(zhǎng)度存儲(chǔ)话浇,char按指定max_length( )存儲(chǔ)數(shù)據(jù),所有前者更節(jié)省硬盤空間闹究;
EmailField(CharField):
IPAddressField(Field)
URLField(CharField)
SlugField(CharField)
UUIDField(Field)
FilePathField(Field)
FileField(Field)
ImageField(FileField)
CommaSeparatedIntegerField(CharField)
------- 在模型表中的定義 -------
class Publisher(models.Model):
id = models.AutoField(primary_key=True)
publishName = models.CharField(max_length=32)
address = models.CharField(max_length=32)
tele = models.CharField(max_length=32)
(2) 時(shí)間字段
models.DateTimeField(null=True)
date=models.DateField()
(3) 數(shù)字字段
(max_digits=30,decimal_places=10) 總長(zhǎng)度30幔崖,小數(shù)位10位
num = models.IntegerField()
num = models.FloatField() 浮點(diǎn)
price=models.DecimalField(max_digits=8,decimal_places=3) 精確浮點(diǎn)
(4) 枚舉字段
?在數(shù)據(jù)庫(kù)存儲(chǔ)枚舉類型,比外鍵有什么優(yōu)勢(shì)渣淤?
?無(wú)需連表查詢性能低赏寇,省硬盤空間(選項(xiàng)不固定時(shí)用外鍵);
?在modle文件里不能動(dòng)態(tài)增加(選項(xiàng)一成不變用Django的choice)
choice=(
(1,'男人'),
(2,'女人'),
(3,'其他')
)
lover=models.IntegerField(choices=choice) #枚舉類型
# 或者
gender = models.CharField(verbose_name='性別', max_length=8, choices=(("male", "男"), ("female", "女")),default='female')
(5) 其他字段
db_index = True 表示設(shè)置索引
unique(唯一的意思) = True 設(shè)置唯一索引
------- 聯(lián)合唯一索引 -------
class Meta:
unique_together = (
('email','ctime'),
)
------- 聯(lián)合索引(不做限制)------
index_together = (
('email','ctime'),
)
------- 多對(duì)多操作 -------
ManyToManyField(RelatedField)
(6) 自定義字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
自定義char類型字段:
class FixedCharField(models.Field):
"""
自定義的char類型的字段類
"""
def __init__(self, max_length, *args, **kwargs):
self.max_length = max_length
super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)
def db_type(self, connection):
"""
限定生成數(shù)據(jù)庫(kù)表的字段類型為char,長(zhǎng)度為max_length指定的值
"""
return 'char(%s)' % self.max_length
class Class(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=25)
# 使用自定義的char類型的字段
cname = FixedCharField(max_length=25)
附ORM字段與數(shù)據(jù)庫(kù)實(shí)際字段的對(duì)應(yīng)關(guān)系
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
【常用字段參數(shù)】
-
null
表示某個(gè)字段可以為空价认,如果為True嗅定,Django將用NULL在數(shù)據(jù)庫(kù)中存儲(chǔ)空值,默認(rèn)是False用踩。 -
blank
如果為True渠退,該字段允許不填,默認(rèn)為False脐彩。
??tips:這與null不同碎乃,null純粹是數(shù)據(jù)庫(kù)范疇的,而blank是數(shù)據(jù)驗(yàn)證范疇的惠奸。如果一個(gè)字段的blank=True梅誓,表單的驗(yàn)證將允許該字段是空值,如果字段的blank=False佛南,該字段是必填的 - default
字段的默認(rèn)值证九,可以是一個(gè)值或者可調(diào)用對(duì)象,如果可調(diào)用共虑,每有新對(duì)象被創(chuàng)建它都會(huì)被調(diào)用愧怜。 - primary_key
如果為True,那么這個(gè)字段就是模型的主鍵妈拌,如果你沒(méi)有指定任何一個(gè)字段的primary_key=True,Django就會(huì)自動(dòng)添加IntegerField字段作為主鍵拥坛,所以除非你想覆蓋默認(rèn)的主鍵行為蓬蝶,否則沒(méi)必要設(shè)置任何一個(gè)字段的primary_key=True。 - unique
如果該值設(shè)置為True猜惋,則這個(gè)數(shù)據(jù)字段的值在整張表中必須是唯一的丸氛。 - required
如果設(shè)置該值為True,則表示請(qǐng)求不能為空 - choice
由二元組組成的一個(gè)可迭代對(duì)象(例如:列表或元組)著摔,用來(lái)給字段提供選擇項(xiàng)缓窜,如果設(shè)置了choices,默認(rèn)的表單將是一個(gè)選擇框而不是標(biāo)準(zhǔn)的文本框谍咆,而且這個(gè)選擇框的選項(xiàng)就是choices中的選項(xiàng)禾锤。
class User(models.Model):
name=models.CharField(max_length=32)
password=MyCharField(max_length=32)
choices=((1,'重點(diǎn)本科'),(2,'普通本科'),(3,'屇〔欤科'),(4恩掷,'其他'))
education= models.IntegerField(choices=choices) 通過(guò)選擇數(shù)字來(lái)選擇相應(yīng)的屬性
user_obj.education 拿到的是數(shù)字??
uer_obj.get_education_display() 固定用法,獲取choice字段對(duì)應(yīng)的注釋??
- db_index
如果db_index=True供嚎,則表示為此字段設(shè)置索引黄娘。 - only與defer
兩者是相反的models.User.objects.only('name')
??不走數(shù)據(jù)庫(kù)
[ DateField 和 DateTimeField中的參數(shù) ]
- auto_now_add
配置auto_now_add=True,創(chuàng)建數(shù)據(jù)記錄的時(shí)候會(huì)把當(dāng)前時(shí)間添加到數(shù)據(jù)庫(kù)克滴。 - auto_now
配置auto_now=True逼争,每次更新數(shù)據(jù)記錄的時(shí)候會(huì)更新該字段。
【關(guān)系字段及參數(shù)】
??ForeignKey
?外鍵類型在ORM中用來(lái)表示外鍵關(guān)聯(lián)關(guān)系劝赔,一般把ForeignKey字段設(shè)置在‘一對(duì)多中多的一方’誓焦。
?ForeignKey可以和其他表做關(guān)聯(lián)關(guān)系同時(shí)也可以和自身做關(guān)聯(lián)關(guān)系。
- 字段參數(shù)
to?設(shè)置要關(guān)聯(lián)的表
to_field?設(shè)置要關(guān)聯(lián)的表的字段
related_name?反向操作時(shí)使用的字段名望忆,用于代替原反向查詢時(shí)的'表名_set'
舉個(gè)例子??
class Classes(models.Model):
name = models.CharField(max_length=32)
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes")
當(dāng)我們要查詢某個(gè)班級(jí)關(guān)聯(lián)的所有學(xué)生(反向查詢)時(shí)罩阵,我們會(huì)這么寫models.Classes.objects.first().student_set.all()
竿秆,
當(dāng)我們?cè)贔oreignKey字段中添加了參數(shù) related_name 后
class Student(models.Model):
name = models.CharField(max_length=32)
theclass = models.ForeignKey(to="Classes", related_name="students")
當(dāng)我們要查詢某個(gè)班級(jí)關(guān)聯(lián)的所有學(xué)生(反向查詢)時(shí)启摄,我們會(huì)這么寫models.Classes.objects.first().students.all()
?related_query_name?反向查詢操作時(shí),使用的連接前綴幽钢,用于替換表名
?on_delete?當(dāng)刪除關(guān)聯(lián)表中的數(shù)據(jù)時(shí)歉备,當(dāng)前表與其關(guān)聯(lián)的行為,即外鍵的刪除
on_delete屬性:
1匪燕、常見(jiàn)的使用方式(設(shè)置為null) >>>?on_delete=models.SET_NULL
2蕾羊、關(guān)于別的屬性的介紹
?models.CASCADE:?這就是默認(rèn)的選項(xiàng),級(jí)聯(lián)刪除帽驯,你無(wú)需顯性指定它龟再,刪除關(guān)聯(lián)數(shù)據(jù),與之關(guān)聯(lián)也刪除尼变。
?models.PROTECT:?保護(hù)模式利凑,如果采用該選項(xiàng)浆劲,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候,會(huì)拋出ProtectedError錯(cuò)誤哀澈。
?models.SET_NULL:?置空模式牌借,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候,外鍵字段被設(shè)置為空割按,前提就是blank=True, null=True,定義該字段的時(shí)候膨报,允許為空。
?models.SET_DEFAULT:?置默認(rèn)值适荣,刪除關(guān)聯(lián)數(shù)據(jù)的時(shí)候现柠,外鍵字段設(shè)置為默認(rèn)值,所以定義外鍵的時(shí)候注意加上一個(gè)默認(rèn)值束凑。
?models.DO_NOTHING:?刪除關(guān)聯(lián)數(shù)據(jù)晒旅,引發(fā)錯(cuò)誤IntegrityError。
?models.SET:?刪除關(guān)聯(lián)數(shù)據(jù)
??a.與之關(guān)聯(lián)的值設(shè)置為指定值汪诉,設(shè)置:models.SET(值)
??b.與之關(guān)聯(lián)的值設(shè)置為可執(zhí)行對(duì)象的返回值废恋,設(shè)置:models.SET(可執(zhí)行對(duì)象)
def func():
return 10
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id",
on_delete=models.SET(func)
)
?db_constraint?是否在數(shù)據(jù)庫(kù)中創(chuàng)建外鍵約束扒寄,默認(rèn)為True
??OneToOneField
一對(duì)一字段
通常一對(duì)一字段用來(lái)擴(kuò)展已有字段
- 字段參數(shù)
?to?設(shè)置要關(guān)聯(lián)的表
?to_field?設(shè)置要關(guān)聯(lián)的字段
?on_delete?同F(xiàn)oreign字段
??ManyToManyField
??用于表示多對(duì)多的關(guān)聯(lián)關(guān)系鱼鼓,在數(shù)據(jù)庫(kù)中通過(guò)第三張表來(lái)建立關(guān)聯(lián)關(guān)系。
- 字段參數(shù)
?to?設(shè)置要關(guān)聯(lián)的表
?related_name?同F(xiàn)oreignKey字段
?related_query_name?同F(xiàn)oreignKey字段
?symmetrical?僅用于多對(duì)多自關(guān)聯(lián)時(shí)该编,指定內(nèi)部是否創(chuàng)建反向操作的字段迄本,默認(rèn)為True。
??舉個(gè)例子??
class Person(models.Model):
name = models.CharField(max_length=16)
friends = models.ManyToManyField("self")
??此時(shí)person對(duì)象就沒(méi)有person_set屬性
class Person(models.Model):
name = models.CharField(max_length=16)
friends = models.ManyToManyField("self", symmetrical=False)
?through?在使用ManyToManyField字段時(shí)课竣,Django將自動(dòng)生成一張表來(lái)管理多對(duì)多的關(guān)聯(lián)關(guān)系嘉赎。但我們也可以手動(dòng)創(chuàng)建第三張表來(lái)管理多對(duì)多關(guān)系,此時(shí)就需要通過(guò)through來(lái)指定第三張表的表名于樟。
?through_fields?設(shè)置關(guān)聯(lián)字段
?db_table?默認(rèn)創(chuàng)建第三張表時(shí)公条,數(shù)據(jù)庫(kù)中表的名稱
??元信息
??ORM對(duì)應(yīng)的類里面包含另一個(gè)Meta類,而Meta類封裝了一些數(shù)據(jù)庫(kù)的信息迂曲。主要字段如下:
- db_table?ORM在數(shù)據(jù)庫(kù)中的表名默認(rèn)是app_類名靶橱,可以通過(guò)db_table重寫表名
- index_together?聯(lián)合索引
- ordering?指定默認(rèn)按什么字段排序。只有設(shè)置了該屬性路捧,我們查詢到的結(jié)果才可以被reverse()
三关霸、單表操作
1、前言
??orm使用方式:orm操作可以使用類實(shí)例化杰扫,obj.save的方式队寇,也可以使用create( )的形式。
QuerySet數(shù)據(jù)類型介紹
???只要是queryset對(duì)象就可以無(wú)限的點(diǎn)queryset方法章姓,比如```models.User.object.filter( ).filter( ).count( )
?QuerySet與惰性機(jī)制
?所謂惰性機(jī)制:publish.obj,all( )或者.filter( )等都只是返回了一個(gè)QuerySet(查詢結(jié)果集對(duì)象)佳遣,它并不會(huì)馬上執(zhí)行sql炭序,而是當(dāng)調(diào)用QuerySet的時(shí)候才執(zhí)行。
?QuerySet特點(diǎn):可迭代的苍日;可切片惭聂;惰性計(jì)算和緩存機(jī)制
def queryset(request):
books=models.Book.objects.all()[:10] #切片 應(yīng)用分頁(yè)
books = models.Book.objects.all()[::2]
book= models.Book.objects.all()[6] #索引
print(book.title)
for obj in books: #可迭代
print(obj.title)
books=models.Book.objects.all(). #惰性計(jì)算--->等于一個(gè)生成器,不應(yīng)用books不會(huì)執(zhí)行任何SQL操作
# query_set緩存機(jī)制1次數(shù)據(jù)庫(kù)查詢結(jié)果query_set都會(huì)對(duì)應(yīng)一塊緩存相恃,再次使用該query_set時(shí)辜纲,不會(huì)發(fā)生新的SQL操作;
#這樣減小了頻繁操作數(shù)據(jù)庫(kù)給數(shù)據(jù)庫(kù)帶來(lái)的壓力;
authors=models.Author.objects.all()
for author in authors:
print(author.name)
print('-------------------------------------')
models.Author.objects.filter(id=1).update(name='陳某')
for author in authors:
print(author.name)
#但是有時(shí)候取出來(lái)的數(shù)據(jù)量太大會(huì)撐爆緩存拦耐,可以使用迭代器優(yōu)雅得解決這個(gè)問(wèn)題耕腾;
models.Publish.objects.all().iterator()
return HttpResponse('OK')
拿出前面準(zhǔn)備好的表:
from django.db import models
class Book(models.Model):
id=models.AutoField(primary_key=True)
title=models.CharField(max_length=32)
state=models.BooleanField()
pub_date=models.DateField()
price=models.DecimalField(max_digits=8,decimal_places=2)
publish=models.CharField(max_length=32)
?? 接下來(lái)使用單獨(dú)的py文件測(cè)試ORM操作,在test.py中需要配置的參數(shù)如下:
import os
import sys
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "test.settings")
import django
django.setup()
from app import models ??這句話必須在下面
2杀糯、添加表記錄(新增數(shù)據(jù)的三種方式)
-
方法一
基于create創(chuàng)建(create)
表.objects.create()
??create方法的返回值book_obj就是插入book表中python葵花寶典這本書(shū)籍記錄對(duì)象
book_obj = Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社"扫俺,pub_date="2019-12-12")
-
方法二
基于對(duì)象的綁定方法創(chuàng)建(save)也就是類實(shí)例化
obj=類(屬性=xx)?obj.save()
book_obj = Book(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date="2012-12-12")
book_obj.save()
-
方法三
自動(dòng)識(shí)別時(shí)間格式字符串進(jìn)行傳參
from datetime import datetime
ctime = datetime.now()
models.Book.objects.create(title="python葵花寶典",state=True,price=100,publish="蘋果出版社",pub_date=ctime)
3、查詢表記錄
- 基本查詢
查詢API | 作用 |
---|---|
all( ) | 查詢所有結(jié)果 |
filter(**kwargs) | 包含了與所給篩選條件相匹配的對(duì)象 |
get(**kwargs) | 返回與所給篩選條件相匹配的對(duì)象固翰,返回結(jié)果有且只有一個(gè)狼纬,如果符合篩選條件的對(duì)象超過(guò)一個(gè)或者沒(méi)有都會(huì)拋出錯(cuò)誤 |
exclude(**kwargs) | 包含了與所給篩選條件不匹配的對(duì)象 |
order_by(*field) | 對(duì)查詢結(jié)果排序 |
reverse( ) | 對(duì)查詢結(jié)果反向排序,前面要先有排序才能反向 |
count( ) | 返回?cái)?shù)據(jù)庫(kù)中匹配查詢(QuerySet)的對(duì)象數(shù)量 |
first( ) | 返回第一條記錄 |
last( ) | 返回最后一條記錄 |
exists( ) | 如果QuerySet包含數(shù)據(jù)骂际,就返回True疗琉,否則返回False |
values(*field) | 返回一個(gè)ValueQuerySet—一個(gè)特殊的QuerySet,運(yùn)行后得到的并不是一系列model的實(shí)例化對(duì)象歉铝,而是一個(gè)可迭代的字典序列 |
values_list(*field) | 它與values( )非常相似盈简,它返回的是一個(gè)元組序列,values返回的是一個(gè)字典序列 |
distinct( ) | 從返回結(jié)果中剔除重復(fù)記錄,去重的對(duì)象必須是完全相同的數(shù)據(jù)才能去重 |
例子們??:
?? filter(**kwargs):
models.User.objects.filter(name='wpr',age=18) ——查出來(lái)是<QuerySet []>
models.User.objects.filter(name='wpr',age=23) ——查出來(lái)是<QuerySet [<User: User object>, <User: User object>]>
????:filter里面可以放多個(gè)限制條件太示,但多個(gè)條件之間是and
?? exclude(**kwargs):
models.User.objects.exclude(name='wpr')
?? order_by(*field):
models.User.objects.order_by('-age') ——可以在排序的字段前面加一個(gè)減號(hào)柠贤,就是降序
models.User.objects.order_by('age') ——默認(rèn)是升序
?? reverse():
models.User.objects.order_by('age').reverse() ——要先有排序才能反向
?? count():
models.User.objects.count()
models.User.objects.all().count()
?? first():
models.User.objects.all().first()
models.User.objects.all()[0] ——不支持負(fù)數(shù)的索引取值
?? last():
models.User.objects.all().last()
?? exists():
models.User.objects.all().exists()
models.User.objects.filter(name='wpr',age=3).exists()
?? values(*field):
models.User.objects.values('name') ——??列表套字典[{},{},{}]
?? values_list(*field):
models.User.objects.value_list('name','age') ——??列表套元組[(),(),()]
?? distinct():
models.User.objects.values('name','age').distinct() ——??去重的對(duì)象必須是完全相同的數(shù)據(jù)才能去重
必須完全一樣才可以去重(意味著帶了id就沒(méi)有意義了)
models.Book.objects.all().values('name').distinct() ——先查一個(gè)重復(fù)的值再去重
- 基于雙下劃線的模糊查詢
------- 數(shù)字查詢 -------
查詢價(jià)格為100或200或300的書(shū)籍 models.Book.objects.filter(price__in=[100,200,300])
查詢年齡大于44歲的用戶 models.User.objects.filter(age__gt=44)
查詢價(jià)格小于100的書(shū)籍 models.Book.objects.filter(price__lt=100)
查詢年齡大于等于44的用戶 models.User.objects.filter(age__gte=44)
查詢年齡小于等于44的用戶 models.User.objects.filter(age__lte=44)
查詢價(jià)格再100到200之間的書(shū)籍 models.Book.objects.filter(price__range=[100,200])
查詢年齡是否為空(0:is not null | 1:is null) models.User.objects.filter(age__isnull=0|1)
------- 字符串查詢 -------
查詢名字中包含字母n的用戶 models.User.objects.filter(name__contains='n')
查詢名字中包含字母n的用戶并且忽略大小寫 models.User.objects.filter(name__icontains='n')
查詢書(shū)籍中以py開(kāi)頭的書(shū)籍 models.Book.objects.filter(title__startswith='py')
查詢書(shū)籍中以n結(jié)尾的用戶 models.User.objects.filter(name__endswith='n')
查詢名字滿足某個(gè)正則表達(dá)式的用戶 models.User.objects.filter(name__regex='正則表達(dá)式')
------- 日期 -------
查詢注冊(cè)時(shí)間是在2017年的用戶 models.User.objects.filter(register_time__year=2017)
?? tips:返回queryset對(duì)象的方法
??all ( )
??filter ( )
??exclude ( )
??order_by ( )
??reverse ( )
??distinct ( )
??values ( )?返回一個(gè)可迭代的字典序列
??values_list ( )?返回一個(gè)可迭代的元祖序列
4、刪除表記錄
- 基于對(duì)象
user_obj = models.User.objects.filter(name='json').first()
user_obj.delete()
??刪除方法就是delete(),它運(yùn)行時(shí)立即刪除對(duì)象而不返回任何值类缤,例如user_obj.delete()
臼勉。
- 基于queryset
models.User.objects.filter(name='wpr').delete()
??一次性刪除多個(gè)對(duì)象,每個(gè)QuerySet都有一個(gè)delete()方法呀非,它一次性刪除QuerySet中所有的對(duì)象坚俗。例如镜盯,下面的代碼將刪除pub_date是2005年的Entry對(duì)象:
Entry.objects.filter(pub_date__year=2005).delete()
在Django刪除對(duì)象時(shí)岸裙,會(huì)模仿SQL約束ON DELETE CASCADE的行為,換句話說(shuō)速缆,刪除一個(gè)對(duì)象時(shí)也會(huì)刪除與他相關(guān)聯(lián)的外鍵對(duì)象降允,例如:
b = Blog.objects.get(pk=1)
# This will delete the Blog and all of its Entry objects.
b.delete()
要注意的是:delete()方法是QuerySet上的方法,但并不適用于Manager本身艺糜。這是一種保護(hù)機(jī)制剧董,是為了避免意外地調(diào)用Entry.objects.delete()方法導(dǎo)致所有的記錄被誤刪除幢尚。如果你確認(rèn)要?jiǎng)h除所有的對(duì)象,那么你必須顯式地調(diào)用Entry.objects.all().delete()
如果不想級(jí)聯(lián)刪除翅楼,可以設(shè)置為:
pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)
5尉剩、修改表記錄
- 基于對(duì)象,先獲取用戶對(duì)象毅臊,然后賦值理茎,最后保存
user_obj = models.User.objects.filter(name='wpr').first()
user_obj.age = 17
user_obj.save()
-
基于queryset,queryset直接更新
??update()方法對(duì)于任何結(jié)果集(QuerySet)均有效,這意味著你可以同時(shí)更新多條記錄管嬉,update()方法會(huì)返回一個(gè)整型值皂林,表示受影響的記錄的條數(shù)。
models.User.objects.filter(name='wpr').update(age=77)
三蚯撩、多表操作
1础倍、Django多表ORM設(shè)計(jì)規(guī)則
(1)關(guān)聯(lián)的表之間建議建立外鍵,但可以取消關(guān)聯(lián)關(guān)系(db_constraint=False)
(2)關(guān)聯(lián)表之間的外鍵字段建議采用對(duì)應(yīng)類名的全小寫
(3) 采用關(guān)聯(lián)表的主鍵或?qū)ο缶苓M(jìn)行操作
2胎挎、表與表之間的關(guān)系
一對(duì)一(OneToOneField):一對(duì)一字段無(wú)論建在哪張關(guān)系表里都可以沟启,但是推薦建在查詢頻率比較高的那張表中
一對(duì)多(ForeignKey):一對(duì)多字段建在多的那一方
多對(duì)多(ManyToManyField):多對(duì)多字段無(wú)論建在哪張關(guān)系表里都可以,但是推薦建在查詢頻率比較高的那張表中
多對(duì)多:add() 添加??|??set() 修改??|??remove() 不能接收可迭代對(duì)象??|??clear() 清空犹菇,不用傳參外鍵關(guān)系
publish = models.ForeignKey(to='Publish',to_field='title')
to?是設(shè)置要關(guān)聯(lián)的表
to_field?是設(shè)置要關(guān)聯(lián)的表的字段美浦,不設(shè)置默認(rèn)關(guān)聯(lián)id
on_delete?是當(dāng)刪除關(guān)聯(lián)表中的數(shù)據(jù)時(shí),當(dāng)前表與其關(guān)聯(lián)的行的行為项栏。在django2.0中要加models.CASCADE
db_constraint?是否在數(shù)據(jù)庫(kù)中創(chuàng)建外鍵約束浦辨,默認(rèn)為True
3、創(chuàng)建模型
【作者Author】:一個(gè)作者有姓名沼沈,作者詳情和作者之間是一對(duì)一關(guān)系(one-to-one)
【作者詳情AuthorDetail】:包含年齡流酬、電話和信息
【出版社Publish】:出版商有名稱,地址
【書(shū)籍Book】:書(shū)籍有書(shū)名列另、價(jià)格和出版日期芽腾,一本書(shū)可能會(huì)有多個(gè)作者,一個(gè)作者也可以寫多本書(shū)页衙,所以作者和書(shū)籍的關(guān)系就是多對(duì)多的關(guān)聯(lián)關(guān)系(many-to-many)摊滔;一本書(shū)只應(yīng)該由一個(gè)出版商出版,所以出版商和書(shū)籍是一對(duì)多關(guān)聯(lián)關(guān)系(one-to-many)
from django.db import models
class Author(models.Model):
name=models.CharField( max_length=32)
# 與AuthorDetail建立一對(duì)一的關(guān)系
author_detail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
class AuthorDetail(models.Model):
age=models.IntegerField()
telephone=models.BigIntegerField()
info=models.CharField( max_length=64)
class Publish(models.Model):
name=models.CharField( max_length=32)
address=models.CharField( max_length=32)
class Book(models.Model):
name = models.CharField( max_length=32)
price=models.DecimalField(max_digits=5,decimal_places=2)
publish_date=models.DateField()
# 與Publish建立一對(duì)多的關(guān)系,外鍵字段建立在多的一方
publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
# 與Author表建立多對(duì)多的關(guān)系,ManyToManyField可以建在兩個(gè)模型中的任意一個(gè)店乐,自動(dòng)創(chuàng)建第三張表
authors=models.ManyToManyField(to='Author',)
4艰躺、一對(duì)多關(guān)系
-
規(guī)則
(1)關(guān)系中“多”依賴于“一”;
(2)Django 1.x外鍵關(guān)聯(lián)默認(rèn)有級(jí)聯(lián)刪除眨八,2.x需要手動(dòng)明確外鍵的級(jí)聯(lián)刪除(on_delete=models.CASCADE)
① 增
??方式一:外鍵為關(guān)聯(lián)對(duì)象,這里外鍵是實(shí)際表的字段
先有出版社离钝,才有書(shū)籍
publish = Publish.objects.create(name="南方出版社",address="上海)
Book.objects.create(name='人間失格',price=88.88,publish_date='2018-8-8',publish=publish)
?? 方式二:外鍵字段為關(guān)聯(lián)對(duì)象主鍵彭沼,這里外鍵不是實(shí)際表的字段
id = Publish.objects.create(name="北方出版社", address="北京").id
Book.objects.create(name='活著', price=37.70, publish_date='2043-8-8', publish_id=id)
② 刪
刪除出版社章贞,默認(rèn)有級(jí)聯(lián)刪除,出版社出版的數(shù)據(jù)全會(huì)被刪除
Publish.objects.first().delete()
③ 改
??方式一:save()
book_obj=models.Book.objects.filter(pk=1).first()
book_obj.publish=new_publish_obj
book_obj.save()
??方式二:update()
models.Book.objects.filter(pk=1).update(publish_id=2)
或 models.Book.objects.filter(pk=1).update(publish_id=publish_obj)
5篓足、一對(duì)一關(guān)系
-
規(guī)則
通過(guò)外鍵所在表決定依賴關(guān)系
① 增
??遵循操作順序
author_detail = AuthorDetail.objects.create(age=18,telephone=1232331231,ingo="真帥)
Author.objects.create(name='wpr',author_detail=author_detail)
② 刪
??擁有級(jí)聯(lián)刪除
AuthorDetail.objects.first().delete()
③ 改
??一般不考慮該關(guān)聯(lián)字段
6、多對(duì)多關(guān)系
-
規(guī)則
(1) 多對(duì)多關(guān)系存在關(guān)系表闰蚕,關(guān)系表建議采用ManyToManyField字段處理
(2) 需要手動(dòng)創(chuàng)建關(guān)系表時(shí)栈拖,在字段中明確through與through_field值
① 增 add( )
為書(shū)籍添加作者的主鍵或?qū)ο髠?book.author.add(*args)
② 刪 remove( )
刪除書(shū)籍已有作者的主鍵或?qū)ο髠?book.author.remove(*args)
③ 改 set( ) | 清空clear( )
清空并添加作者的主鍵或?qū)ο?設(shè)置作者的主鍵或?qū)ο笮问降牧斜?book.authoe.clear()
book.author.set([*args])
??tips:add(),set()没陡,remove()都可以支持傳多個(gè)數(shù)字或?qū)ο笕杩瑂et必須接收一個(gè)可迭代對(duì)象
7、跨表查詢規(guī)則
正向反向的概念
關(guān)聯(lián)字段在你當(dāng)前這張表查詢另一張表叫正向
關(guān)聯(lián)字段不在你當(dāng)前這張表查詢另一張表叫反向正向查詢按字段名進(jìn)行跨表查詢
反向查詢按表名小寫進(jìn)行跨表查詢
8诗鸭、基于對(duì)象的跨表查詢(子查詢)
??在跨表查詢的規(guī)則上染簇,跨表查詢的結(jié)果為多條數(shù)據(jù)時(shí)需要在字段后添加_set;還有如果返回的是None,只要加個(gè)all( )强岸。
一對(duì)一查詢
(1) 查詢得到作者對(duì)象author_obj=Author.object.first()
(2) 基于作者對(duì)象跨表獲取作者詳情對(duì)象锻弓,正向通過(guò)字段名author_detailauthor_detail_obj=author_obj.author_detail
(3) 基于作者詳情對(duì)象跨表獲取作者對(duì)象,反向通過(guò)表名小寫authorauthor_obj=author_detail_obj.author
一對(duì)多查詢
(1) 查詢得到書(shū)籍對(duì)象book_obj=Book.objects.first()
(2) 獲取出版社對(duì)象蝌箍,正向通過(guò)字段名publishpublish_obj=book_obj.publish
(3) 獲取書(shū)籍對(duì)象們青灼,反向通過(guò)表名小寫book,多條數(shù)據(jù)添加_set,如果返回的結(jié)果是None,加個(gè)all()book_list_obj=publish_obj.book_set.all()
多對(duì)多查詢
(1) 查詢得到書(shū)籍對(duì)象book_obj=Book.objects.first()
(2) 獲取作者對(duì)象們,正向通過(guò)字段名authorauthor_list_obj=book_obj.author
(3) 基于作者對(duì)象獲取書(shū)籍對(duì)象們妓盲,反向通過(guò)表名小寫book杂拨,多條數(shù)據(jù)添加_set
author = Author.objects.first()
book_list_obj=author.book_set
????tips:如果在ForeignKey()和ManyToManyField的定義中設(shè)置related_name的值來(lái)復(fù)寫FOO_set的名稱。
例如:在Article model中做一下更改
publish = ForeignKey(Book,related_name='bookList')
那么在“查詢?nèi)嗣癯霭嫔绯霭孢^(guò)的所有書(shū)籍”時(shí)結(jié)果如下:
publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() ??與人民出版社關(guān)聯(lián)的所有書(shū)籍對(duì)象集合悯衬,這里使用all代替了_set
- 多級(jí)跨表查詢
某作者出版的第一本書(shū)的出版社名字
author.book_set.first().publish.name
9弹沽、基于雙下劃線的跨表查詢(聯(lián)表查詢)
??Django還提供了一種直觀而高效的方式在查詢(lookups)中表示關(guān)聯(lián)關(guān)系,他能自動(dòng)確認(rèn)SQL JOIN聯(lián)系筋粗。要做跨關(guān)系查詢策橘,就使用兩個(gè)下劃線來(lái)連接模型(model)間關(guān)聯(lián)字段的名稱,直到最終連接到想要的model為止娜亿。
??正向查詢按字段丽已,反向查詢按表名小寫用來(lái)告訴ORM引擎join哪張表。
??滿足跨表查詢規(guī)則买决。
??filter方法與values方法支持__查詢規(guī)則沛婴。
- 一對(duì)多查詢
(1) 正向查詢,按字段名:publish
queryResult=Book.objects.filter(publish__name="南方出版社").values_list("title","price")
??(2)反向查詢督赤,按表名小寫:book
queryResult=Publish.objects.filter(name="北方出版社").values_list("book__title","book__price")
- 多對(duì)多查詢
(1) 正向查詢嘁灯,按字段名:authors
queryResult=Book.objects.filter(authors__name="wpr").values_list("title)
??(2)反向查詢,按表名小寫:book
queryResult=Author.objects.filter(name="wpr").values_list("book_list","book_price")
- 一對(duì)一查詢
(1) 正向查詢
Author.objects.filter(name="wpr").values("author detail__telephone")
??(2) 反向查詢
AuthorDetail.objects.filter(author__name="wpr").values("telephone")
- 兩表關(guān)聯(lián)
查詢所有小于18歲作者的名字與實(shí)際年齡
authors_dic=Author.objects.filter(author_detail__id__gt=0).values('name','author_detail__age')
- 多表關(guān)聯(lián)(連續(xù)跨表)
查詢出版社在上海的出版過(guò)的所有書(shū)的作者姓名够挂、作者電話旁仿、具體出版社名的相關(guān)信息
info_dic=Book.objects.filter(publish__address__contains="上海").value('author__name','author__author_detail__telephone','publish__name')
queryset對(duì)象.query能夠查看內(nèi)部對(duì)應(yīng)的sql語(yǔ)句
??all()
??filter()
??values()
??value_list()
??order_by()
??reverse()
??distinct()
??exclude()?取反
??count()?計(jì)數(shù)
??exists()?布爾值
??get()?數(shù)據(jù)對(duì)象本身
??first()
??last()
?? related_name
反向查詢時(shí)藕夫,如果定義了related_name孽糖,則用related_name替換表名枯冈,例如 publish=ForeignKey(Blog,related_name='bookList')
那么在“查詢?nèi)嗣癯霭嫔绯霭孢^(guò)的所有書(shū)籍的名字與價(jià)格(一對(duì)多)”,反向查詢办悟,不再按表名:book,而是related_name:bookList
queryResult=Publish.objects.filter(name="人民出版社").values_list("boolList__title","bookList__price")
??tips:配置文件配置參數(shù)查看所有orm操作內(nèi)部的sql語(yǔ)句
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
10尘奏、聚合查詢與分組查詢
(1)聚合(aggregate)
??from django.db.models import Max,Min,Sum,Count,Avg
??aggregate(*args,**kwargs)
舉個(gè)例子??:計(jì)算所有圖書(shū)的平均價(jià)格
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
>>> {'price__avg': 34.35} # 自動(dòng)生成
aggregate()是QuerySet的一個(gè)終止子句,意思是說(shuō)病蛉,它返回一個(gè)包含一些鍵值對(duì)的字典炫加。鍵的名字是聚合值的標(biāo)識(shí)符,值是計(jì)算出來(lái)的聚合值铺然。鍵的名字是按照字段和聚合函數(shù)的名稱自動(dòng)生成出來(lái)的俗孝。如果想要為聚合值指定一個(gè)名稱,可以向聚合子句提供它魄健。
Book.objects.aggregate(average_price=Avg('price'))
>>> {'average_price':34.35}
如果想生成不止一個(gè)聚合赋铝,你可以想aggregate()子句中添加另一個(gè)參數(shù),所以如果想知道所有圖書(shū)價(jià)格的最大值和最小值沽瘦,可以這樣查詢:
from django.db.models import Avg, Max, Min
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
>>> {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
(2)分組(group by)
??annotate():依據(jù)前面的基表
??group_concat():特地用在分組
??concat():所有分組
??tips:字符串拼接要用到concat和value
- 單表分組查詢
emp表:
id | name | age | salary | depends |
---|---|---|---|---|
1 | wpr | 27 | 1000 | 銷售部 |
2 | wpp | 30 | 3000 | 人事部 |
3 | www | all( ) | 5000 | 人事部 |
查詢每一個(gè)部門名稱以及對(duì)應(yīng)的員工數(shù)
sql語(yǔ)句:select dep,Count(*) from emp group by dep;
ORM語(yǔ)句:emp.objects.values("dep").annotate(c=Count("id")
- 多表分組查詢
emp表:
id | name | age | salary | dep_id |
---|---|---|---|---|
1 | wpr | 27 | 1000 | 1 |
2 | wpp | 30 | 3000 | 2 |
3 | www | all( ) | 5000 | 2 |
dep表:
id | name |
---|---|
1 | 銷售部 |
2 | 人事部 |
emp-dep表:
id | name | age | salary | dep_id | id | name |
---|---|---|---|---|---|---|
1 | wpr | 27 | 1000 | 1 | 1 | 銷售部 |
2 | wpp | 30 | 3000 | 2 | 2 | 人事部 |
3 | www | all( ) | 5000 | 2 | 2 | 人事部 |
查詢每一個(gè)部門名稱以及對(duì)應(yīng)的員工數(shù)
sql語(yǔ)句:select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id
ORM語(yǔ)句dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
class Emp(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField()
salary=models.DecimalField(max_digits=8,decimal_places=2)
dep=models.CharField(max_length=32)
province=models.CharField(max_length=32)
??annotate()為調(diào)用的QuerySet中每一個(gè)對(duì)象都生成一個(gè)獨(dú)立的統(tǒng)計(jì)值(統(tǒng)計(jì)方法用聚合函數(shù))革骨。
總結(jié):跨表分組查詢本質(zhì)就是將關(guān)聯(lián)表join成一張表,再按單表的思路進(jìn)行分組查詢析恋。
11良哲、F查詢與Q查詢
from django.db.models import F,Q
(1)F查詢
??對(duì)兩個(gè)字段的值做比較,Django提供F()來(lái)做這樣的比較助隧。F()的實(shí)例可以在查詢中引用字段筑凫,來(lái)比較同一個(gè)model實(shí)例中兩個(gè)不同字段的值。
??F可以取到表中某個(gè)字段對(duì)應(yīng)的值來(lái)當(dāng)作我的篩選條件并村,而不是自定義常量的條件漏健,實(shí)現(xiàn)了動(dòng)態(tài)比較的效果。
??Django支持F()對(duì)象之間以及F()對(duì)象和常數(shù)之間的加減乘除和取模的操作橘霎。
?? 比較兩個(gè)不同字段的值
查詢?cè)u(píng)論數(shù)大于收藏?cái)?shù)的書(shū)籍
from django.db.models import F
Book.objects.filter(commnetNum__lt=F('keepNum'))
?? 取到某個(gè)字段的值當(dāng)作篩選條件
字符串拼接要用到 concat和value
將所有商品的名字后面加一個(gè)爆款后綴
from django.db.models.functions import Concat
from django.db.models import Value
models.Product.objects.update(name=Concat(F('name'),Value('爆款')))
?? 通過(guò)字段名獲取可以直接做運(yùn)算的查詢結(jié)果
from Django.db.models import F
案例一:將id為1的結(jié)果年齡增加1
models.User.objects.filter(id=1).update(age=F('age')+1)
案例二:查詢id是年齡1/4的結(jié)果
models.User.objects.filter(id=F('age')/4)
(2)Q查詢
- filter()等方法中的關(guān)鍵字參數(shù)查詢都是一起進(jìn)行'AND'與的關(guān)系蔫浆,如果要執(zhí)行更復(fù)雜的查詢(例如or或者not)的關(guān)系查詢數(shù)據(jù),可以使用Q對(duì)象姐叁。
?? 默認(rèn)是and關(guān)系
models.Product.objects.filter(Q(name='變形金剛'),Q(price=999.999))
?? 與普通過(guò)濾條件混合使用瓦盛,完成邏輯運(yùn)算方式的查詢
from Django.db.models import Q
與[&] models.User.objects.filter(Q(id=1)&Q(age=10))
或[|] models.User.objects.filter(Q(id=1)|Q(id=2))
非[~] models.User.objects.filter(~Q(id=1))
當(dāng)一個(gè)操作符在兩個(gè)Q對(duì)象上使用時(shí),它產(chǎn)生一個(gè)新的Q對(duì)象
bookList=Book.objects.filter(Q(authors__name="wpr")|Q(authors__name="wpp"))
等同于下面的SQL WHERE子句
where name="wpr" OR name="wpp"
同時(shí)外潜,Q 對(duì)象可以使用~ 操作符取反原环,這允許組合正常的查詢和取反(NOT) 查詢:
bookList=Book.objects.filter(Q(authors__name="wpr") & ~Q(publishDate__year=2017)).values_list("title")
- 查詢函數(shù)可以混合使用Q對(duì)象和關(guān)鍵字參數(shù),所有提供給查詢函數(shù)的參數(shù)(關(guān)鍵字參數(shù)或Q對(duì)象)都將'AND'在一起处窥,但是嘱吗,如果出現(xiàn)Q對(duì)象,它必須位于所有關(guān)鍵字參數(shù)的前面,例如:
bookList=Book.objects.filter(Q(publishDate__year=2019) | Q(publishDate__year=2020),title__icontains="python" )
- Q查詢進(jìn)階操作——可以傳入字符串
先實(shí)例化一個(gè)Q對(duì)象
q=Q()
q.connector='or'
q.children.append(('name','wpr'))
Q對(duì)象支持直接放在filter括號(hào)內(nèi)
models.User.objects.filter(q) q對(duì)象默認(rèn)也是and關(guān)系
12谒麦、事務(wù)(上下文管理)
??定義:將多個(gè)sql語(yǔ)句操作變成原子性操作俄讹,要么同時(shí)成功,有一個(gè)失敗則里面回滾到原來(lái)的狀態(tài)绕德,保證數(shù)據(jù)的完整性和一致性(NoSQL數(shù)據(jù)庫(kù)對(duì)于事務(wù)則是部分支持)
??事務(wù)的ACID:A(原子性)?C(一致性)?I(隔離性)?D(持久性)
from django.db import transaction
with transaction.atomic():
這里寫多個(gè)數(shù)據(jù)庫(kù)操作
print('其他邏輯代碼')
13患膛、淺談ORM查詢性能
- 普通查詢
obj_list=models.Love.objects.all()
for row in obj_list: #for循環(huán)10次發(fā)送10次數(shù)據(jù)庫(kù)查詢請(qǐng)求
print(row.b.name)
這種查詢方式第一次發(fā)送查詢請(qǐng)求,每for循環(huán)一次也會(huì)發(fā)送查詢請(qǐng)求耻蛇,頻繁進(jìn)行IO操作踪蹬。
-
select_related:結(jié)果為對(duì)象,注意query_set類型的對(duì)象都有該方法
原理:查詢時(shí)主動(dòng)完成連表形成一張大表臣咖,for循環(huán)時(shí)不用額外發(fā)請(qǐng)求跃捣。
應(yīng)用場(chǎng)景:節(jié)省硬盤空間,數(shù)據(jù)量少的時(shí)候使用夺蛇,相當(dāng)于只做了一次數(shù)據(jù)庫(kù)查詢
obj_list=models.Love.objects.all().select_related('b')
for row in obj_list:
print(row.b.name)
- prefetch_related:結(jié)果都是對(duì)象
原理:select_related雖然好枝缔,但是做連表操作依然會(huì)影響查詢性能,所以出現(xiàn)了prefetch_related蚊惯,prefetch_related不做連表查詢愿卸,多次單表查詢外鍵表,去重之后顯示截型,2次單表查詢(有幾個(gè)外鍵做1+N次單表查詢)趴荸。
應(yīng)用場(chǎng)景:效率高,數(shù)據(jù)量大的時(shí)候適用
obj_list=models.Love.objects.all().prefetch_related('b')
for obj in obj_list:
print(obj.b.name)
- update()和對(duì)象.save()修改方式的性能比較
update():
models.Book.objects.filter(id=1).update(price=3)
??執(zhí)行結(jié)果:
(0.000) BEGIN; args=None
(0.000) UPDATE "app01_book" SET "price" = '3.000' WHERE "app01_book"."id" = 1; args=('3.000', 1)
(0.000) SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", "app01_book"."date", "app01_book"."publish_id", "app01_book"."classify_id" FROM "app01_book" WHERE "app01_book"."id" = 1; args=(1,)
??save():
book_obj=models.Book.objects.get(id=1)
book_obj.price=5
book_obj.save()
??執(zhí)行結(jié)果:
(0.000) BEGIN; args=None
(0.000) UPDATE "app01_book" SET "title" = '人間失格', "price" = '5.000', "date" = '1370-09-09', "publish_id" = 4, "classify_id" = 3 WHERE "app01_book"."id" = 1; args=('人間失格', '5.000', '1370-09-09', 4, 3, 1)
總結(jié):update()比obj.save()性能好宦焦。