Django-ORM框架


對象關系映射模型是通過面向對象的方式來操作數據庫,這就需要對應的關系映射,數據中可以分為庫,表,字段信息,一條條數據,而需要用面向對象的關系去對應。於是就有了下面對應關系。

數據庫  --   面向對象模型
  表   <-->    類
 字段  <-->   類屬性
 記錄  <-->   每個實例

Django中的關系映射

使用面向對象的方式描述數據庫的關系模型,Django采用了以下的方式。

class Employees(models.Model):   # 類名
    class Meta:                  # Meta類中指定表元數據
        db_table = "Employees"    # 指定數據庫中的表名,否則為類名小寫

    # 每一個model的Field類型屬性都對應數據庫表中的一條字段。數據類型通過不同類型的Field屬性指定,約束條件通過參數傳遞的方式
    emp_no = models.AutoField(primary_key=True, null=False)  
    birth_data = models.DateField(null=False)                # 對應日期類型
    first_name = models.CharField(null=False, max_length=14)
    last_name = models.CharField(null=False, max_length=16)  # 對應varchar 類型
    gender = models.SmallIntegerField(null=False)            # 對應int類型
    hire_data = models.DateField(null=False)

Fieldl類型

常用類型 說明
AutoField 自增整數類型
BigAutoField 更大自增整數類型
BigIntegerField 大整數
BooleanField 布爾類型True或者False
CharField 字符串類型
DateField 日期類型
DateTimeField 日期時間
DecimalField 使用python的Decimal實例表示10進制浮點數,需要極高精度的數值使用該字段
EmailField 能做Email檢驗,基於CharField,最大254
FileField 文件字段
FilePathField 文件路徑
FloatField 浮點數
ImageField 繼承了FileField,但是對上傳的文件進行檢驗,確保為一個圖片
IntegerField int類型
GenericIPAddressField Ipv4/Ipv6校驗
NullBooleanField 布爾值可以為空
PositiveIntegerField 正整數
TextField 大文本,超過4000字符
TimeField 時間類型
URLField 能做URL檢驗,最大200

關系映射

ForeignKey一對多關系

關系類型字段可以將兩張表進行關聯,該字段在一對多方字段建立。例如官方的實例,一個Manufacture可以生產多個車型的汽車,所以在車輛表中建立一個外鍵(多端),標識該汽車類屬於哪一個Manufacture

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

創建外鍵至少需要指定兩個參數,指定關聯的類和on_delete,主表字段和從表字段之間聯系,上面指定為級聯,即主表中的某個數據刪除,從表中關聯了該主表數據的數據將會被刪除。

上面Car中的manufacturer字段與Manufacture類進行了關聯,在生成的manufacture表中,將會多出一個Car_set的字段,可以通過一條Manufacture信息查詢出多條Car信息。而Car中manufacturer字段將被設置為manufacture_id,該數據將會關聯到Manufacture中對應的數據。

ManyToManyField多對多關系

官網的示例:一種Topping可能存在於多個Pizza中,並且每個Pizza含有多種Topping,這就是一個多對多的關系

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

這個屬性可以在關聯的任何一個類中創建,不能同時在連個類中添加該字段。一般來講,應該把 ManyToManyField 實例放到需要在表單中被編輯的對象中

在定義的字段類型中,指定某些參數選項,可以將這個字段做一些特殊的設置或者做一些特殊的驗證。主要包括以下的字段選項。

字段參數選項

選項 說明
null 是否可以null
blank 是否可以為空
choices 類似枚舉
db_column 指定數據庫列名
db_index 是否在該字段建索引
db_tablespace  
default 默認值
editable  
error_messages 指定后覆蓋默認的錯誤信息
help_text 指定幫助信息
primary_key 定義為主鍵
unique 定義為唯一鍵
unique_for_date  
verbose_name 字段備注名
validators models.IntegerField(validators=[validate_even]),添加驗證函數

Manager管理器對象

管理器對象用於實現表的增刪改查。管理器對象是類型為django.db.models.manager.Manager類型,管理器對象可以在定義模型類中創建,如果沒有創建,默認會創建一個objects管理器,手動創建后將不會自動創建。

管理器Django查詢數據庫的接口,每一個模型都至少有一個管理器,用戶自定義管理器模型需要繼承於django.db.models.manager.Manager類。

內部測試 manager管理器

根下創建一個Python 文件,寫入以下內容即可使用

# 從wsgi復制

import os
from django.core.wsgi import get_wsgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ORM.settings')
application = get_wsgi_application()

# 將模型類導入,也就是定義模型類的model文件,
from salary.models import Employees

# object 是Employees自帶的一個管理器,這樣我們可以使用這個管理進行查詢
mgr = Employees.objects

print(mgr.all())

配置日志輸出

import os

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console'],
            'level': os.getenv('DJANGO_LOG_LEVEL', 'INFO'),
        },
    },
}

查詢

使用管理器對象查詢,將會返回一個查詢集,Django內部將這些結果封裝為一個Django.db.models.manager.query.Query類型。這個結果是惰性(只有需要時才會真的查詢),可以進行迭代。查詢的結果不會緩存,再次使用必須再次從數據庫查詢。

mgr = Employees.objects

result_sset = mgr.all()  # 查詢所有結果,將整個表查詢返回結果集
print(mgr.all()) # 獲取數據時,才會執行sql查詢

並且由於我們的日志配置,我們可以在日志中看到執行sql語句信息,這樣方便我們對查詢進行調優。

查詢方法

方法 說明
all() 查詢所有結果的查詢集
filter() 過濾查詢,類似於where
exclude() 過濾的結果取反
order_by() 排序結果 類似於order by
values() 返回這個查詢集,每一個元素是{字段:值}的字典

 

方法 說明
get(pk=1) 返回單個值,沒有數據和多條數據都將報錯
count() 返回查詢的總條數
first() 返回第一個對象
last() 返回最后一個對象
exist() 如果能查到返回True

條件表達式

filter,exclude,get等方法中需要控制查詢條件。django使用了一套條件表達式作為查詢方法中的參數,實現查詢條件篩選。

表達式參數 說明

=,

exact

iexact

等於,mgr.filter(id=1)

 

不等於 mgr.filter(id__iexact=3)

gt, gte 大於,大於等於, mge.filter(id__gt=10001)
lt, lte 小於,小於等於 mgr.exclude(id__lte=10010)

startwith

endswith

首尾匹配,大小寫敏感

isnull

isnotnull

是否為null
in 在某個范圍,mgr.filter(pk__in=(1,2,3))

contains

icontains

包含指定的值,%值%,效率極低

iexact

icontains

istartwith

iendswith

i,表示忽略大小寫

year

month

判斷一個時間,需要時時間類型的字段,mgr.filter(birth_date__year=2000) 年份為2000的

Q 對象

在一個函數中連續寫條件表示這些條件是and(與)關系,並不能表示(or)或(!)非的關系,此時需要使用Q對象,Q對象可以使用 |或, &與, ~非;來描述條件之間的關系。

Q 對象的類型:django.db.models.Q

from django.db.models import Q

# 使用 Q 對象處理這個條件,並使用 | 連接,表示或關系,& 表示與
mgr.filter( Q(id__lt=10005) | Q(lastname__startswith="K") & Q(age=12))  # 與或
mgr.filter( ~Q(id__lt=10005))                  # ~ 表示取反

分組,聚合

聚合函數

from django.db.models import Q, Avg, Sum, Max, Min, Count

aggregate

將所有列的數據分為一組進行聚合

UseInfo.objects.all().aggregate(Sum("age"))   # 所有年齡之和,自動使用主鍵分組

返回一個字典{"age__sum": 120}, 如果想控制age_sum這個key的名字,使用aggregate(agesum=Sum("age"))key將會替換成agesum。

annotate()

annotate可以和value組合實現分組聚合

from django.db.models import Q, Avg, Sum, Max, Min, Count
# emp_no 員工編號
# salary 工資

# 使用emp_no分組,計算每個員工的工資總和
s2 = sal.all().values("emp_no").annotate(Sum("salary"))   # 返回查詢后的結果集

# 只會顯示兩個字段
# <QuerySet [{'emp_no': 1, 'salary__sum': 1281612}, 
             {'emp_no': 2, 'salary__sum': 413127}]}>
# 顯示所有字段使用values,或者在values中指定想要的字段,沒有使用聚合函數的行使用第一行數據為准
s2.values()
# 還可以對其繼續排序。等操作
s2.values().order_by("salary_sum")

Raw

直接使用原生的sql語句

empmgr = Employee.objects
raw_query = "select * from Employee where id > %s"

emp = empmgr.raw(raw_query, params=None)

extra

表示添加額外的部分,select參數表示再select處添加字段以及值,所以使用字段key-value,where表示添加額外的條件,使用列表,and連接內部元素。tables參數表示關聯的表。

Userinfo.objects.all().values("id", name)
            .extra(
                select={
                    "total":"select count("id") from table ",
                    "height":"select count("height") from table where gengder=%s",
                    },
                select_params=[1,男] 
                where=["user_type.id=userinfo.type_id"],
                params=[0, 18]
                tables="app01.user_type"
            )
    等價的sql
        "select 
        id, name, 
        select count("id") from table as "total", 
        select count("height") from table as height
        
        form (userinfo, app01.user_type) 
        where user_type.id=userinfo.type_id

select_related

會根據指定的字段做連表查詢

Userinfo.objects.all().select_related("group")    # 查詢group 表的內容

select * from `userinfo` inner join `group` on userinfo.group_id = group.id

prefetch_related

效果同select_releted,但是執行多次單表查詢,用連表字段的條件進行查詢。

Userinfo.objects.all().perfetch_releted("group")    

select 其他字段, group_id from userinfo              # 查處所有group_id
select * from group where group.id in group_ids     # 更具id查出所有組

ManytoMany

class Boy:
    id = AutoField()
    name = models.Charfield()
    friend = models.ManyToMany("Girl")     # 會自動建立第三章表
    # f = models.ManyToMany("Girl", through="Friend", through_field = ["b", "g"])
    # 使用我們自己建立的第三章表來關聯,指定表和字段,第三章表的b,g一定是分別做了外鍵的才可以
    
class Girl:
    id = AutoField()
    name = models.Charfield()
    
    # boy_set  反向找boy對象的屬性
    
    
obj = models.objects.get(name="小明")
# 操作這個第三張表,進行查詢操作
obj.friend.add(10)      # 對應的一個girl的id值
obj.friend.remove(10)   #  
obj.friend.set([10, 20])        # 設置
obj.friend.clear()
obj.friend.all()        # 獲得所有關聯的girl對象,是一個查詢集。可以繼續filter查詢

foreigenKey實現多對多

使用第三張表,第三張表兩個字段,分別於其他兩個表做外鍵

class Boy:
    id = AutoField()
    name = models.Charfield()
    
class Girl:
    id = AutoField()
    name = models.Charfield()

class Friend:
    b = models.ForeigenKey("Boy")
    g = models.ForeigenKey("Boy")

如果需要查詢時小明的所有女性朋友名字,可以有三種方式,

# 小明的所有女性朋友名字
# 第一種
boy = Boy.objects.filter(name="小明").first()   # 小明對象
for f in boy.Friend_set:    # Friend中和小明有關的所有Fiend對象
    g_name = f.g.name        # 每個f對象關聯了一個girl對象獲得
# for 循環中出現了連表,出現了N+1多次查表得問題

# 第二種: 使用join連表,從中間表起手,使用values連表或者select_related都可以
friends = Friend.objects.filter(b__name="小明").values("g__name")  # 使用了join連表,得到了
for friend in friends:
    g_name = friend.g_name

# 第三種:perfetch_related多次單表查詢
friends = Friend.objects.filter(b__name="小明").perfetch_related("g__name")  # 使用了join連表,得到了
for friend in friends:
    g_name = friend.g_name

建立聯合唯一索引

class friend:
    g = field()
    b = field()
    class Meta:
        unique_together=["g", "b"]

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM