對象關係映射(英語:(Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程序技術,用於實現面向對象編程語言裏不一樣類型系統的數據之間的轉換 。從效果上說,它實際上是建立了一個可在編程語言裏使用的--「虛擬對象數據庫」。前端
簡單一句話來講:就是把數據庫的表映射爲一個個對象,對對象的操做會被映射成SQL語句,在數據庫執行。
python
django默認支持sqlite,mysql, oracle,postgresql數據庫。mysql
在django的項目中會默認使用sqlite數據庫,在settings裏有以下設置:git
DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
下面是MySQL的配置sql
DATABASES = { 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'books', # 你的數據庫名稱 - 'USER': 'root', # 你的數據庫用戶名 - 'PASSWORD': '', # 你的數據庫密碼 - 'HOST': '',- # 你的數據庫主機,留空默認爲localhost - 'PORT': '3306', # 你的數據庫端口 } }
參數以下:數據庫
django可使用以下mysql的驅動程序:django
MySQLdb(不完美支持Python3) mysqlclient (官方建議) PyMySQL(純python的mysql驅動程序)
建議使用:編程
django基礎配置部分已經描述了mysqlclient的安裝,這裏介紹如何使用pymysql,首先須要安裝緩存
pip install pymysql
下一步只須要找到項目名文件下的__init__,在裏面寫入:
import pymysql pymysql.install_as_MySQLdb()
便可完成配置
咱們所說的ORM主要分爲兩種:
DB First
數據庫裏先建立數據庫表結構,根據表結構生成類,根據類操做數據庫Code First
先寫代碼,執行代碼建立數據庫表結構主流的orm都是code first。django 的orm也是code first,因此本質上分爲兩部分:
如今有一張表,主要字段以下:
對應的models中的映射類爲:
from django.db import models # 導入models,django提供的對象包含不少建表的方法 # Create your models here. class UserInfo(models.Model): # 須要繼承models.Model class Meta: db_table = 'userinfo' # 在數據庫中生成的代表 id = models.AutoField(primary_key=True,null=False,verbose_name='ID') name = models.CharField(max_length=4,null=False,verbose_name='用戶名') password = models.CharField(max_length=64,null=False,verbose_name='密碼') email = models.EmailField(null=False,verbose_name='郵箱') # AutoField : 自增字段,相似於mysql的int字段加auto_increment屬性 # CharField:可變長字段,相似於mysql的varchar類型,須要指定長度 # EmailField:郵件字段,僅僅提供給 django admin進行約束使用,映射到MySQL上,根本上也是字符串類型 # null:是否爲空,通用參數,默認爲否。 # verbose_name:django admin上對錶操做時,顯示的字段名稱 # primary_key:主鍵 # max_length:針對於CharField字段,標示其長度
部分字段類型及說明以下:
字段名稱 | 含義 |
---|---|
AutoField(Field) | int自增列,必須填入參數 primary_key=True |
BigAutoField(AutoField) | bigint自增列,必須填入參數 primary_key=True 注:當model中若是沒有自增列,則自動會建立一個列名爲id的列 |
SmallIntegerField(IntegerField) | 小整數 -32768 ~ 32767 |
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) | 正小整數 0 ~ 32767 |
IntegerField(Field) | 整數列(有符號的) -2147483648 ~ 2147483647 |
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) | 正整數 0 ~ 2147483647 |
BigIntegerField(IntegerField) | 長整型(有符號的) -9223372036854775808 ~ 9223372036854775807 |
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 |
URLField(CharField) | 字符串類型,Django Admin以及ModelForm中提供驗證 URL |
SlugField(CharField) | 字符串類型,Django Admin以及ModelForm中提供驗證支持 字母、數字、下劃線、鏈接符(減號) |
CommaSeparatedIntegerField(CharField) | 字符串類型,格式必須爲逗號分割的數字 |
UUIDField(Field) | 字符串類型,Django Admin以及ModelForm中提供對UUID格式的驗證 |
FilePathField(Field) | 字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功 |
FileField(Field) | 字符串,路徑保存在數據庫,文件上傳到指定目錄 |
ImageField(FileField) | 字符串,路徑保存在數據庫,文件上傳到指定目錄 |
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進制小數 |
BinaryField(Field) | 二進制類型 |
django中提供了不少參數對字段進行控制
通用類:
屬性 | 含義 |
---|---|
null | 是否能夠爲空 |
default | 默認值 |
primary_key | 主鍵 |
db_column | 列名 |
db_index | 索引(bool) |
unique | 惟一索引 |
時間日期類:
屬性 | 含義 |
---|---|
unique_for_date | 對日期字段來講,表示只對時間作索引 |
unique_for_month | 對日期字段來講,表示只對月份作索引 |
unique_for_year | 對日期字段來講,表示只對年作索引 |
auto_now | 不管是你添加仍是修改對象,時間爲你添加或者修改的時間。 |
auto_now_add | 爲添加時的時間,更新對象時不會有變更。 |
django admin相關:
屬性 | 含義 |
---|---|
choices | 在django admin中顯示下拉框,避免連表查詢(好比用戶類型,能夠在存在內存中) |
blank | 在django admin中是否能夠爲空 |
verbose_name | 字段在django admin中顯示的名稱 |
editable | 在django admin中是否能夠進行編輯,默認是true |
error_message | 當在django admin中輸入的信息不匹配時,字段的提示信息 |
help_text | 在django admin中輸入框旁邊進行提示 |
validators | 在django admin中自定義規則限制 |
django中提供了不少的字段類型,大部分都是提供給django admin 來作驗證的,實際體如今數據庫中的,大部分都是字符串類型。
GenericIPAddressField(Field)
:字符串類型,Django Admin以及ModelForm中提供驗證 Ipv4和Ipv6
參數:
FilePathField(Field)
:字符串,Django Admin以及ModelForm中提供讀取文件夾下文件的功能
參數:
FileField(Field)
:字符串,路徑保存在數據庫,文件上傳到指定目錄
參數:
ImageField(FileField)
:字符串,路徑保存在數據庫,文件上傳到指定目錄
參數:
DecimalField(Field)
:10進制小數
參數:
在類內部定義內部類Meta,因爲設置映射的表的元數據信息,
class User(models.Model): class Meta: db_name = 'user' id... name...
這裏的Meta的類屬性db_name,表示生成的數據庫表名稱爲'user',更多的參數有:
屬性信息 | 含義 |
---|---|
db_tablespace | 有些數據庫有數據庫表空間,好比Oracle。你能夠經過db_tablespace來指定這個模型對應的數據庫表放在哪一個數據庫表空間。 |
managed | 默認值爲True,Django能夠對數據庫表進行 migrate或migrations、刪除等操做,若是爲False的時候,不會對數據庫表進行建立、刪除等操做。能夠用於現有表、數據庫視圖等,其餘操做是同樣的。 |
ordering | 告訴Django模型對象返回的記錄結果集是按照哪一個字段排序的 |
unique_together | 設置兩個字段保持惟一性時使用 |
verbose_name | 給模型類起一個更可讀的名字 |
經常使用的就是db_table,用於指定生成的表的名稱
前面咱們已經編寫了對應數據庫表的類,這裏咱們將進行實例化(建立數據庫對應的表)。利用django提供的命令進行數據庫的初始化工做.(以及其餘對數據庫表進行修改的動做,好比修改表結構,字段屬性等,都須要執行以下步驟)
# 進入項目目錄下執行 python manage.py makemigrations # 大體含義是:把類轉換成對應格式的sql語句。 # 建立表 python manage.py migrate # 在數據庫中執行生成的sql語句
注意:
若是沒有生成對應的表文件,那麼須要在settings.py中,註冊你的app
# settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01.apps.App01Config', '你的app名稱' ]
當咱們對已生成的表添加新字段時,會出現以下狀況
ou are trying to add a non-nullable field 'code' to business without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option:
因爲咱們添加字段會影響以前的數據,因此這時django提醒咱們,不能只是單純的添加一個字段,還須要爲該字段指定對應的值,固然有幾種解決辦法:
接下來就須要利用orm來對數據庫的表數據進行增刪改查等基本操做了。
根據django的MTV架構,業務的處理是在views中完成的,那麼對數據庫的查詢邏輯也應該在views中編寫,而咱們定義的數據庫對象在model.py中,那麼在導入以後,才能進行表操做。
django提供了兩種增長數據的方式:一、create,二、save
#------------------------------------- create ------------------------------------- from app01 import models # 從本身的應用中導入models模塊 username = 'daxin' password = 'abc.123' email = 'daixin@qq.com' models.UserInfo.objects.create(name=username,password=password,email=email) # 表對象.objects.create() 用來新增數據 # name,password,email 表示表的字段 # 等號右邊的表示數據 # 傳遞的key也能夠是字典利用**dic進行傳遞,在後面利用form的時候會常用這種方式。 dict = {'usernane':'daxin','password':'abc.123',email='daxin@qq.com'} models.UserInfo.objects.create(**dict) #------------------------------------- save ------------------------------------- from app01 import models # 從本身的應用中導入models模塊 username = 'daxin' password = 'abc.123' email = 'daixin@qq.com' userobj = models.UserInfo(name=username,password=password,email=email) userobj.save() # save的方式是利用對象實例化的傳遞參數後,調用save方法進行保存的 # 利用對象,咱們能夠方便的進行數據修改後,再進行save # 好比 userobj.name = 'dahuaidan' # userobj.save()
注意:利用create新增數據時,會默認返回新增長的數據對象,咱們能夠接受該對象來進行其餘的操做,好比添加多對多關係等
比較經常使用的方式是 利用create進行數據的增長
想要對數據進行刪除,首先須要匹配到數據,而後執行刪除操做,那麼就涉及兩部分:查找,刪除
models.UserInfo.objects.filter(id=1).delete() # 利用filter對數據惟一的字段進行過濾,匹配到刪除的行,也能夠用all()匹配全部數據,進行刪除 # 利用delete方法進行刪除操做
總體思路和刪除是相同的,首先查找到數據,而後對字段進行修改
models.UserInfo.objects.filter(id=1).update(name='dachenzi') # 利用filter過濾到要修改的數據,而後更新它的name字段的值
查詢會返回結果的集,它是django.db.models.query.QuerySet類型。是惰性求值,和sqlalchemy同樣。結果就是查詢的集。同時它也是一個可迭代對象。
返回結果集的方法:
名稱 | 說明 |
---|---|
all() | 獲取全部 |
filter() | 過濾,返回知足條件的數據 |
exclude() | 排除,排除知足條件的數據 |
order_by() | 以什麼字段排序(在字段前面加減號 ,表示倒序) |
values() | 返回一個對象字典的列表,列表的元素是字典,字典內是字段和值的鍵值對 |
values_list() | 返回一個對象字典的列表,列表的元素是元組,字典內是字段和值的鍵值對 |
models.Userinfo.object.all().values('id','username') # 表示只取指定的id和username列 # 結果依舊返回的是QuerySet,可是數據不一樣,體現的形式是一個字典 QuerySet({'id':1,'username':'daxin'},{'id':2,'username':'dachenzi'}......) # 使用value_list('id','username'),體現的形式是一個元組 QuerySet((1,'daxin'),(2,'dachenzi')......)
關於filter:
獲取結果集的方法經過適當的組合能夠鏈式編寫。
返回單個值的方法:
名稱 | 說明 |
---|---|
get() | 僅返回單個知足條件的對象 若是未能返回對象則拋出DoesNotExist異常; 若是能返回多條拋出MultipleObjectsReturned異常 |
count() | 返回當前查詢的總條數 |
first() | 返回第一個對象 |
last() | 返回最後一個對象 |
exist() | 判斷查詢集中是否有數據,若是有則返回True |
# 獲取表裏全部的對象 models.UserInfo.objects.all() # 結果爲一個QuerySet對象(django提供的),能夠理解爲一個列表 # 獲取id爲1的對象 models.UserInfo.objects.filter(id=1) # 相似於sql中的 where查詢條件,結果也是一個QuerySet # 獲取name字段包含da的記錄 models.UserInfo.objects.filter(name__contains='da') # 這裏涉及了萬能的雙下劃線,在後續會進行說明 # get獲取一條數據 models.UserInfo.objects.get(id=1) # 注意get不到數據,會直接拋出異常 # 除了id等於1的其餘數據 models.UserInfo.objects.exclude(id=1)
注意:
PS:因爲filter、all取到的數據默認是 QuerySet
格式,在某些場景下咱們只須要驗證是否取到,咱們能夠直接獲取結果中的第一個數據便可,即在最後添加.first()便可,表示取第一個數據,或者使用.count()來統計匹配到的數據的個數。
module.UserInfo.filter(username='daxin').first()
查詢集對象能夠直接使用索引下標的方式(不支持負索引),至關於SQL語句中的limit和offset子句。
注意使用索引返回的新的結果集,依然是惰性求值,不會當即查詢。
qs = User.objects.all()[20:40] # LIMIT 20 OFFSET 20 qs = User.objects.all()[20:30] # LIMIT 10 OFFSET 20
字段查詢表達式能夠做爲filter()、exclude()、get()的參數,實現where子句。
語法:字段名稱__比較運算符=值
,屬性名和運算符之間使用雙下劃線
比較運算符以下
名稱 | 舉例 | 說明 |
---|---|---|
exact filter(isdeleted=False) |
filter(isdeleted__exact=False) | 嚴格等於,可省略不寫 |
contains | exclude(title__contains='天') | 是否包含,大小寫敏感,等價於like '%天%' |
statswith endswith |
filter(title__startswith='天') | 以什麼開頭或結尾,大小寫敏感 |
isnull isnotnull |
filter(title__isnull=False) | 是否爲null |
iexact icontains istartswith iendswith |
i的意思是忽略大小寫 | |
in | filter(pk__in=[1,2,3,100]) | 是否在指定範圍數據中 |
gt、gte lt、lte |
filter(id__gt=3) filter(pk__lte=6) filter(pub_date__gt=date(2000,1,1)) |
|
year、month、day week_day、hour minute、second |
filter(pub_date__year=2000) | 對日期類型屬性處理 |
雖然Django提供傳入條件的方式,可是不方便,它還提供了Q對象來解決。Q對象是django.db.models.Q,可使用&(and)、|(or)操做符來組成邏輯表達式。 ~ 表示not。
from django.db.models import Q User.objects.filter(Q(pk__lt=6)) # 不如直接寫User.objects.filter(pk<6) User.objects.filter(pk__gt=6).filter(pk_lt=10) # 與 User.objects.filter(Q(pk_gt=6) & Q(pk_lt=10)) # 與 User.objects.filter(Q(pk=6) | Q(pk=10)) # 或 User.objects.filter(~Q(pk__lt=6)) # 非
可以使用&|和Q對象來構造複雜的邏輯表達式
咱們常說的表與表之間的關係有:一對1、一對多、多對多,下面分別說明
表示當前表的某個字段的值,來自於其餘表,好比人員表和部門表,在人員表中利用一對多外鍵關係標明該員工屬於哪一個部門。
user_type = models.ForeignKey('表名',to_field='字段') # 默認會自動關聯對方表的ID字段(主鍵),手動指定的話必須是惟一列才行。
注意:
什麼叫正向查詢?仍是拿人員表和部門表舉例,外鍵關係存在人員表中,那麼咱們經過人員表利用表中的外鍵字段就能夠查詢到該人員的部門信息,我通常稱之爲正向查詢。
一對多跨表查詢例子:
# -------------------- models.py -------------------- from django.db import models class Business(models.Model): class Meta: db_table='business' caption = models.CharField(max_length=32) code = models.CharField(max_length=16,default='SA') class Host(models.Model): class Meta: db_table='host' nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=16) ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) port = models.IntegerField() b = models.ForeignKey(to='Business',to_field='id') # 外鍵關聯Business表 # -------------------- views.py -------------------- def host(request): v1 = models.Host.objects.all() return render(request,'host.html',{'v1':v1}) # -------------------- host.html -------------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <p>主機信息</p> {% for row in v1 %} <p>{{ row.nid }} - {{ row.hostname }} - {{ row.ip }} - {{ row.port }} - {{ row.b_id }} - {{ row.b.caption }} - {{ row.b.code }}</p> # 經過b對象,進行跨表查詢主機業務線相關信息 {% endfor %} </div> </body> </html>
這裏經過row.b_id 和經過b對象來獲取 row.b.id 結果是相同的,區別在於使用b對象,會多一次sql查詢
PS:當多個表進行及聯跨表,那麼均可以經過表的外鍵字段使用點來進行跨表訪問(或者使用雙下劃線)
雙下劃線和點跨表的不一樣之處在於:
# -------------- 利用點進行跨表查詢 -------------- # 若是我只想獲取主機地址和所屬業務線使用點跨表的話 v2 = models.Host.objects.all() return render(request,'host.html',{'v2':v2}) # 前端代碼 {% for row in v2 %} <p>{{ row.hostname }} - {{ row.b.caption }}</p> {% endfor %} # 咱們只用了兩個字段,卻取出了相關的全部數據。 # 是否能夠在查詢階段只取出所需字段便可?使用雙下劃線__便可 # -------------- 利用雙下劃線跨表查詢 -------------- v3 = models.Host.object.all().values('hostname','b__caption') # 咱們能夠看到在做爲value的條件語句,經過b對象配合雙下劃線就進行了一次跨表查詢 # 前端代碼 <div> {% for row in v2 %} <p>{{ row.hostname }} - {{ row.b__caption }}</p> # 只須要向字典同樣便可。 {% endfor %} </div>
在django內部,它之因此能識別__,是由於,它在處理values條件時,會使用__做爲分隔符,分隔後再進行相關處理
不少時候咱們會有另外一種需求,查詢一個部門下的全部員工,這時部門表中沒有外間字段關聯人員表啊,該怎麼查?實際上是能夠的,我把這個查詢方式稱之爲反向查詢。
在創建外鍵關係時,不只會在外鍵所在表中產生外鍵所關聯表的對象,在所關聯表中也會產生一個關聯表的對象(可能有點繞,沒找到更好的表達方式),這個對象通常有兩種體現方式:
通常用在values,values_list看成條件作字段過濾
通常看成對象,看成字段處理
def index(request): users = models.User.objects.all() departments = models.Department.objects.values('user__name') # 跨到管理本表的user表中,查找對應的user的名稱,利用的是join 格式 for dep in departments: print(dep) # {'user__name': 'daxin'} # {'user__name': 'dachenzi'} # {'user__name': 'hello'} # {'user__name': 'xiaoming'} # {'user__name': 'xiaochen'} return HttpResponse('ok')
對應的sql語句爲:
def index(request): departments = models.Department.objects.filter(pk=1) for dep in departments: for username in dep.user_set.values('name'): # user_set指代的是關聯的user表對象 print(username) # {'name': 'daxin'} # {'name': 'dachenzi'} # {'name': 'hello'} return HttpResponse('ok')
對應的sql語句爲:
前面說了一對多的狀況,這裏還有一種狀況叫多對對,好比一個主機能夠關聯多個業務線,不一樣的業務線能夠關聯多個主機,因此這裏,業務線和主機的關係爲多對多,在多對多的狀況下,有須要一張額外的表來表示對應關係,這裏有兩種狀況來建立這張關係表。
手動建立,故名思議,咱們須要手動的建立一張關係表,而後建立兩個ForeignKey字段(一對多),關聯兩張表便可。
# 業務線表 class Business(models.Model): caption = models.CharField(max_length=32) code = models.CharField(max_length=16,default='SA') # 主機表 class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=16) ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) port = models.IntegerField() # 多對多關係表 class Application(models.Model): h = models.ForeignKey(to='Host',to_field='nid') # 關聯主機id,字段名爲h_id,同時存在對象h,存放對應的Host信息 b = models.ForeignKey(to='Business',to_field='id') # 關聯業務線id,字段名爲b_id,同時存在對象b,存放對應的Business信息
PS:一共手動建立三張表,能夠利用建立的Application關係表來直接操做多對多關係。
在Django中,還存在一種方式爲自動建立, 經過django提供的ManyToMany關鍵字建立。
# 業務線表 class Business(models.Model): caption = models.CharField(max_length=32) code = models.CharField(max_length=16,default='SA') # 主機表 class Host(models.Model): nid = models.AutoField(primary_key=True) hostname = models.CharField(max_length=16) ip = models.GenericIPAddressField(protocol='ipv4',db_index=True) port = models.IntegerField() business = models.ManyToManyField('Business') # 經過manytomany字段建立多對多關係
注意:
手動建立關係表的狀況下,因爲含有第三張表對應的class,那麼咱們能夠直接使用這個class對關係表進行操做,可是多對多的狀況下沒有關係表的class,因此咱們須要經過其餘辦法來操做。
方法 | 含義 |
---|---|
add() | 添加關係 |
remove() | 刪除關係 |
clear() | 清除全部關係 |
all() | 獲取對應的全部關係對象(在查詢時這裏可使用all,filter,get等,就像查找過濾其餘數據同樣) |
models.Host.objects.filter(nid=1).first().business.add(1,2) # 添加兩條多對多關係 1(host) --> 1(business),1(host) --> 2(business) models.Host.objects.filter(nid=1).first().business.remove(2) # 刪除一條多對多關係 1 -x-> 2 models.Host.objects.filter(nid=1).first().business.clear() # 清除nid爲1的全部的多對多關係
下面是一個小栗子:
# models.py class Bussiness(models.Model): class Meta: db_table = 'bussiness' bus_id = models.AutoField(primary_key=True) bus_name = models.CharField(max_length=64, null=False) class Host(models.Model): class Meta: db_table = 'host' host_id = models.AutoField(primary_key=True) host_name = models.CharField(max_length=64, null=False) host_ip = models.GenericIPAddressField(protocol='both') bus = models.ManyToManyField(Bussiness) # views.py def index(request): hosts = models.Host.objects.all() for host in hosts: for bus in host.bus.all(): # 經過bus對象獲取它關聯的全部bussiness實例記錄 print(host.host_name, host.host_ip, bus.bus_name) return HttpResponse('ok')