Django ORM

HammerZe發表於2022-02-27

Django ORM

image

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()

資料庫同步

  1. 資料庫遷移命令,連結

    python manage.py makemigrations

  2. 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自動補充

image

  • ORM自動建立書籍和作者的第三張表,只有多對多關係表被單獨建立出來

image

  • 外來鍵被當作虛擬欄位,建立表完成後,不會例項化出來,而是告訴了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之後的地址

?path官網文件

?URL排程

?“江狗”

在新版本Django2.x中,url的路由表示用pathre_path代替;

模組的匯入由django1.x版本的from django.conf.urls import url,include變成現在的Django2.x中的from django.urls import path, re_path, include

Django提供了兩種設計URL的方法: pathre_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

image

下例中,我們分別以pathre_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):
    # 展示某篇文章

在使用pathre_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')

'''路由使用正則,正則匹配加括號分組,當作了檢視函式的第二個位置引數'''

有名分組

在使用路由的時候,正規表示式可以起別名,別名當作關鍵字引數傳給檢視函式;

沒有按關鍵字引數寫報錯

image

按別名,傳參

'''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>

image

無名分組反向解析

# 無名分組
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一般是獲取到的主鍵值

?參考文獻:大江狗

相關文章