orm(對象關系映射)
對象是指 orm的models類名實例化的對象
關系是指 orm中的數據模型和數據庫的關系
類----------------》表 #models.py中的類對應數據庫中的表
對象--------------》記錄(數據行) #obj=models.User.objects.get(id=1) obj對應數據庫表中的行數據
屬性--------------》字段 #models.py類中的屬性對應數據庫表中的字段
映射 orm其實就是將models.類名.object.filter類對象的語法通過orm翻譯翻譯成sql語句的一個引擎
字段介紹
'''
<1> CharField
字符串字段, 用於較短的字符串.
CharField 要求必須有一個參數 maxlength, 用於從數據庫層和Django校驗層限制該字段所允許的最大字符數.
<2> IntegerField
#用於保存一個整數.
<3> DecimalField
一個浮點數. 必須 提供兩個參數:
參數 描述
max_digits 總位數(不包括小數點和符號)
decimal_places 小數位數
舉例來說, 要保存最大值為 999 (小數點后保存2位),你要這樣定義字段:
models.DecimalField(..., max_digits=5, decimal_places=2)
要保存最大值一百萬(小數點后保存10位)的話,你要這樣定義:
models.DecimalField(..., max_digits=17, decimal_places=10) #max_digits大於等於17就能存儲百萬以上的數了
admin 用一個文本框(<input type="text">)表示該字段保存的數據.
<4> AutoField
一個 IntegerField, 添加記錄時它會自動增長. 你通常不需要直接使用這個字段;
自定義一個主鍵:my_id=models.AutoField(primary_key=True)
如果你不指定主鍵的話,系統會自動添加一個主鍵字段到你的 model.
<5> BooleanField
A true/false field. admin 用 checkbox 來表示此類字段.
<6> TextField
一個容量很大的文本字段.
admin 用一個 <textarea> (文本區域)表示該字段數據.(一個多行編輯框).
<7> EmailField
一個帶有檢查Email合法性的 CharField,不接受 maxlength 參數.
<8> DateField
一個日期字段. 共有下列額外的可選參數:
Argument 描述
auto_now 當對象被保存時(更新或者添加都行),自動將該字段的值設置為當前時間.通常用於表示 "last-modified" 時間戳.
auto_now_add 當對象首次被創建時,自動將該字段的值設置為當前時間.通常用於表示對象創建時間.
(僅僅在admin中有意義...)
<9> DateTimeField
一個日期時間字段. 類似 DateField 支持同樣的附加選項.
<10> ImageField
類似 FileField, 不過要校驗上傳對象是否是一個合法圖片.#它有兩個可選參數:height_field和width_field,
如果提供這兩個參數,則圖片將按提供的高度和寬度規格保存.
<11> FileField
一個文件上傳字段.
要求一個必須有的參數: upload_to, 一個用於保存上載文件的本地文件系統路徑. 這個路徑必須包含 strftime #formatting,
該格式將被上載文件的 date/time
替換(so that uploaded files don't fill up the given directory).
admin 用一個<input type="file">部件表示該字段保存的數據(一個文件上傳部件) .
注意:在一個 model 中使用 FileField 或 ImageField 需要以下步驟:
(1)在你的 settings 文件中, 定義一個完整路徑給 MEDIA_ROOT 以便讓 Django在此處保存上傳文件.
(出於性能考慮,這些文件並不保存到數據庫.) 定義MEDIA_URL 作為該目錄的公共 URL. 要確保該目錄對
WEB服務器用戶帳號是可寫的.
(2) 在你的 model 中添加 FileField 或 ImageField, 並確保定義了 upload_to 選項,以告訴 Django
使用 MEDIA_ROOT 的哪個子目錄保存上傳文件.你的數據庫中要保存的只是文件的路徑(相對於 MEDIA_ROOT).
出於習慣你一定很想使用 Django 提供的 get_<#fieldname>_url 函數.舉例來說,如果你的 ImageField
叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 這樣的方式得到圖像的絕對路徑.
<12> URLField
用於保存 URL. 若 verify_exists 參數為 True (默認), 給定的 URL 會預先檢查是否存在( 即URL是否被有效裝入且
沒有返回404響應).
admin 用一個 <input type="text"> 文本框表示該字段保存的數據(一個單行編輯框)
<13> NullBooleanField
類似 BooleanField, 不過允許 NULL 作為其中一個選項. 推薦使用這個字段而不要用 BooleanField 加 null=True 選項
admin 用一個選擇框 <select> (三個可選擇的值: "Unknown", "Yes" 和 "No" ) 來表示這種字段數據.
<14> SlugField
"Slug" 是一個報紙術語. slug 是某個東西的小小標記(短簽), 只包含字母,數字,下划線和連字符.#它們通常用於URLs
若你使用 Django 開發版本,你可以指定 maxlength. 若 maxlength 未指定, Django 會使用默認長度: 50. #在
以前的 Django 版本,沒有任何辦法改變50 這個長度.
這暗示了 db_index=True.
它接受一個額外的參數: 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.
<13> XMLField
一個校驗值是否為合法XML的 TextField,必須提供參數: schema_path, 它是一個用來校驗文本的 RelaxNG schema #的文件系統路徑.
<14> FilePathField
可選項目為某個特定目錄下的文件名. 支持三個特殊的參數, 其中第一個是必須提供的.
參數 描述
path 必需參數. 一個目錄的絕對文件系統路徑. FilePathField 據此得到可選項目.
Example: "/home/images".
match 可選參數. 一個正則表達式, 作為一個字符串, FilePathField 將使用它過濾文件名.
注意這個正則表達式只會應用到 base filename 而不是
路徑全名. Example: "foo.*\.txt^", 將匹配文件 foo23.txt 卻不匹配 bar.txt 或 foo23.gif.
recursive可選參數.要么 True 要么 False. 默認值是 False. 是否包括 path 下面的全部子目錄.
這三個參數可以同時使用.
match 僅應用於 base filename, 而不是路徑全名. 那么,這個例子:
FilePathField(path="/home/images", match="foo.*", recursive=True)
...會匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif
<15> IPAddressField
一個字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16> CommaSeparatedIntegerField
用於存放逗號分隔的整數值. 類似 CharField, 必須要有maxlength參數.
'''
屬性介紹
(1)null
如果為True,Django 將用NULL 來在數據庫中存儲空值。 默認值是 False.
(1)blank
如果為True,該字段允許不填。默認為False。
要注意,這與 null 不同。null純粹是數據庫范疇的,而 blank 是數據驗證范疇的。
如果一個字段的blank=True,表單的驗證將允許該字段是空值。如果字段的blank=False,該字段就是必填的。
(2)default
字段的默認值。可以是一個值或者可調用對象。如果可調用 ,每有新對象被創建它都會被調用,如果你的字段沒有設置可以為空,那么將來如果我們后添加一個字段,這個字段就要給一個default值
(3)primary_key
如果為True,那么這個字段就是模型的主鍵。如果你沒有指定任何一個字段的primary_key=True,
Django 就會自動添加一個IntegerField字段做為主鍵,所以除非你想覆蓋默認的主鍵行為,
否則沒必要設置任何一個字段的primary_key=True。
(4)unique
如果該值設置為 True, 這個數據字段的值在整張表中必須是唯一的
(5)choices
由二元組組成的一個可迭代對象(例如,列表或元組),用來給字段提供選擇項。 如果設置了choices ,默認的表單將是一個選擇框而不是標准的文本框,<br>而且這個選擇框的選項就是choices 中的選項。
(6)db_index
如果db_index=True 則代表着為此字段設置數據庫索引。
DatetimeField、DateField、TimeField這個三個時間字段,都可以設置如下屬性。
(7)auto_now_add
配置auto_now_add=True,創建數據記錄的時候會把當前時間添加到數據庫。
(8)auto_now
配置上auto_now=True,每次更新數據記錄的時候會更新該字段,標識這條記錄最后一次的修改時間。
1.MVC或者MVC框架中包括一個重要的部分,就是ORM,它實現了數據模型與數據庫的解耦,即數據模型的設計不需要依賴於特定的數據庫,通過簡單的配置就可以輕松更換數據庫,這極大的減輕了開發人員的工作量
2.ORM是“對象-關系-映射”的簡稱。
3.執行流程
類對象--->sql--->pymysql--->mysql服務端--->磁盤
orm其實就是將類對象的語法翻譯成sql語句的一個引擎
orm語句 -- sql -- 調用pymysql客戶端發送sql -- mysql服務端接收到指令並執行
django 連接mysql順序
1 settings配置文件中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 引擎
'NAME': 'day53', # 數據庫名稱
'HOST': '127.0.0.1', # IP
'PORT': 3306, # 端口號
'USER': 'root', # 用戶名
'PASSWORD': '123' # 密碼
}
}
2 項目文件夾下的init文件中寫上下面內容,
作用:用pymysql替換mysqldb(djnago自帶的)
mysqldb 不能python3.4以上的版本
需要使用第三方模塊pymysql進行替換
app01中的--init--文件
補充
MySQLdb的是一個接口連接到MySQL數據庫服務器從Python
MySQLdb並不支持Python3.4之后版本
原因:
#python默認連接的MySQLdb並不支持Python3.4之后版本
解決辦法:
#12使用第三方模塊pymysql進行替換
import pymysql
pymysql.install_as_MySQLdb()
3 models文件中創建一個類(類名就是表名)
#引用一個模塊 from django.db import models
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=10)
bday = models.DateField()
checked = models.BooleanField()
# 1 翻譯成sql語句
# 2 django內置的一個sqlite客戶端將sql語句發給sqlite服務端
# 3 服務端拿到sql,到磁盤里面進行了數據操作(create table app01_userinfo(id name...))
4.執行數據庫同步指令,
添加字段的時候別忘了,該字段不能為空,所有要么給默認值,要么設置它允許為空 null=True
#注意在每次增加字段時候都需要執行一遍這個方法
python manage.py makemigrations#創建一個表記錄
python版本號
python manage.py migrate 執行記錄
數據庫問題mysql 更改時區和改變版本號mysql for 5.1
更改時區連接 https://www.cnblogs.com/strawberry-1/p/11599376.html
5 創建記錄
(實例一個對象,調用save方法)
models.py
#創建一條記錄,增
def query(request):
new_obj = models.UserInfo(
id=2,
name='子文',
bday='2019-09-27',
checked=1,
)
new_obj.save()
#翻譯成sql語句,
#然后調用pymysql,發送給服務端
相當於執行insert into app01_userinfo values(2,'子文','2019-09-27',1)
return HttpResponse('xxx')
6.字段約束
通過選項實現對字段的約束,選項如下:
null:如果為True,表示允許為空,默認值是False。
blank:如果為True,則該字段允許為空白,默認值是False。
對比:null是數據庫范疇的概念,blank是表單驗證證范疇的。
db_column:字段的名稱,如果未指定,則使用屬性的名稱。
db_index:若值為True, 則在表中會為此字段創建索引,默認值是False。
default:默認值。
primary_key:若為True,則該字段會成為模型的主鍵字段,默認值是False,一般作為AutoField的選項使用。
unique:如果為True, 這個字段在表中必須有唯一值,默認值是False。
配置數據庫
python3 manage.py makemigrations 創建腳本
python3 manage.py migrate 遷移
創建app01中的--init--文件
class Book(models.Model): #必須要繼承的
nid = models.AutoField(primary_key=True) #自增id(可以不寫,默認會有自增id AutoField是自增)
title = models.CharField(max_length=32)
publishDdata = models.DateField() #出版日期
author = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5,decimal_places=2) #一共5位,保留兩位小數
替換數據庫的時候需要重新創建表記錄
更改字段時候也需要
python36 manage.py makemigrations 創建腳本
python36 manage.py migrate 遷移
數據庫的操作(django)
創建字段django特殊說明
1.字段的值默認是不為空的 需要手動設置 不然會報錯
2.主鍵無需手動添加 自動生成id字段(主鍵)
3.引用模塊from django.db import models
獲取id字段的兩種方式
1.表名.id
例
book.id
2.表名.pk
例
book.pk
增:
方式1:
new_obj = models.UserInfo(#直接表名創建
過程
#1.先實例化產生對象,2.然后調用save方法保存
id=2,
name='子文',
bday='2019-09-27',
checked=1,
)
new_obj.save()
方式2:
# ret 是創建的新的記錄的model對象(重點)
ret = models.UserInfo.objects.create(
name='衛賀',
bday='2019-08-07',
checked=0
)
print(ret) #UserInfo object
print(ret.name) #UserInfo object
print(ret.bday) #UserInfo object
時間問題
models.UserInfo.objects.create(
name='楊澤濤2',
bday=current_date,
# now=current_date, 直接插入時間沒有時區問題
checked=0
)
但是如果讓這個字段自動來插入時間,就會有時區的問題,auto_now_add創建記錄時自動添加當前創建記錄時的時間,存在時區問題
now = models.DateTimeField(auto_now_add=True,null=True)
解決方法:
settings配置文件中將USE_TZ的值改為False
# USE_TZ = True
USE_TZ = False # 告訴mysql存儲時間時按照當地時間來寸,不要用utc時間
使用pycharm的數據庫客戶端的時候,時區問題要注意
刪
from django.db import models
簡單查詢:filter() -- 結果是queryset類型的數據里面是一個個的model對象,類似於列表
models.UserInfo.objects.filter(id=7).delete() #queryset對象調用
models.UserInfo.objects.filter(id=7)[0].delete() #model對象調用
改
方式1:update
# models.UserInfo.objects.filter(id=2).update(
# name='籃子文',
# checked = 0,
#
# )
# 錯誤示例,model對象不能調用update方法
# models.UserInfo.objects.filter(id=2)[0].update(
# name='加籃子+2',
# # checked = 0,
# )
方式2
ret = models.UserInfo.objects.filter(id=2)[0]
ret.name = '加籃子+2'
ret.checked = 1
ret.save()
更新時的auto_now參數
# 更新記錄時,自動更新時間,創建新紀錄時也會幫你自動添加創建時的時間,但是在更新時只有使用save方法的方式2的形式更新才能自動更新時間,有缺陷,放棄
now2 = models.DateTimeField(auto_now=True,null=True)
批量插入(bulk_create)
步驟
1.把查詢的對象放入字典里一次性傳入前端進行交互
# bulk_create
obj_list = []
for i in range(20):
obj = models.Book(
title=f'金瓶{i}',
price=20+i,
publish_date=f'2019-09-{i+1}',
publish='24期出版社'
)
obj_list.append(obj)
models.Book.objects.bulk_create(obj_list) #批量創建
form表單提交之字典
request.POST -- querydict類型 {'title': ['asdf '], 'price': ['212'], 'publish_date': ['2019-09-12'], 'publish': ['asdf ']}
data = request.POST.dict() -- 能夠將querydict轉換為普通的python字典格式
創建數據
models.Book.objects.create(
## title=title, #title='asdf '
## price=price, #price='212'
## publish_date=publish_date,
#'publish_date': ['2019-09-12']
## publish=publish,
#publish=['asdf ']
**data#通過打散把鍵和值轉換成以上數據
)
查詢api
篩選基本都是queryset類型
reservse 必須要排序才能反轉
1.all()
查詢對應表名的所有對象,結果是對象列表
結果為queryset類型
寫法
models.表名.object.all()
例如
all_books = models.Book.objects.all()
數量過多會自動截斷
2.filter條件查詢
過濾出符合條件的數據
filter 條件查詢
ret = models.Book.objects.filter(title='金瓶7',publish='24期出版社') #相當於mysql數據庫中and多條件查詢
查詢條件不能匹配到數據時,不會報錯,返回一個空的queryset,<QuerySet []>,如果沒有寫查詢條件會獲取所有數據,queryset類型的數據還能夠繼續調用fitler方法
3.get()
得到一個滿足條件的model對象 有且只有一個
ret = models.Book.objects.get() #得到的是一個model對象,有且只能有一個
1. 查不到數據會報錯 :Book matching query does not exist.
2. 超過一個就報錯 :returned more than one Book -- it returned 13!
4.exclude()#排除
#除了這個之外
models.BOOK.objects.exclude(title__startswith=('金瓶'))
#model類型不能使用這個方法
1.object能夠調用,models.Book.objects.exclude(title__startswith='金瓶')
2.queryset類型數據能夠調用, models.Book.objects.all().exclude(title__startswith='金瓶')
5.order by()排序
models.Book.objects.all().order_by('-price','id')
#sql語句寫法 orderby price desc,id asc;
models類型不能使用
排序order by 加上-字段名 不加是升序
6.reverse() 反轉
models.Book.objects.all().order_by('id').reverse() #數據排序之后才能反轉
7.count()
計數,統計返回結果的數量
models.Book.objects.all().count()
sql語句 聚合函數
8.first()
類似於models.類名(表名).objects.filter(條件判斷)[0]
models.類名(表名).objects.filter(條件判斷).first()
#返回滿足條件的第一條數據
返回第一條數據,結果是model對象類型
9.last()
返回最后一條數據,結果是model對象類型
ret = models.Book.objects.all().first()
ret = models.Book.objects.all().last()
10.exists()
判斷返回結果集是不是有數據
models.Book.objects.filter(id=9999).exists()
#有結果就是True,沒有結果就是False
11.values
(返回的queryset類型,里面是字典類型數據)
12.values_list
(返回的queryset類型,里面是數組類型數據)
ret = models.Book.objects.filter(id=9).values('title','price')
ret = models.Book.objects.all().values_list('title','price')
ret = models.Book.objects.all().values()
ret = models.Book.objects.values() #調用values或者values_list的是objects控制器,那么返回所有數據
13.distinct()
去重,配置values和values_list來使用,不能帶有id 因為id默認唯一
models.Book.objects.all().values('publish').distinct()
filter雙下划線查詢
queryset類型 篩選出來的是queryset類型
get篩選出來一個是model對象
queryset 類型的方法可以多次調用使用
13個api能調用的函數和方法(重點)
# ret = models.Book.objects.all().values('publish').distinct()
# ret = models.Book.objects.filter(price__gt=35) #大於
# ret = models.Book.objects.filter(price__gte=35) # 大於等於
# ret = models.Book.objects.filter(price__lt=35) # 小於等於
# ret = models.Book.objects.filter(price__lte=35) # 小於等於
# ret = models.Book.objects.filter(price__range=[35,38]) # 大於等35,小於等於38 # where price between 35 and 38
# ret = models.Book.objects.filter(title__contains='金瓶') # 字段數據中包含這個字符串的數據都要
# ret = models.Book.objects.filter(title__contains='金瓶')
# ret = models.Book.objects.filter(title__icontains="python") # 不區分大小寫
# from app01.models import Book
# ret = models.Book.objects.filter(title__icontains="python") # 不區分大小寫
# ret = models.Book.objects.filter(title__startswith="py") # 以什么開頭,istartswith 不區分大小寫
# ret = models.Book.objects.filter(publish_date='2019-09-15')
某年某月某日(對於日期的修改):
ret = models.Book.objects.filter(publish_date__year='2018')
ret = models.Book.objects.filter(publish_date__year__gt='2018')#2018寫數字也可以
ret = models.Book.objects.filter(publish_date__year='2019',publish_date__month='8',publish_date__day='1')
找字段數據為空的雙下滑線
models.Book.objects.filter(publish_date__isnull=True) #這個字段值為空的那些數據
多表查詢
多表是會為減少數據的冗余 加速查詢的效率
models.字段類型()
表結構
rom django.db import models
# Create your models here.
class Author(models.Model):
"""
作者表
"""
name=models.CharField( max_length=32)
age=models.IntegerField()
#一對一 authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE) #
#一對一
au=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)
class AuthorDetail(models.Model):
"""
作者詳細信息表
"""
birthday=models.DateField()
telephone=models.CharField(max_length=11)
addr=models.CharField(max_length=64)
# class Meta:
# db_table='authordetail' #指定表名
# ordering = ['-id',]
class Publish(models.Model):
"""
出版社表
"""
name=models.CharField( max_length=32)
city=models.CharField( max_length=32)
class Book(models.Model):
"""
書籍表
"""
title = models.CharField( max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
#一對多
publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
多對多
authors=models.ManyToManyField('Author',)
注意事項
#重中之重 不要把表名和庫名設置成一樣的
1.不寫字段默認外鍵默認連接id
2.創表默認創建主鍵 無需手動創建
3.oneto one 一對一#OneToOneField
4.id可以省略(自動連接另一個表的id字段(主鍵))
5.djnago1.0版本可以不寫外鍵 默認級聯刪除
6.djnago2.0版本必須寫on_delete=models.CASCADE
7.int類型不能進行模糊搜索 例如 電話去模糊匹配 前三位
字段名__startwith='151'
8.外鍵字段是賦值變量名=變量名_id
9.class AuthorDetail(models.Model) 創建表結構時 要繼承 models.Model方法
完整版寫法:
authorDetail=models.ForeignKey(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
class meta:
指定創建表時的源信息
ordering 排序
db contrations 去除強制約束效果
多對多關系
# 多對多沒辦法使用一個表的外鍵去設置
#多對多關系用第三張表存儲關系
優點
存入字段數據少 數據庫小 增加執行效率
1.manytomany #authors=models.ManyToManyField('Author',)
ManyToManyField不會加字段
book——author 一個表-另一個表
1.會生成一個表 字段會自己建立
2。一個字段是表名——id
3.下一個字段是另一個表——id
會自動創建 對應id字段,存入到一個屬性中
使用方法
4.類名或者實例化對象(表名)去調用這個屬性
一對一關系
#注意事項
1.如果兩張表數據不統一 是表數據少的去連接數據多的(數據多的是主表)
2.如果數據統一 建哪個東西
sql語句 把外鍵變成unique(唯一)格式一樣
authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
多對一
1。對於外鍵不需要唯一性,
2。不建立外鍵唯一(使用models.OneToOneField)建立外鍵
外鍵
1.完整版寫法
authorDetail=models.ForeignKey(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
2.id可以省略(自動連接另一個表的id字段(主鍵))
publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
to=,to_field
#publishs是字段名
2.連接表的兩種寫法
第一種寫法
to="加對應表名" 默認連接主鍵(id)
第二種寫法
publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
可以不寫 直接寫表名 但是如果加載在之后會報錯因為沒有預加載出來
admin使用超級管理員
1.連接數據庫
注意要先連接數據庫才能進入admin頁面輸入密碼進行操作
創建與數據庫之間的連接
1.在對應的app01 的models文件夾下創建好表,其中表包括(字段類型(約束大小限制)
1.2外鍵的創建#需要注意的是1.1默認創建 2.0版本需要手動添加
1.2.1 一對一創建外鍵約束 #屬性名=OneToOneFlied(on_update=models.CASCADE)
一對多創建外鍵約束# 屬性名=foreignKey()
也對就是對應的子程序的models
2.創建表記錄
3.在對應子程序中把python默認支持的mysqldb 替換成pymysql
原因mysqldb不支持3.4以上版本
2.創建超級管理員
寫法 createsuperuser
兩種寫法
1.pycharm的一個控制台
#1.manage.py@dbcont > createsuperuser#一開始顯示這個
使用createsuperuser創建管理員
2.python控制台
2.1python36 manage.py createsuperuser#創建超級管理員
2.2同理輸入賬號和密碼 郵箱可以不用輸入
創建數據(記錄)
1.把models的表導入到admin
在對應app01(子程序中)的admin.py文件中
2.from django.contrib import admin
from app01 import models
admin.site.register(models.author)#格式 admin.site.reister(models.類名(表名))
增刪改查
在對應子程序的view視圖里寫增刪改查
注意要先引用models模塊(對應的models.py文件)
增加
一對一
# 一對一
# au_obj = models.AuthorDetail.objects.get(id=4)
查詢出對應的models對象 存入表類中
models.Author.objects.create(
name='海狗',
age=59,
# 兩種方式
au_id=4
# au=au_obj
#屬性對應的值是對應的models對象
)
一對多
與一對一的區別
對於連接字段沒有唯一的約束
pub_obj = models.Publish.objects.get(id=3)
models.Book.objects.create(
title='xx2',
price=13,
publishDate='2011-11-12',
publishs=pub_obj,
#類屬性作為關鍵字時,值為model對象
publishs_id=3
# 如果關鍵字為數據庫字段名稱,那么值為關聯數據的值
)
多對多
多對多關系表記錄的增加
ziwen = models.Author.objects.get(id=3)
haigou = models.Author.objects.get(id=5)
new_obj = models.Book.objects.create(
title='海狗產后護理第二部',
price=0.5,
publishDate='2019-09-29',
publishs_id=2,
)
new_obj是一個對象
#第一種寫法
new_obj.authors.add(對應外鍵字段的值)#對象.屬性.方法名添加add
new_obj.authors.add(3,5) #*args **kwargs
#添加的多個值用逗號隔開
new_obj.authors.add(*[3,5]) # 用的最多,
用法:
1.select 下拉框 的值是一個列表 使用name屬性 查看對應value的值
2.多選的value值 是提交到后端是一個列表
3.使用*[value]打散#*[3,5]
#第二種寫法 添加model對象
new_obj.authors.add(ziwen, haigou)
創建完字段之后要把對應的關系字段寫到第三張表里
刪除
一對一
models.AuthorDetail.objects.filter(id=3).delete()
models.Author.objects.filter(id=3).delete()
一對多
默認級聯刪除
關聯的表是主表 主表刪除子表的相對應的整條字段也被刪除
models.Publish.objects.filter(id=3).delete()#Publish是主表
models.book.objects.filter(id=3).delete()
多對多
只操作對應第三張表
book_obj = models.Book.objects.get(id=2)
book_obj.authors.add() # 添加
book_obj.authors.remove(1) #刪除括號里對應的外鍵關聯的id
book_obj.authors.clear() # 清除,篩選出的所有的對應外鍵關系字段
book_obj.authors.set(['1','5']) # 先清除對應外鍵關系字段再添加,相當於修改
#易錯點
不是在原位置修改是刪除這條對應記錄,再去添加新紀錄
改
# 改
ret = models.Publish.objects.get(id=2)
models.Book.objects.filter(id=5).update(
title='華麗麗',
#publishs=ret,
publishs_id=1,
)
兩種方式
publishs=ret#使用屬性 對應的值是model對象
publishs_id=1#對應的id值
基於對象的跨表查詢
obj=models.Author.objects.get(name='王洋')和filter用法一樣出來的類型不一樣
obj=models.Author.objects.filter(name='王洋').first()
get和filter的區別
1.filter篩選出來的是queryset對象需要轉成model對象
1.2如果是多個值 需要for循環取值轉換
寫法
a=models.Author.objects.filter()[0]
a查詢出來多個值 這樣使用就只能查出一個
需要for 循環取值
2.get是對應的model對象 可以直接拿來用
反向查詢和正向查詢的區別
1.正向查詢是創建外鍵關系的屬性 在當前表中
用法
obj=models.Author.objects.get(name='王洋')
obj.對應的外鍵屬性名.字段名
2.反向查詢是表中沒有對應創建外鍵關系的屬性
用法·
obj=models.Author.objects.get(name='王洋')
obj.小寫表名.字段名 #obj,表名找到對應表
如果 查詢東西多
1.需要用 注意:正向查和反向查有一些區別,當正相查結果為多個時,直接obj.對應的外鍵屬性名.all()既可以,但是反向查有一些不同obj.小寫表名_set.all()屬性名(字段)_set.all()
取值
2.使用 for循環列表進行對象取值
舉例
obj=models.表名.objects.filter(name='子文')
ret=obj.book_set.all()#查詢出來的是個列表s
for i in ret:
print(i.title)
#注意:正向查和反向查有一些區別,當正相查結果為多個時,直接obj.對應的外鍵屬性名.all()既可以,但是反向查有一些不同obj.小寫表名_set.all()
一對一
# 一對一
正向查詢 對象.屬性
obj = models.Author.objects.filter(name='王洋').first()
ph = obj.au.telephone
print(ph)
# 查一下電話號碼為120的作者姓名
# 反向查詢 對象.小寫的表名
obj = models.AuthorDetail.objects.filter(telephone=120).first()
ret = obj.author.name #陳碩
print(ret)
一對多
# 查詢
# 一對多
# 查詢一下 海狗的慫逼人生這本書是哪個出版社出版的 正向查詢
obj = models.Book.objects.filter(title='海狗的慫逼人生').first()
ret = obj.publishs.name
print(ret) #24期出版社
# 查詢一下 24期出版社出版過哪些書
obj = models.Publish.objects.filter(name='24期出版社').first()
ret = obj.book_set.all() #<QuerySet [<Book: 母豬的產后護理>, <Book: 海狗的慫逼人生>]>
for i in ret:
print(i.title)
多對多
關於多對多正向反向查詢的的解釋
一句話概括
是兩個的表的關系屬性在哪個表里(類) 不是外鍵字段存在哪個表
解釋
1.多對多需要建立在數據之上 因為沒有數據的支持沒辦法建立外鍵,建立外鍵約束
2.多對多采用把關系存在第三張表里
3.基於對象的查詢是根據屬性查詢
4.兩個表的關系屬性在哪個表里 哪個表就是正向查詢
具體事例
# 多對多
如果post請求多個值使用getlist(字段)
寫法
obj=request.POST.getlist("author_id")
#取得的是一個列表
# 海狗的慫逼人生 是哪些作者寫的 -- 正向查詢
obj = models.Book.objects.filter(title='海狗的慫逼人生').first()
ret = obj.authors.all()#可以直接查詢到作者對應的名字 (直接查詢到)
print(ret) #<QuerySet [<Author: 王洋>, <Author: 海狗>]>
for i in ret:
print(i.name)
# 查詢一下海狗寫了哪些書 -- 反向查詢
obj = models.Author.objects.filter(name='海狗').first()
ret = obj.book_set.all()
print(ret)
for i in ret:
print(i.publishs.name)
print(i.title)
return HttpResponse('ok')
基於雙下划線的跨表查詢(join)
原生sql語句寫法
select emp.name from emp inner join dep on dep.id=emp.id where emp.name='技術部';
select emp.name from dep inner join emp on dep.id=emp.id where emp.name='技術部';
在配置里面設置顯示orm的操作的原生sql語句
注意
1.表的查詢先后指定哪個表都可以
2.value里面是什么 查詢出來字典的鍵就是什么,
3.可以查詢出來多個不用.all()
4.屬性__字段 小寫表名__字段都是關聯表字段
寫法
表的查詢先后指定哪個表都可以
#正向查詢
寫法
models.含有外鍵關聯屬性的表名.filter(本表字段=條件).value( 屬性_ _外鍵字段)
海狗的慫逼人生這本書是哪個出版社出版的
#反向查詢
寫法
models.不含有外鍵關聯屬性的表名.filter(小寫表名_ _字段).values('本表字段')
示例
海狗的慫逼人生這本書是哪個出版社出版的
ret = models.Publish.objects.filter(book__title='海狗的慫逼人生').values('name')
print(ret) #<QuerySet [{'name': '24期出版社'}]>
返回多個值
反向查詢返回多個值
寫法
models.不有外鍵關聯屬性的表名.filter(本表字段=條件).value( 小寫表名_ _字段)
示例
查詢一下24期出版社出版了哪些書
#反向查詢返回多個值
ret = models.Publish.objects.filter(name='24期出版社').values('book__title')
print(ret) #<QuerySet [{'book__title': '華麗的產后護理'}, {'book__title': '海狗的慫逼人生'}]>
正向查詢返回多個值
寫法
models.含有外鍵關聯屬性的表名.filter(屬性_ _字段).values('本表字段')
#示例
查詢一下24期出版社出版了哪些書
#正向查詢返回多個值
ret = models.Book.objects.filter(publishs__name='24期出版社').values('title')
print(ret) #<QuerySet [{'title': '華麗的產后護理'}, {'title': '海狗的慫逼人生'}]>
一對一
示例
# 查詢一下王洋的電話號碼
#正向
ret = models.Author.objects.filter(name='王洋').values('au__telephone')
#反向
ret = models.AuthorDetail.objects.filter(author__name='王洋').values('telephone')
# print(ret) #<QuerySet [{'au__telephone': '110'}]> #<QuerySet [{'telephone': '110'}]>
一對多
示例
海狗的慫逼人生這本書是哪個出版社出版的
#正向
ret = models.Book.objects.filter(title='海狗的慫逼人生').values('publishs__name')
print(ret) #<QuerySet [{'publishs__name': '24期出版社'}]>
#反向
ret = models.Publish.objects.filter(book__title='海狗的慫逼人生').values('name')
print(ret) #<QuerySet [{'name': '24期出版社'}]>
#返回多個值
查詢一下24期出版社出版了哪些書
#反向查詢返回多個值
ret = models.Publish.objects.filter(name='24期出版社').values('book__title')
print(ret) #<QuerySet [{'book__title': '華麗的產后護理'}, {'book__title': '海狗的慫逼人生'}]>
#正向查詢返回多個值
ret = models.Book.objects.filter(publishs__name='24期出版社').values('title')
print(ret) #<QuerySet [{'title': '華麗的產后護理'}, {'title': '海狗的慫逼人生'}]>
多對多
示例
海狗的慫逼人生 是哪些作者寫的
正向 返回多個值
authors__name 是屬性__外鍵字段
ret = models.Book.objects.filter(title='海狗的慫逼人生').values('authors__name')
print(ret)
反向 返回多個值
book__title 是小寫表名__外鍵字段
ret = models.Author.objects.filter(book__title='海狗的慫逼人生').values('name')
print(ret) #<QuerySet [{'name': '王洋'}, {'name': '海狗'}]>
補充
related_name
寫法
related_name==xxx 別名 注意用了必須用 以后就代指這個字段
查看原生sql語句的配置
聚合查詢
arregate 聚合
不分組聚合只取一個最大的
注意
1.需要引用模塊
2.聚合查詢是orm結束符
3.查詢出來的值是python的字典
4.不能再繼續篩選
用法
from django.db.models import AVG,SUM,Max
arregate (*args,**kwargs)聚合
1.單個聚合函數
ret=models.表名.objects.all().arregate(AVG('字段名'))
2.多個聚合函數
ret=models.表名.objects.all().arregate(AVG('字段名'),Max('字段名'))
示例
# 計算所有圖書的平均價格
from app01.models import Book
from django.db.models import Avg
models.Book.objects.all().aggregate(Avg('price'))
Book.objects.all().aggregate(Avg('price'))
#或者給它起名字(查詢出來字典顯示的鍵):aggretate(a=Avg('price'))
{'price__avg': 34.35}
分組查詢
annotate其實就是對分組結果的統計
分組執行的順序
例如 models.Book.objects.annotate(max=Max('price')).arregate(Avg('max'))
#分組完進一步用聚合函數篩選
1.以book的id進行分組
2.annotate(max=Max('price'))#以最高的價錢進行統計起一個別名max
3.arregate(Avg('max')#把篩選完的表名 進一步進行篩選
4.arregate是orm的最后結束符
跨表分組查詢本質就是將關聯表join成一張表,再按單表的思路進行分組查詢,,既然是join連表,就可以使用咱們的雙下划線進行連表了。
注意
1.查詢的平均值的字段必須要起別名 用values進行別名取值 vlues('別名')
2.values是分組的依據 可以屬性可以字段
3.可以直接 model.表名.objects.annotate #默認依據表名的id主鍵進行分組
原生sql語句
#單表
select dep,Count(*) from emp group by dep;
#多表
select dep.name,Count(*) from emp left join dep on emp.dep_id=dep.id group by dep.id;
用法
兩種寫法
第一種 指定字段分組
models.分組的表名.objects.values('分組的字段').annotate(別名=聚合函數('字段名')).values('別名')
#別名取值
#示例
models.emp.objects.values("dep").annotate(c=Count("id")).values('c')
#對象.values()
models.dep.objetcs.values("id").annotate(c=Count("emp")).values("name","c")
第二種 不指定values分組默認id進行分組
models.分組的表名.objects.annotate(別名=聚合函數('字段名')).values('別名')
# 示例
ret = models.Publish.objects.annotate(a=Avg('book__price')).values('a')
# print(ret) #<QuerySet [{'a': None}, {'a': 71.166667}, {'a': 6.0}]>
F查詢
比較同一個 model (同一個表)實例中兩個不同字段的值。
可以查詢出來所有進行修改
用法
from django.db.models import Avg, Sum, Max, Min, Count,F
F('本表字段')
#示例
from django.db.models import Avg, Sum, Max, Min, Count,F
#查詢一下評論數大於點贊數的書
兩個字段進行比較
ret = models.Book.objects.filter(comment__gt=F('good'))
print(ret)
查詢出所有本表字段進行修改
將所有書的價格上調100塊
# models.Book.objects.all().update(
# price=F('price')+100
# )
Q查詢
filter()
等方法中的關鍵字參數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR
語句),你可以使用Q 對象
。
寫法
或查詢
q()
#注意
models.Book.objects.filter(Q(id=2),price=3)
不能寫在Q()前面會報錯
用法
或| 與& 非~
q()|q()
q()&q()
q()|~q()
1.單層
q('字段'__比較=值)|q('字段'__比較=值)
2.多層嵌套 關鍵字必須 寫在q查詢之后會報錯 想要寫之前就要用q()
#示例
ret = models.Book.objects.filter(Q(id=2)&Q(Q(price__gt=112)|~Q(comment__lte=200)))
print(ret)
orm執行原生sql語句
raw()# 取的是一個對象
寫法
ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
for i in ret:
print(i.id, i.sname, i.haha)
需要for 循環
自定義sql語句
django封裝的
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
ret = cursor.fetchone()
cookie
1.是保存在用戶瀏覽器上的鍵值對,向服務端發送請求時會自動攜帶
2.cookie可以通過 瀏覽器進行cookie刪除
max_age=None 多少秒之后自動失效
expires=None固定的時間
path="/"
domain 可以設置指定域名
http=true 就可以設置cookie 能不能修改
cookie的使用
通過js設置cookie
document.cookie="k1=wy222;path=/"#js寫法
$.cookie("k1","wy222",{path:'/'})#jquery寫法
path的作用
path默認所有域名"/"
1./ 根目錄當前網站的所有的url都能讀取到此值
2.""只能在當前頁面訪問的到此數據
3./index/,只能在/index/xxx的網頁中查看
后台默認在根目錄也就是path=’/’
cookie修改需要指定根目錄 "decument.cookie=121313 path= /"
不寫默認path為""
不安全 可以使用瀏覽器進行修改可以獲取到敏感數據
簡單博客登陸
檢查數據的庫的密碼是否正確
view.py寫法
def login(requset):
if requset.method=='GET':
return render(requset,'login.html',{'age':18})
# 獲取用戶提交的用戶名和密碼
user = request.POST.get('user')#獲取鍵對應的值
pwd = request.POST.get('pwd')#獲取鍵對應的值
#判斷用戶名和密碼
user_object=models.UserInfo.objects.filter(username=user,password=pwd).first()
if user_object:
# 用戶登錄成功
result = redirect('/index/')
#在寫入一個cookie
result.set_cookie('xxxxxxxx',user)
return result
# 用戶名或密碼輸入錯誤
return render(request,'login.html',{'error':'用戶名或密碼錯誤'})
def index(request):
"""
博客后台首頁
:param request:
:return:
"""
#拿到存到瀏覽器的cookie
user = request.COOKIES.get('xxxxxxxx')
if not user:#如果沒有獲得
return redirect('/login/')#返回頁面
return render(request,'index.html',{'user':user})
為防止通過url直接訪問頁面 使用cookie進行判斷
應用場景
- 投票
- 每頁默認顯示數據
- 登陸認證
避免把敏感數據存儲在瀏覽器使用到了session
session
依賴cookie
是一種存儲數據的方式,依賴於cookie,實現本質:
用戶(瀏覽器(向服務端發送請求,服務端做兩件事:
1.生成隨機字符串;
2.為此用戶開辟一個獨立的空間來存放當前用戶獨有的值(數據).
在空間中如何想要設置值:
request.session['x1'] = 123
request.session['x2'] = 456
request.session['x2']如果不存在會報錯keyerror錯誤#在空間中取值:
request.session.get('x2')
#視圖函數中的業務操作處理完畢,給用戶響應,在響應時會 將隨機字符串存儲到用戶瀏覽器的cookie中
session中的數據是根據用戶相互隔離每個都是獨立的
session中的數據是根據用戶相互隔離.
示例
def login(request): # 獲取用戶提交的用戶名和密碼
user = request.POST.get('user') request.session['user_name'] = user
def index(request): print(request.session['user_name'])
應用場景
- 可以權限判斷 放置權限
- 短信驗證過期
- 登陸認證
session和cookie的區別
cookie是存儲在客戶端瀏覽器上的鍵值對,發送請求時瀏覽器會自動攜帶
session是一種存儲數據方式 依賴cookie基於cookie實現,將數據存儲在服務端 (django默認)
擴展 修改session默認存儲位置
-
- (默認在數據庫) - 小系統:默認放在數據庫即可. 大系統:緩存(redis)
-
文件
-
SESSION_ENGINE = 'django.contrib.sessions.backends.file' #引擎把session放入文件中 SESSION_FILE_PATH = '/ssss/' #在根目錄的/ssss生成一個隨機文件
-
緩存(內存)
-
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_CACHE_ALIAS = 'default' CACHES = { 'default': { 'BACKEND':'django.core.cache.backends.locmem.LocMem Cache', 'LOCATION': 'unique-snowflake', } }
-
緩存(redis)
-
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' SESSION_CACHE_ALIAS = 'default' 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} # "PASSWORD": "密碼", } } }
簡單博客登陸(session)
檢查數據的庫的密碼是否正確
from django.shortcuts import render,redirect
from app01 import models
def login(request):
"""
用戶登錄
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html')
# 獲取用戶提交的用戶名和密碼
user = request.POST.get('user')
pwd = request.POST.get('pwd')
print(user,pwd)
# 去數據庫檢查用戶名密碼是否正確
user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
if user_object:
request.session['user_name']=user_object.username#名字存入session
request.session['user_id']=user_object.pk#id存入session
return redirect('/index/')#重定向頁面
# 用戶名或密碼輸入錯誤
return render(request,'login.html',{'error':'用戶名或密碼錯誤'})
def index(request):
"""
博客后台首頁
:param request:
:return:
"""
name=request.session.get("user_name")
if not name:
return redirect('/login/')
return render(request,'index.html',{'user':name})
··············································
簡化版為了減少登陸驗證的重復
··············································
from django.shortcuts import render,redirect
from app01 import models
def login(request):
"""
用戶登錄
:param request:
:return:
"""
if request.method == 'GET':
return render(request, 'login.html')
# 獲取用戶提交的用戶名和密碼
user = request.POST.get('user')
pwd = request.POST.get('pwd')
print(user,pwd)
# 去數據庫檢查用戶名密碼是否正確
user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
if user_object:
request.session['user_name']=user_object.username#名字存入session
request.session['user_id']=user_object.pk#id存入session
return redirect('/index/')#重定向頁面
# 用戶名或密碼輸入錯誤
return render(request,'login.html',{'error':'用戶名或密碼錯誤'})
def auth(func):#防止多次判斷 簡化判斷 裝飾器
@functools.wraps(func)
def inner( request,*args,**kwargs):
name = request.session.get("user_name")
if not name:
return redirect('/login/')
return func(request,*args,**kwargs)
return inner
@auth
def index(request):
"""
博客后台首頁
:param request:
:return:
"""
return render(request,'index.html')
操作session
# 設置(添加&修改) request.session['x1'] = 123 request.session['x2'] = 456
# 讀取
request.session['xx'] #讀取不到會報key error錯誤
request.session.get('xx')#讀取不到值返回none (不報錯)
# 刪除
del request.session['xx']
request.session.keys() #獲取當前session的鍵
request.session.values() #獲取當前session的值
request.session.items() #獲取當前session的鍵值對
# 設置會話Session和Cookie的超時時間
request.session.set_expiry(value)
* 如果value是個整數,session會在些秒數后失效。
* 如果value是個datatime或timedelta,session就會在這個時間后失效。
* 如果value是0,用戶關閉瀏覽器session就會失效。
* 如果value是None,session會依賴全局session失效策略。
request.session.session_key 獲取sessionid(隨機字符串)的值
擴展 django和session相關的配置
SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在瀏覽器上時的key可以修改,即: sessionid=隨機字符串
SESSION_COOKIE_DOMAIN = None #session的cookie保存的域名(都可以在那個域名,子域名下可用 所有的域名都可用讀取到)
# api.baidu.com /www.baidu.com/ xxx.baidu.com
SESSION_COOKIE_PATH = "/" # Session的cookie 保存的路徑
SESSION_COOKIE_HTTPONLY = True # 是否 Session的cookie只支持http傳輸 只能讀不能修改
SESSION_COOKIE_AGE = 1209600 # Session的 cookie失效日期(2周)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否關閉瀏覽器使得Session過期
SESSION_SAVE_EVERY_REQUEST = False # 是否每 次請求都保存Session,默認修改之后才保存
request會刷新 ture 按照最后一次刷新時間 false 會在兩周之后過期
django中的session如何設置過期時間?
SESSION_COOKIE_AGE = 1209600 # Session的 cookie失效日期(2周)