Django-ORM操作
一、orm概述
1 orm:物件關係對映(跟語言無關) 資料庫中的表 ----》對應程式的一個類 資料庫中的一行資料----》對應程式中的一個物件 2 python中常見orm框架 -django的orm框架 -sqlachemy的orm框架 3 java:(擴充套件),java中寫web專案 ssh框架 :spring+struts(有漏洞)+hibernate(orm框架)(很早的年代) ssm框架:spring+springmvc+mybatis(orm框架,可以寫原生sql) springboot:sb框架 ,tomcat內建進去了 springcloud:微服務 4 orm能幹的事 -建立表(不能建立資料庫,手動建立資料庫) -增加刪除表內欄位 -增刪查改資料
二、orm簡單使用
1.配置檔案
雖然使用orm對應與許多的資料庫,但是因為我們一般是使用mysql資料庫,所以需要在settings中配置,不然預設使用sqlite。若使用sqlite跳過這一步直接從第二步開始。
1. #settings.py DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'day62', 'HOST': '127.0.0.1', 'PORT': 3306, 'USER': 'root', 'PASSWORD':'123' # 下面兩個暫時不用加,也不用管 'ATOMIC_REQUEST': True, 'OPTIONS': { "init_command": "SET storage_engine=MyISAM", } } } 2.django預設情況連結mysql,用的驅動是mysqldb模組,python 3.x以後,這個模組用不了了,咱們用的全都是pymysql,需要做個替換 # app的__init__.py 中寫以下程式碼,其實隨便在哪寫,但是規範是在這寫 # 因為對於django專案,在執行的時候,所有檔案的程式碼都會執行,所以無所謂在哪寫,公司裡可能會換地方寫 import pymysql pymysql.install_as_MySQLdb()
2.利用orm建立表
# 第一步在models中寫要給類 class Book(models.Model): # 如果不寫id,會預設一個id,並且自增 # primary_key=True 表示該欄位是主鍵,一個表中只能有一個主鍵 id = models.AutoField(primary_key=True) # varchar型別,長度, # 欄位是否可以為空:null=True,可以為空 # 預設值:default='未知書名',如果沒傳,預設是它 # 設定索引:db_index=True 表示該欄位是輔助索引 # 是否唯一:unique=True 表示唯一 name=models.CharField(max_length=32,null=True,default='未知書名',db_index=True,unique=True) # float型別 # max_digits 最大長度是5 4567.5 # decimal_places=2 小數點後兩位 23.56 999.99 price=models.DecimalField(max_digits=5,decimal_places=2) # DateTimeField年月日時分秒 # auto_now=True 新增,預設使用當前時間 # auto_now_add=True 修改,設定當前時間,這兩者只能設定一種 publish_date=models.DateTimeField(auto_now=True) ''' auto_now無論是你新增還是修改物件,時間為你新增或者修改的時間。 auto_now_add為新增時的時間,更新物件時不會有變動。 ''' publish=models.CharField(max_length=32) # 第二步,把表創建出來(執行兩個命令) -python3 manage.py makemigrations # 這條命令會在migrations建立一條記錄,資料庫變更記錄 -python3 manage.py migrate # 把更改同步到資料庫
3.orm單表增加
from app01 import models
# 註冊功能
def register(request):
if request.method == 'GET':
return render(request, 'register.html')
elif request.method == 'POST':
# 獲取資料
res = request.POST
name = res.get('name')
password = res.get('password')
gender = res.get('gender')
province = res.get('province')
# 通過orm存到資料庫
# 方式一
user=models.UserInfo(name=name,password=password,gender=gender,province=province)
user.save()
# 方式二
user=models.UserInfo.objects.create(name=name,password=password,gender=gender,province=province)
# 註冊完成直接跳轉到使用者列表頁面
return redirect('/userlist')
4.orm單表查詢
# 檢視目前註冊的使用者
def user_list(request):
# 通過orm查詢所有使用者,返回列表,內部有一個一個的user物件 [user1,user2,user3]
userlist=models.UserInfo.objects.all()
return render(request, 'userlist.html',context={'userlist':userlist})
def select_user(request):
# 查詢名字叫xxx的人(是個列表:QuerySet)
res = models.UserInfo.objects.filter(name='xxx')
res = models.UserInfo.objects.filter(name='xxx')[0]
res = models.UserInfo.objects.filter(name='xxx').first()
# 利用get方法查詢名字叫xxx的人(UserInfo),如果沒有或者由多個,都報錯
# 查詢結果必須有且僅有一個才正常,否則報錯
res=models.Book.objects.get(name='xxx')
三、常用和非常用欄位和引數概覽
1.常用欄位
# 最為常用
IntegerField 整數
AutoField
BooleanField
CharField
DateField
DateTimeField
DecimalField
FileField 上傳檔案,本質是varchar
ImageField 圖片,本質是varchar,繼承了FileField
TextField 存大文字
EmailField 本質是varchar
# 詳細
AutoField(Field)
- int自增列,必須填入引數 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必須填入引數 primary_key=True
注:當model中如果沒有自增列,則自動會建立一個列名為id的列
from django.db import models
class UserInfo(models.Model):
# 自動建立一個列名為id的且為自增的整數列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定義自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整數 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整數 0 ~ 32767
IntegerField(Field)
- 整數列(有符號的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整數 0 ~ 2147483647
BigIntegerField(IntegerField):
- 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807
自定義無符號整數字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值為欄位在資料庫中的屬性,Django欄位預設的值為:
'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)',
BooleanField(Field)
- 布林值型別
NullBooleanField(Field):
- 可以為空的布林值
CharField(Field)
- 字元型別
- 必須提供max_length引數, max_length表示字元長度
TextField(Field)
- 文字型別
EmailField(CharField):
- 字串型別,Django Admin以及ModelForm中提供驗證機制
IPAddressField(Field)
- 字串型別,Django Admin以及ModelForm中提供驗證 IPV4 機制
GenericIPAddressField(Field)
- 字串型別,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
- 引數:
protocol,用於指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
unpack_ipv4, 如果指定為True,則輸入::ffff:192.0.2.1時候,可解析為192.0.2.1,開啟刺功能,需要protocol="both"
URLField(CharField)
- 字串型別,Django Admin以及ModelForm中提供驗證 URL
SlugField(CharField)
- 字串型別,Django Admin以及ModelForm中提供驗證支援 字母、數字、下劃線、連線符(減號)
CommaSeparatedIntegerField(CharField)
- 字串型別,格式必須為逗號分割的數字
UUIDField(Field)
- 字串型別,Django Admin以及ModelForm中提供對UUID格式的驗證
FilePathField(Field)
- 字串,Django Admin以及ModelForm中提供讀取資料夾下檔案的功能
- 引數:
path, 資料夾路徑
match=None, 正則匹配
recursive=False, 遞迴下面的資料夾
allow_files=True, 允許檔案
allow_folders=False, 允許資料夾
FileField(Field)
- 字串,路徑儲存在資料庫,檔案上傳到指定目錄
- 引數:
upload_to = "" 上傳檔案的儲存路徑
storage = None 儲存元件,預設django.core.files.storage.FileSystemStorage
ImageField(FileField)
- 字串,路徑儲存在資料庫,檔案上傳到指定目錄
- 引數:
upload_to = "" 上傳檔案的儲存路徑
storage = None 儲存元件,預設django.core.files.storage.FileSystemStorage
width_field=None, 上傳圖片的高度儲存的資料庫欄位名(字串)
height_field=None 上傳圖片的寬度儲存的資料庫欄位名(字串)
DateTimeField(DateField)
- 日期+時間格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
DateField(DateTimeCheckMixin, Field)
- 日期格式 YYYY-MM-DD
TimeField(DateTimeCheckMixin, Field)
- 時間格式 HH:MM[:ss[.uuuuuu]]
DurationField(Field)
- 長整數,時間間隔,資料庫中按照bigint儲存,ORM中獲取的值為datetime.timedelta型別
FloatField(Field)
- 浮點型
DecimalField(Field)
- 10進位制小數
- 引數:
max_digits,小數總長度
decimal_places,小數位長度
BinaryField(Field)
- 二進位制型別
2.常用引數
(1)null
如果為True,Django 將用NULL 來在資料庫中儲存空值。 預設值是 False.
(2)blank
如果為True,該欄位允許不填。預設為False。
要注意,這與 null 不同。null純粹是資料庫範疇的,而 blank 是資料驗證範疇的。
如果一個欄位的blank=True,表單的驗證將允許該欄位是空值。如果欄位的blank=False,該欄位就是必填的。
(3)default
欄位的預設值。可以是一個值或者可呼叫物件。如果可呼叫 ,每有新物件被建立它都會被呼叫。
(4)primary_key
如果為True,那麼這個欄位就是模型的主鍵。如果你沒有指定任何一個欄位的primary_key=True,
Django 就會自動新增一個IntegerField欄位做為主鍵,所以除非你想覆蓋預設的主鍵行為,
否則沒必要設定任何一個欄位的primary_key=True。
(5)unique
如果該值設定為 True, 這個資料欄位的值在整張表中必須是唯一的
(6)choices
由二元組組成的一個可迭代物件(例如,列表或元組),用來給欄位提供選擇項。如果設定了choices,預設的表單將是一個選擇框而不是標準的文字框,而且這個選擇框的選項就是choices中的選項。
3.元資訊
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 資料庫中生成的表名稱 預設 app名稱 + 下劃線 + 類名
db_table = "table_name"
# 聯合索引
index_together = [
("pub_date", "deadline"),
]
# 聯合唯一索引
unique_together = (("driver", "restaurant"),)
# admin中顯示的表名稱
verbose_name = '使用者資訊表'
# 只寫上面一行的話verbose_name會加s,即使用者資訊表s,因為在外國s代表複數,如果要完全改過來寫下面這句
verbose_name_plural = '使用者資訊表'
# 一般這兩句是連用的,但是真正起效果的是下面那句
四、列印原生sql
有時候為了我們方便排查需要打印出原生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',
},
}
}
五、查詢表記錄API
<1> all(): 查詢所有結果
<2> filter(**kwargs): 它包含了與所給篩選條件相匹配的物件
<3> get(**kwargs): 返回與所給篩選條件相匹配的物件,返回結果有且只有一個,如果符合篩選條件的物件超過一個或者沒有都會丟擲錯誤。
<4> exclude(**kwargs): 它包含了與所給篩選條件不匹配的物件,即排除
<5> order_by(*field): 對查詢結果排序,如果要對某個欄位逆排序,可以在欄位前加-號('-id')
<6> reverse(): 對查詢結果反向排序,相當於上面加-號
<7> count(): 返回資料庫中匹配查詢(QuerySet)的物件數量。count操作是在資料庫的時候就統計好的,而不是拿到結果再用python統計
<8> first(): 返回第一條記錄,底層相當於asc,limit1
<9> last(): 返回最後一條記錄,底層相當於desc,limit1
<10> exists(): 如果QuerySet包含資料,就返回True,否則返回False
<11> values(*field): 返回一個ValueQuerySet——一個特殊的QuerySet,執行後得到的並不是一系列model的例項化物件,而是一個可迭代的字典序列
<12> values_list(*field): 它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列
<13> distinct(): 從返回結果中剔除重複紀錄
六、基於雙下劃線的模糊查詢
# 1 價格在[100,200,300]這個範圍內
Book.objects.filter(price__in=[100,200,300])
# 2 大於,小於,大於等於,小於等於
Book.objects.filter(price__gt=100)
Book.objects.filter(price__lt=100)
Book.objects.filter(price__gte=100)
Book.objects.filter(price__lte=100)
# 3 範圍
Book.objects.filter(price__range=[100,200])
# 4 包含
Book.objects.filter(title__contains="python")
# 5 忽略大小寫包含
Book.objects.filter(title__icontains="python")
# 6 以xx開頭
Book.objects.filter(title__startswith="py")
# 7 時間型別,年份是2012年的
Book.objects.filter(pub_date__year=2012)
七、刪除表記錄
# 第一種:queryset的delete方法
res=models.Book.objects.all().delete()
print(res)
# 第二種:物件自己的delete方法
book = models.Book.objects.all().filter(name='金梅').first()
print(type(book))
res=book.delete()
八、修改表記錄
# 第一種:queryset的update方法
res=models.Book.objects.filter(publish='東京').update(name='金梅1')
print(res)
# 第二種:物件自己的
book = models.Book.objects.filter(name='xxx').last()
book.name='asdfasd'
book.save()
九、python指令碼中呼叫django環境
可以讓我們不需要開啟瀏覽器輸入資料,方便測試
# 在指令碼中呼叫djagno服務
import os
if __name__ == '__main__':
#1 引入django配置檔案
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day67.settings')
# 2 讓djagno啟動
import django
django.setup()
# 3 使用表模型
from app01 import models
models.Book.objects.create(name='測試書籍',publish='xx出版社')
十、多表操作
1.多表模型建立注意點
多表因為要建立外來鍵或者對應關係,所以和單表的建立有些許不同
1 圖書表:book,作者表:author,作者詳情表:authordetail,出版社表:publish,(第三張中間表)
2 作者跟作者詳情:是一對一,關聯欄位寫在哪一方都可以
3 圖書跟出版社:是一對多,一對多關係一旦確立,關聯欄位寫在多的一方
4 圖書和作者:是多對多,多對多的關係需要建立第三張表(可以自動生成)
5 models.py中把關係建立出來
from django.db import models
### django版本:目前使用1.11.x或者12.0.x,使用django3會出問題
class Publish(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
phone = models.CharField(max_length=64)
email = models.EmailField()
class Book(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=6, decimal_places=2)
publish_date = models.DateTimeField(auto_now_add=True)
read_num=models.IntegerField(default=0)
commit_num=models.IntegerField(default=0)
# to='Publish'跟Publish表做關聯(ForeignKey,一對多)
# to_field='id'跟哪個欄位做關聯
# publish=models.CharField(max_length=32)
# publish=models.ForeignKey(to='Publish',to_field='id')
# publish = models.ForeignKey(to='Publish') # 不寫的話,預設跟主鍵做關聯
publish = models.ForeignKey(to=Publish)
# 自動創建出第三張表(這句話會自動建立第三張表)
# authors在資料庫中不存在該欄位,沒有to_field
# 預設情況:第三張表有id欄位,還有Book表的id和Author表的id欄位
authors=models.ManyToManyField(to='Author')
class Author(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
# 一對一的本質是 ForeignKey+unique
author_detail=models.OneToOneField(to='AuthorDetail',to_field='id')
# author_detail=models.ForeignKey(to='AuthorDetail',to_field='id',unique=True)
class AuthorDetail(models.Model):
id = models.AutoField(primary_key=True)
sex = models.SmallIntegerField()
addr = models.CharField(max_length=64)
phone = models.BigIntegerField()
6 同步到mysql資料庫
-配置檔案
-pymysql.install_as_mysqldb()
-g的mysqlclient
-兩條命令
7 2.x版本的django
-外來鍵欄位必須加引數:on_delete
-1.x版本不需要,預設就是級聯刪除
-假設,
刪除出版社,該出版社出版的所有圖書也都刪除,on_delete=models.CASCADE
刪除出版社,該出版社出版的圖書不刪除,設定為空on_delete=models.SET_NULL,null=True
刪除出版社,該出版社出版的圖書不刪除,設定為預設on_delete=models.SET_DEFAULT,default=0
2.一對多新增記錄
當建立了外來鍵關係之後,我們在插入表格的時候,被關聯的表格需要先有資料,然後才能在多關係的表格里加資料,如下的book表,publish欄位需要和publish表的id欄位關聯。
publish=models.Publish.objects.create(name='北京出版社',addr='北京',phone='0536-12345678',email='郵箱地址')
# 新增圖書三種方式
book=models.Book.objects.create(name='金梅',price='23.45',publish=publish)# publish=物件
book=models.Book.objects.create(name='西遊記',price='23.55',publish_id=1)# publish_id=數字
book=models.Book.objects.create(name='西遊記',price='23.55',publish_id=publish.id)# publish_id=數字
# 總結:
1 email可以不傳email,本質就是varchar(admin中會判斷)
2 新增圖書:
-publish=publish
-publish_id=publish.id
3 寫在表模型中的publish欄位,到資料庫中會變成publish_id(ForeignKey)
4 查到book物件以後
-book.publish 物件
-book.publish_id id號,數字
3.多對多新增記錄,修改,刪除
自動建立的表,表模型就拿不到,就如同我們在book類裡建立的authors。book.authors代指表模型
# 多對多,作者和書
# 給西遊記這本書新增兩個作者lqz和egon
# 去到西遊記這本書
book=models.Book.objects.get(name='西遊記')
# 代指中間表book.authors
lqz=models.Author.objects.get(id=2)
egon=models.Author.objects.get(id=3)
# 新增作者需要傳id,有以下三種方式
book.authors.add(2,3) # 新增作者,通過id新增
book.authors.add(lqz,egon) # 新增作者,通過物件新增
book.authors.add(2,egon) # 新增作者,通過物件新增
# 西遊記刪除一個作者
book = models.Book.objects.get(name='西遊記')
book.authors.remove(2)
egon = models.Author.objects.get(id=3)
book.authors.remove(egon)
# clear 清空所有作者
book = models.Book.objects.get(name='西遊記')
book.authors.clear()
# set先清空,再add,前提是不存在的作者,即如果這個作者原來在表裡,又使用set,原存在的資料不會被刪除,然後把不存在的資料加進去
book.authors.set([4,])
# add ,remove,set clear
十一、多表操作之查詢
跨表查詢有兩種方式
-基於物件的跨表查詢:相當於我們原生sql子查詢
-基於雙下劃線的跨表查詢:相當於我們原生sql的關聯查詢,連表查詢(就是left join等)
1.基於物件的跨表查詢
基於物件的跨表查詢,先查物件,通過物件再去查另一個物件(正向:欄位名,反向:表名小寫/表名小寫_set.all())
正向查詢:最後查出來的物件有我們需要的屬性,那麼直接,物件.欄位名
反向查詢:最後查出來的物件沒有我們需要的屬性,而是與我們想要的欄位有關聯(比如外來鍵關係),那麼我們可以,物件.類名小寫.屬性,獲取單個數據,物件.類名小寫_set.all(),獲取多個數據返回的列表。
技巧:熟練後,不需要管什麼正向反向,能點出屬性就直接點出來,不能的話就點類名小寫,再看資料是一條還是多條。
一對多
# 查詢主鍵為1的書籍的出版社所在的城市
# 先查主鍵為1的書,下面有兩種方式
book=models.Book.objects.get(id=1) # 第一次查詢
book=models.Book.objects.filter(id=1).first() # 第一次查詢
publish=book.publish # 第二次查詢,內部又執行了一次查詢,根據publish_id查詢publish
print(publish.addr)
# 北京出版社出版的所有書籍,反向
publish=models.Publish.objects.get(name='北京出版社') # 第一次查詢了出版社
books=publish.book_set.all() # 表名小寫_set,第二次,根據出版社id,查詢所有書
print(books)
# 地址為山東的作者寫的所有書
author_detail=models.AuthorDetail.objects.get(addr='山東')
author=author_detail.author
books=author.book_set.all()
print(books[0].name)
# 正向查詢:book表內有publish欄位 直接物件.欄位名
# 反向查詢:publish表內沒有book欄位,出版社物件.Book小寫_set.all()或者Book直接小寫。
一對一
# 查詢所有住址在山東的作者的姓名,反向
# 反向查詢:author_detail沒有author欄位,需要使用author_detail.表名小寫
author_detail=models.AuthorDetail.objects.filter(addr__contains='山東').first()
print(author_detail.author.name)
# 查詢egon作者的地址,正向
author=models.Author.objects.get(name='egon')
print(author.author_detail.addr)
多對多
# 金梅所有作者的名字以及手機號,正向
book=models.Book.objects.get(name='金梅')
authors=book.authors.all()
for author in authors:
print(author.name)
print(author.author_detail.phone)
# 查詢egon出過的所有書籍的名字,反向
egon=models.Author.objects.get(name='egon')
books=egon.book_set.all()
for book in books:
print(book.name)
2.基於雙下劃線的跨表查詢
一對多
# 格式 models.型別.objects.filter,values/values_list(寫 __ 跨表)
# 查詢北京出版社出版過的所有書籍的名字與價格(一對多)
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__price')
print(res)
res=models.Book.objects.filter(publish__name='北京出版社').values('name','price')
print(res)
多對多
# 查詢egon出過的所有書籍的名字,價格(多對多)
# 反向
res=models.Author.objects.filter(name='egon').values('book__name','book__price')
print(res)
# 正向
res=models.Book.objects.filter(authors__name='egon').values('name','price')
print(res)
# 查詢egon的手機號
res=models.Author.objects.filter(name='egon').values('author_detail__phone')
print(res)
res=models.AuthorDetail.objects.filter(author__name='egon').values('phone')
print(res)
3.進階連續跨表查詢
# 查詢北京出版社出版過的所有書籍的名字以及作者的姓名
res=models.Publish.objects.filter(name='北京出版社').values('book__name','book__authors__name')
print(res)
res=models.Book.objects.filter(publish__name='北京出版社').values('name','authors__name')
print(res)
res=models.Author.objects.filter(book__publish__name='北京出版社').values('book__name','name')
print(res)
# 手機號以189開頭的作者出版過的所有書籍名稱以及出版社名稱
res=models.AuthorDetail.objects.filter(phone__startswith='189').values('author__book__name','author__book__publish__name')
print(res)
res = models.Author.objects.filter(author_detail__phone__startswith='189').values('book__name','book__publish__name')
print(res)
十二、聚合查詢
聚合查詢包括:Count,Avg,Min,Max,Sum,和我們原生sql中的聚合函式是一樣的。同時使用也相同,我們在使用了聚合函式之後,只能拿到聚合的欄位。
aggregate()
是QuerySet
的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的識別符號,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函式的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。
from django.db.models import Avg,Max,Min,Count,Sum
# 要用aggregate藥包裹聚合函式,當aggregate結束,已經不是queryset物件了,我們也可以在aggrepate裡用as起別名
# 1 計算所有圖書的平均價格
book=models.Book.objects.all().aggregate(Avg('price'))
book=models.Book.objects.all().aggregate(avg=Avg('price'))
# 2 計算總圖書數
book = models.Book.objects.all().aggregate(count=Count('id'))
# 3 計算最低價格的圖書
book = models.Book.objects.all().aggregate(min_price=Min('price'))
# 4 計算最大價格圖書
book = models.Book.objects.all().aggregate(max_price=Max('price'))
# 5 多個查詢
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
十三、分組查詢
分組查詢,即原生sql裡的group by。我們經常是分組和聚合函式搭配使用,如果是單獨使用聚合函式,那麼就是用aggregate()包括聚合函式,如果要和分組搭配使用,就需要annotate()包裹。
annotate的返回值是querySet,如果不想遍歷物件,可以用上value_list:
queryResult= Publish.objects.annotate(MinPrice=Min("book__price")).values_list("name","MinPrice")
print(queryResult)
'''
使用說明:
annotate() 內寫聚合函式
values在前表示group by的欄位
values在後表示取某幾個欄位
filter在前表示where
filter在後表示having
pk 代指主鍵
如果沒有指定group by的欄位,預設就用基表(Publish)主鍵欄位作為group by的欄位
查詢每個作者的名字,以及出版過書籍的最高價格(建議使用分組的表作為基表)
如果不用分組的表作為基表,資料不完整可能會出現問題
'''
from django.db.models import Avg, Count, Max, Min
# 示例一:查詢每一個出版社id,以及出書平均價格
select publish_id,avg(price) from app01_book group by publish_id;
ret=models.Book.objects.values('publish_id').annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)
# 查詢出版社id大於1的出版社id,以及出書平均價格
select publish_id,avg(price) from app01_book where publish_id>1 group by publish_id;
ret=models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).values('publish_id','avg')
print(ret)
# 查詢出版社id大於1的出版社id,以及出書平均價格大於30的
select publish_id,avg(price)as aaa from app01_book where publish_id>1 group by publish_id HAVING aaa>30;
ret = models.Book.objects.values('publish_id').filter(publish_id__gt=1).annotate(avg=Avg('price')).filter(avg__gt=30).values('publish_id', 'avg')
print(ret)
# 查詢每一個出版社出版的書籍個數
# pk 代指主鍵
ret=models.Publish.objects.values('pk').annotate(count=Count('book__id')).values('name','count')
print(ret)
# 如果沒有指定group by的欄位,預設就用基表(Publish)主鍵欄位作為group by的欄位.即上面可縮寫為
ret=models.Publish.objects.annotate(count=Count('book__id')).values('name','count')
print(ret)
# 另一種方式實現
ret=models.Book.objects.values('publish').annotate(count=Count('id')).values('publish__name','count')
print(ret)
# 查詢每個作者的名字,以及出版過書籍的最高價格(建議使用分組的表作為基表)
# 如果不用分組的表作為基表,資料不完整可能會出現問題
ret=models.Author.objects.values('pk').annotate(max=Max('book__price')).values('name','max')
ret = models.Author.objects.annotate(max=Max('book__price')).values('name', 'max')
ret= models.Book.objects.values('authors__id').annotate(max=Max('price')).values('authors__name','max')
print(ret)
# 查詢每一個書籍的名稱,以及對應的作者個數
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).values('name','count')
ret=models.Book.objects.annotate(count=Count('authors__id')).values('name','count')
ret=models.Author.objects.values('book__id').annotate(count=Count('id')).values('book__name','count')
print(ret)
#統計不止一個作者的圖書
ret=models.Book.objects.values('pk').annotate(count=Count('authors__id')).filter(count__gt=1).values('name','count')
ret = models.Author.objects.values('book__id').annotate(count=Count('id')).filter(count__gt=1).values('book__name', 'count')
print(ret)
# 統計價格數大於10元,作者的圖書
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).values('name', 'count')
print(ret)
#統計價格數大於10元,作者個數大於1的圖書
ret = models.Book.objects.values('pk').filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)
ret = models.Book.objects.filter(price__gt=10).annotate(count=Count('authors__id')).filter(count__gt=1).values('name', 'count')
print(ret)
十四、F和Q查詢
F查詢:取出資料庫的某個欄位的值,因為
Q查詢:製造"與或非"的條件
# F查詢:取出資料庫的某個欄位的值
from django.db.models import F
# 把read_num都加1
ret=models.Book.objects.all().update(read_num=F('read_num')+1)
print(ret)
#查詢評論數大於閱讀數的書籍
ret=models.Book.objects.all().filter(commit_num__gt=F('read_num'))
for i in ret:
print(i.name)
# 查詢評論數大於閱讀數2倍的書籍
ret=models.Book.objects.filter(commit_num__gt=F('read_num')*2)
print(ret)
# Q查詢:製造"與或非"的條件:& | ~
from django.db.models import Q
# 查詢名字叫egon或者價格大於100的書
ret=models.Book.objects.filter(Q(name='egon') | Q(price__gt=100))
# 查詢名字叫egon並且價格大於100的書,並且的用法有兩種方式,一般用第二種,和之前的寫法一樣
ret=models.Book.objects.filter(Q(name='egon') & Q(price__gt=100))
ret=models.Book.objects.filter(name='egon',price__gt=100)
# 查詢名字不為egon的書
ret = models.Book.objects.filter(~Q(name='egon'))
print(ret)
# Q可以巢狀,巢狀就可以使用很多條件的判斷
ret = models.Book.objects.filter((Q(name='egon') & Q(price__lt=100)) | Q(id__lt=3))
print(ret)
十五、原生sql
# 原生sql(有些sql用orm寫不出來,因為水平不到位哈哈)
# 第一種:用的比較少
from django.db import connection
cursor = connection.cursor()
cursor.execute("""SELECT * from app01_book where id = %s""", [1])
row = cursor.fetchone()
row = cursor.fetchall()
print(row)
# 第二種,用的多
books=models.Book.objects.raw('select * from app01_book where id >3')
print(books)#RawQuerySet物件
for book in books:
print(book.name)
books=models.Book.objects.raw('select * from app01_publish')
for book in books:
print(book.__dict__)
print(book.name)
print(book.addr)
print(book.email)
print(book.price)
authors = models.Author.objects.raw('SELECT app01_author.id,app01_author. NAME,app01_authordetail.sex FROM app01_author JOIN app01_authordetail ON app01_author.author_detail_id = app01_authordetail.id WHERE app01_authordetail.sex = 1')
for author in authors:
print(author.name)
print(author.__dict__)
十六、defer和only
# defer和only(查詢優化相關)
# only保持是book物件,但是隻能使用only指定的欄位
books = models.Book.objects.all().only('name')
print(books[0].name)
print(books[0].price) # 能出來,但是要再去資料庫查一遍
books = models.Book.objects.all().only('name')
print(books[0].__dict__)
books = models.Book.objects.all().defer('name','price')
print(books[0].__dict__)
十七、事務(請求,裝飾器,區域性)
# 事物:ACID,事物的隔離級別,鎖, 行級鎖,表級鎖
# djanog orm中使用事物:原子性操作,要麼都成功,要麼都失敗
# 例子:新增一個作者詳情,新增一個作者,這兩者應該同時成功,不可能有作者詳情而沒有作者,反之亦然
# 事物的三個粒度
# 1 區域性使用(常用)
from django.db import transaction
with transaction.atomic(): # 都在事物中,要麼都成功,要麼都失敗
author_detail = models.AuthorDetail.objects.create(addr='xxx', phone='123', sex=1)
# raise Exception('拋了異常')
author = models.Author.objects.create(name='llqz', age=19, author_detail=author_detail)
# 2 在一個檢視中使用,檢視函式裝飾器,這一個檢視函式都在一個事物中(偶爾使用)
@transaction.atomic
def index(request):
return HttpResponse('ok')
# 3 整個http請求,在事物中,在setting.py中配置(基本不用)
DATABASES = {
'default': {
...
'PORT': 3306,
'ATOMIC_REQUEST': True,
}
}
'ATOMIC_REQUEST': True,
# 設定為True統一個http請求對應的所有sql都放在一個事務中執行,百分之九十九不會用這個,因為把整個http請求放進事務太消耗資源,且很多情況下,對於一個http請求我們需要有不同的操作
補充
1.時區和國際化問題
setting.py中
1 後臺管理漢語問題
LANGUAGE_CODE = 'zh-hans' # 管理後臺看到的就是中文
2 時區問題(使用東八區)
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
2.django admin
0 管理後臺是django提供的可以快速對錶進行增刪查改操作
1 建立一個後臺管理賬號
python3 manage.py createsuperuser
輸入使用者名稱
輸入郵箱(可以不填,敲回車)
輸入密碼
確認密碼
# 超級使用者創建出來了,可以登入管理後臺了
2 admin中表中一行一行的資料顯示我們定製的樣子
重寫模型類的__str__方法
3 建立完賬號後,需要將表註冊到後臺中
在app下的admin.py中寫
from app01 import models
# 把book表註冊,管理後臺就能看到了
admin.site.register(models.Book)
4 更改時區,這樣django後臺就變成了中文
3.blank引數作用
1 需要把book表註冊到admin中
在app下的admin.py中寫
from app01 import models
# 把book表註冊一些,管理後臺就能看到了
admin.site.register(models.Book)
2 可以快速的對book表進行增刪查改操作
4.看一看這篇部落格
https://www.cnblogs.com/nokiaguy/p/13803370.html
5.安裝模組相關
pip3 install django
# 本質是去https://pypi.python.org/simple,搜這個模組,會根據你的平臺下載在一個安裝包(windows平臺是whl),下載完,再安裝
# pip安裝失敗的情況
# 我們可以繞過它,有了whl檔案以後,自己裝
# https://www.lfd.uci.edu/~gohlke/pythonlibs/#opencv
pip3 install django.whl
# 官方庫沒有上傳到pypi,官方也不給製作whl檔案
#如何安裝 包 (setup.py)
到達安裝目錄,setup.py所在的目錄
python setup.py build
python setup.py install
# 配置清華源,豆瓣源,本質是
豆瓣源會把pypi,包拉到自己的伺服器上,以後你再下,去它的伺服器上下,所以速度快
# 你自己寫的包,如何上傳到pypi上給別人使用?
6.外來鍵關係要不要建立
1 關聯欄位與外來鍵約束沒有必然的聯絡(建管理欄位是為了進行查詢,建約束是為了不出現髒資料)
2 預設情況,關聯關係建好以後,外來鍵約束就自然建立了
3 實際工作中,外來鍵約束一般不建(影響效率),都是人為約束(程式碼約束)
-db_constraint=False
4 表模型和資料庫表的對應,不要直接修改表(這是可以的,但是不建議),要修改表模型,同步到表中
練習
1.鏈式呼叫
queryset就是鏈式呼叫的使用),實現一個可以支援鏈式呼叫的類
class MyClass:
def func1(self):
print('func1')
return self
def func2(self):
print('func2')
return self
obj = MyClass()
obj.func1().func2()
(重點)2.利用orm的多表查詢完成如下功能
基於物件,基於雙下滑下)
# 查詢所有書名裡包含紅樓的書
res = models.BookInfo.objects.filter(name__contains='紅樓')
# 查找出版日期是2017年的書
res = models.BookInfo.objects.filter(publish_time__year=2017)
# 查找出版日期是2017年的書名
books = models.BookInfo.objects.filter(publish_time__year=2017)
for book in books:
print(book.name)
# 查找價格大於10元的書
res = models.BookInfo.objects.filter(price__gt=10)
# 查找價格大於10元的書名和價格
res = models.BookInfo.objects.filter(price__gt=10)
for i in res:
print(i.name,i.price)
# 查詢在北京的出版社
res = models.Publish.objects.filter(addr='北京')
# 查詢名字以沙河開頭的出版社
res = models.Publish.objects.filter(name__startswith='沙河')
# 查詢作者名字裡面帶“小”字的作者
res = models.Author.objects.filter(name__contains='小')
# 查詢年齡大於30歲的作者
res = models.Author.objects.filter(age__gt=30)
# 查詢手機號是155開頭的作者
res = models.AuthorDetail.objects.filter(phone__startswith='155')
res1 = res.author_set.all()
# 查詢手機號是155開頭的作者的姓名和年齡
res = models.AuthorDetail.objects.filter(phone__startswith='155')
res1 = res.author_set.all()
for i in res1:
print(i.name,i.age)
# 查詢書名是“紅樓夢”的書的出版社
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.publish
# 查詢書名是“紅樓夢”的書的出版社所在的城市
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.publish.addr
res2 = res1.addr
# 查詢書名是“紅樓夢”的書的出版社的名稱
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.publish
res2 = res1.name
# 查詢書名是“紅樓夢”的書的所有作者
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.author_set.all()
# 查詢書名是“紅樓夢”的書的作者的年齡
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.author_set.all()
for i in res1:
print(i.age)
# 查詢書名是“紅樓夢”的書的作者的手機號碼
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.author_set.all()
res2 = res1.authordetail.phone
# 查詢書名是“紅樓夢”的書的作者的地址
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.author_set.all()
res2 = res1.authordetail.addr
# 查詢書名是“紅樓夢”的書的作者的郵箱
res = models.BookInfo.objects.filter(name='紅樓夢')
res1 = res.author_set.all()
res2 = res1.authordetail.email
3.完成如下功能
1 查詢老男孩出版社出版過的價格大於200的書籍
res = models.BookInfo.objects.filter(price__gt = 200).first()
2 查詢2017年8月出版的所有以py開頭的書籍名稱
res = models.BookInfo.objects.filter(publish_time__year = 2017).filter(name__startswith = 'py')
3 查詢價格為50,100或者150的所有書籍名稱及其出版社名稱
4 查詢價格在100到200之間的所有書籍名稱及其價格
5 查詢所有人民出版社出版的書籍的價格(從高到低排序,去重)
4.整理
查詢api--14個方法中,哪些返回值是queryset物件,哪些不是queryset物件,是什麼?
5.登入功能,連線mysql
前端頁面略
路由
url(r'^login', views2.login),
url(r'^index', views2.index),
url(r'^test', views.test),
檢視函式
def index(request):
return render(request, 'index2.html')
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
else:
name = request.POST.get('name')
password = request.POST.get('password')
# 建立一個數據庫連結
conn = pymysql.connect(host='127.0.0.1', user='root', password='123', database='userinfo', port=3306, )
# 拿到一個遊標
cursor = conn.cursor()
# 執行sql
cursor.execute('select * from user where name=%s and password=%s ', (name, password))
# 獲取結果
ret = cursor.fetchone()
print(ret)
if ret:
return redirect('/index')
else:
return HttpResponse('使用者名稱或密碼錯誤')
6.(重點)完成如下查詢
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE','django6.settings')
import django
django.setup()
from app01 import models
from django.db.models import Count, Max, Min,Avg,Sum,F,Q
# 1統計每一本書作者個數
ret = models.Book.objects.values("id").annotate(count = Count('authors__id')).values("name","count")
print(ret)
# 2統計每一個出版社的最便宜的書
ret = models.Publish.objects.annotate(min_price = Min('book__name')).values("name",'min_price')
print(ret)
# 3統計每一本以py開頭的書籍的作者個數:
ret = models.Book.objects.filter(name__startswith = 'book').annotate(count=Count('authors__id')).values("name", 'count')
print(ret)
# 4作者數量大於2的圖書名字和價格
ret = models.Book.objects.annotate(count = Count('authors__id')).filter(count__gt = 2).values('name','price')
print(ret)
# 5根據一本圖書作者數量的多少對查詢集QuerySet進行排序:
ret = models.Book.objects.annotate(count=Count('authors__id')).values('name','count').order_by('count')
print(ret)
# 6查詢各個作者出的書的總價格
ret = models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')
print(ret)
# 7查詢每個出版社的名稱和書籍個數
ret = models.Publish.objects.annotate(count=Count('book__id')).values('name', 'count')
print(ret)
# 8查詢所有書籍的平均價格
ret = models.Book.objects.aggregate(avg_price=Avg('price'))
print(ret)
# 9統計圖書平均價格和最大價格
ret = models.Book.objects.aggregate(avg_price=Avg('price'),max_price = Max('price'))
print(ret)
# 10統計圖書總個數和平均價格
ret = models.Book.objects.aggregate(count_price=Count('pk'),avg_price=Avg('price')).
print(ret)
# 11查詢評論數小於等於閱讀數2倍的書籍
ret = models.Book.objects.filter(commit_num__lte=F('read_num')*2)
for book in ret:
print(book.name)
# 12將每一本書的價格提高30元
ret = models.Book.objects.all().update(price = F("price")+30)
print(ret)
# 13將id大於3的每本書價格提高5元
ret = models.Book.objects.filter(id__gt=3).update(price = F("price")+5)
print(ret)
# 14查詢名字不是西遊,並且價格大於150的書
ret = models.Book.objects.filter(~Q(name='西遊') & Q(price__gt = 150))
for book in ret:
print(book.name)
# 15查詢名字叫西遊並且價格大於150或者id小於等於5的圖書
ret = models.Book.objects.filter(Q(name='西遊') & Q(price__gt = 150) | Q(id__lte=5))
for book in ret:
print(book.name)
# 16查詢名字叫西遊或者出版社為北京出版社的圖書
ret = models.Book.objects.filter(Q(name='西遊') | Q(publish__name = '北京出版社'))
for book in ret:
print(book.name)