Django ORM
- 2022 年 2 月 27 日
- 筆記
- django, Django ORM
Django ORM
ORM:對象映射關係程序
通過orm將編程語言的對象模型和數據庫的關係模型建立映射關係,這樣我們在使用編程語言對數據庫進行操作的時候可以直接使用編程語言的對象模型進行操作就可以了,而不用直接使用sql語言;
python與MySQL映射關係
Python | 映射 | MySQL |
---|---|---|
類 | ——-> | 表 |
對象 | ——-> | 表裏面的數據 |
對象點屬性 | ——-> | 字段對應的值 |
ORM實操之數據庫遷移
✨我們的模型類需要寫在應用下的models.py文件中
# SQL原生語句和ROM創建的區別
class User(models.Model):
# id int primary key auto_increment
id = models.AutoField(primary_key=True)
# name varchar(32)
name = models.CharField(max_length=32) # CharField必須要加max_length參數
# age int
age = models.IntegerField()
✨✨✨數據庫遷移命令(重點)
- 只要修改了
models.py
文件執行了和數據庫相關的命令,就重新執行下面這兩條命令:python3 manage.py makemigrations、python3 manage.py migrate - 或者打開Tools,點擊
RUN manage.py Task
,輸入makemigrations
1.將數據庫修改操作先記錄到"本本"(對應應用下的migrations文件夾)
python3 manage.py makemigrations
# 執行完,產生一個文件,用來記錄
2.真正的執行數據庫遷移操作,同步到數據庫
python3 manage.py migrate
# 執行完會在數據庫中產生Django所需的依賴表,自動創建的
# 自己創建的表user以'應用名_表名'的形式創建,app01_user
✨✨不指定id字段和主鍵等,ORM會自動創建id
# 如果你不指定主鍵 那麼orm會自動幫你創建一個名為id的主鍵字段
class user(models.Model):
username = models.CharField(max_length=32)
# 就不用寫id了···,需要定製id,就寫上~
ORM實操之字段的修改
# 原來的表
class user(models.Model):
username = models.CharField(max_length=32)
# 增加字段(兩種方法)
password = models.IntegerField('密碼',null=True) # 該字段可以為空
is_delete = models.IntegerField(default=0) # 默認值
# 修改字段
直接改代碼,然後執行makemigrations,數據庫遷移
# 刪除
直接刪除或者注釋掉代碼,然後執行makemigrations,數據庫遷移
ORM實操之數據的增刪改查
查詢操作
username = request.POST.get('username')
# 獲取用戶post從頁面提交的數據,username是獲取到用戶提交的數據
# 1.查詢數據
# select * from user where name=username;
user_obj_list = models.User.objects.filter(name=username) # 獲取到的是列表,看成列表套數據對象
obj_info = user_obj_list[0]
# 獲取id、name、age
print(obj_info.id,
obj_info.name,
obj_info.age) # 1 hammer 18
# 或這下面這樣提取列表數據也可以
user_obj = models.User.objects.filter(name=username).first()
'''如果用戶提交的信息不存在返回None'''
# 登錄功能示例
user_check = models.User.objects.filter(name=username,password=password).first() # 等價於select * from user where name=username and pwd = password
if user_check:
return HttpResponse('登錄成功')
# 2.添加數據
# insert into user(name,pwd) values(username,password);
models.User.objects.create(name=username,pwd=password)
# 3.查詢所有的數據,展示所有數據到前端頁面,通過for循環在html頁面獲取表數據
# select * from user;
models.User.objects.all() # 返回列表[obj1,obj2,obj3,obj4]
# 4.修改數據
models.User.objects.filter(id=edit_id).update(name=username,pwd=password)
# 或者
edit_obj.name = username
edit_obj.pwd = password
edit_obj.save()
# 5.刪除數據
models.User.objects.filter(id=delete_id).delete()
數據庫同步
-
數據庫遷移命令,鏈接
python manage.py makemigrations
-
inspectdb 表名
語句反向操作數據庫,反向輸出sql語句對應的類
ps:如果inspectdb
後不跟表名,那麼就會將該數據庫內的所有表反向解析成類(python語句)
# 數據庫裏面已經有一些表,我們如何通過django orm操作?
1.照着數據庫表字段自己在models.py
2.django提供的反向同步
操作:
1.先執行數據庫遷移命令 完成鏈接
python manage.py makemigrations
2.查看代碼
python manage.py inspectdb
class Userinfo(models.Model):
id = models.IntegerField(blank=True, null=True)
name = models.CharField(max_length=32, blank=True, null=True)
pwd = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'userinfo'
ORM創建表關係
表與表之間的關係有一下三種:
一對多、多對多、一對一,沒關係暫且排外,下面演示如何通過ORM來創建外鍵確立表關係~
ORM創建外鍵字段的位置:
- 一對多:創建在多的一方
- 一堆一:創建在任何一方都可以,但是推薦創建在查詢頻率較高的表中
- 多對多(兩種方式):
- 自己創建第三張表
- 創建在任何一方都可以,但是推薦創建在查詢頻率較高
# 創建書籍表 出版者表 作者表
# 先寫表的基本結構,在考慮表關係如何寫外鍵
# 書籍表
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2) # 總共8位 小數佔2位
# 作者外鍵
Author = models.ManyToManyField(to='Author')
# 出版社外鍵
Publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
Atuhor_Detail = models.OneToOneField(to='Atuhor_Detail',on_delete=models.CASCADE)
# 作者詳情表
class Atuhor_Detail(models.Model):
phone = models.BigIntegerField()
Email = models.EmailField()
# 出版社表
class Publish(models.Model):
name = models.CharField(max_length=32)
address = models.CharField(max_length=32)
注意:
- 外鍵不需要寫id,會自動添加id,比如author_id,不需要寫_id,orm自動補充
- ORM自動創建書籍和作者的第三張表,只有多對多關係表被單獨創建出來
-
外鍵被當作虛擬字段,創建表完成後,不會實例化出來,而是告訴了ORM創建第三張表的關係
-
Django2.0版本以上,在創建外鍵和一對一關係的時候,需要添加級聯更新參數
on_delete=models.CASCADE
,不然會報錯,只有OneToOneField和ForeignKey需要寫,ManyToManyField不需要# django 升級到2.0之後,表與表之間關聯的時候,必須要寫on_delete參數,否則會報異常: # TypeError: init() missing 1 required positional argument: 『on_delete』 Atuhor_Detail = models.OneToOneField(to='Atuhor_Detail',on_delete=models.CASCADE) Publish = models.ForeignKey(to='Publish',on_delete=models.CASCADE)
Django請求生命周期流程圖
路由匹配
- 路由:通俗理解為除去ip和port之後的地址
👉「江狗」
在新版本Django2.x中,
url
的路由表示用path
和re_path
代替;模塊的導入由django1.x版本的
from django.conf.urls import url,include
變成現在的Django2.x中的from django.urls import path, re_path, include
Django提供了兩種設計URL的方法: path
和re_path
,它們均支持向視圖函數或類傳遞參數。path
是正常參數傳遞,re_path
是採用正則表達式regex匹配;
path
方法:採用雙尖括號<變量類型:變量名>
或<變量名>
傳遞,例如<int:id>
,<slug:slug>
或<username>
。re_path
方法: 採用命名組(?P<變量名>表達式)
的方式傳遞參數。path
支持匹配的數據類型只有str
,int
,slug
,uuid
四種。一般來說re_path
更強大,但寫起來更複雜一些
簡單示例
'''urls.py'''
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
# 路由匹配
re_path(r'test',views.test),
re_path(r'testadd',views.testadd)
]
'''views.py'''
from django.shortcuts import render,HttpResponse,redirect
# Create your views here.
def test(request):
return HttpResponse('from test')
def testadd(request):
return HttpResponse('from testadd')
'''
這樣匹配的話,相當於使用正則表達式,如果url後面寫test返回fromtest,那麼再寫testadd呢?也會返回from test,這是一種包含關係,需要更明確的去區別,比如寫test/,或者testadd/,建議在末尾加上/
'''
urlpatterns = [
path('admin/', admin.site.urls),
# 限制開頭
re_path(r'^test/',views.test),
# 限制開頭和結尾
re_path(r'^testadd/$',views.testadd)
]
- Django特性:如果在url後面寫路徑沒有寫
/
,那麼會自動補充/
,相當於跳轉頁面,如果不想使用該特性,可以在setting.py文件中取消:APPEND_SLASH = False
下例中,我們分別以path
和re_path
定以了兩個urls,它們是等效的,把文章的id(整數類型)傳遞給了視圖。re_path
里引號前面的小寫r
表示引號里為正則表達式, ^
代表開頭,$
代表以結尾,\d+
代表正整數
# blog/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
path('blog/articles/<int:id>/', views.article_detail, name = 'article_detail'),
re_path(r'^blog/articles/(?P<id>\d+)/$', views.article_detail, name='article_detail'),
]
# blog/views.py
def article_detail(request, id):
# 展示某篇文章
在使用path
和re_path
方法設計urls需注意:
- url中的參數名要用尖括號,而不是圓括號;
- 匹配模式的最開頭不需要添加斜杠
/
,但建議以斜杠結尾; - 使用
re_path
時不一定總是以$
結尾,有時不能加。比如下例中把blog.urls
通過re_path
加入到項目urls中時就不能以$
結尾,因為這裡的blog/
並不是完整的url,只是一個開頭而已。
from django.urls import include, re_path
urlpatterns = [
re_path(r'^blog/', include('blog.urls')),
...
]
無名分組
涉及到urls.py寫對應關係,無名分組後如果不給視圖函數傳參,那麼就會報錯
通俗理解:路由使用正則,正則匹配加括號分組,當作了視圖函數的第二個位置參數
'''urls.py'''
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 匹配數字
# re_path(r'^test/[0-9]{4}/$',views.test),
# 無名分組,匹配1個到多個正整數
re_path(r'^test/(\d+)/$',views.test),
]
'''views.py'''
def test(request,num):
print(num) # 1234
return HttpResponse('from test')
'''路由使用正則,正則匹配加括號分組,當作了視圖函數的第二個位置參數'''
有名分組
在使用路由的時候,正則表達式可以起別名,別名當作關鍵字參數傳給視圖函數;
沒有按關鍵字參數寫報錯
按別名,傳參
'''urls.py'''
urlpatterns = [
re_path(r'^test/(?P<id>\d+)/$',views.test),
]
'''views.py'''
def test(request,id):
print(id)
return HttpResponse('from test')
# 有名分組,將匹配到的數字命名成id,當成關鍵字參數傳給視圖函數
注意:
-
無名分組和有名分組不能混合使用
re_path(r'^test/(\d+)/(?P<id>\d+)/$',views.test) def test(request,a,id): print(a,id) return HttpResponse('from test') # 這樣不可以
-
相同分組可以混合使用
re_path(r'^testadd/(\d+)/(\d+)/$',views.testadd) def testadd(request,*args,**kwargs): return HttpResponse('from testadd') # 這樣使用相同分組是可以的
反向解析
反向解析解決了當路由頻繁變化的時候,html界面上的連接地址實現動態解析;
'''urls.py'''
# 1、給路由與視圖函數對應關係添加一個別名
from django.contrib import admin
from django.urls import path,re_path,include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 反向解析
path('index/',views.index,name='index_name'),
path('home/',views.home)
]
'''views.py'''
from django.shortcuts import HttpResponse,render,reverse
def index(request):
return HttpResponse('from index')
def home(request):
print(reverse('index_name'))
# 或者
# return redirect('index_name')
return render(request,'home.html')
'''home.html'''
<a href="{% url 'index_name' %}">111</a>
<a href="{% url 'index_name' %}">111</a>
<a href="{% url 'index_name' %}">111</a>
總結:
當路由頻繁變化的時候,html界面上的連接地址如何做到動態解析
# 1.給路由與視圖函數對應關係添加一個別名(名字自己指定 只要不衝突即可)
url(r'^index/',views.index,name='index_name')
# 2.根據該別名動態解析出一個結果,該結果可以直接訪問到對應的路由
前端
<a href="{% url 'index_name' %}">111</a>
後端
from django.shortcuts import reverse
reverse('index_name')
ps:redirect括號內也可以直接寫別名
無名分組和有名分組反向解析
如果有分組的情況,不寫數字參數會報錯
'''路由'''
urlpatterns = [
path('admin/', admin.site.urls),
# 反向解析
re_path(r'index/(\d+)/',views.index,name='index_name'),
path('home/',views.home)
]
'''後端'''
def index(request):
return HttpResponse('from index')
def home(request):
print(reverse('index_name'))
return render(request,'home.html')
'''前端'''
<a href="{% url 'index_name' %}">111</a>
無名分組反向解析
# 無名分組
1、起別名
url(r'^index/(\d+)/',views.index,name='index_name')
2、前端
<a href="{% url 'index_name' 1 %}"></a> # 只要給個數字即可
3、後端
reverse('index_name',args=(1,)) # 只要給個數字即可
有名分組反向解析
# 無名分組
1、起別名
url(r'^index/(?P<id>\d+)/',views.index,name='index_name')
2、前端
<a href="{% url 'index_name' id=1 %}"></a> # 只要給個數字即可
3、後端
reverse('index_name',kwargs={'id':1}) # 只要給個數字即可
總結:
- 再次驗證了,分組對應傳的參數,無名對應位置參數,有名對應關鍵字參數
- 分組了,一定要記得傳參數,無名分組傳位置參數,有名分組傳關鍵字參數
- 需要注意的是django2.0版本的變化,path和re_path寫路由,django1.0版本用到的是url寫路由,在前端頁面寫反向解析的時候用到的是
url
和Django1.0一樣,一定要區別開來 - 上面用參數用數字代替,在實際應用中經常使用數據主鍵值(視圖函數的參數)
def index(request,id):
return render(request,'home.html')
# id一般是獲取到的主鍵值