Django-ORM-單表操作

先生發表於2021-06-13

一、ORM介紹

ORM是(物件-關係-對映)的簡稱:它實現了資料模型與資料庫的解耦,對於資料庫的操作,就不用去寫原生的 SQL 語句,取而代之的是基於物件導向的思想去編寫類、物件、呼叫相應的方法等,ORM 會將其轉換成對應的原生 SQL 語句交給 pymysql 執行。

直接編寫原生的 SQL 語句會存在兩個方面的問題:

  1. SQL 語句的執行效率:應用開發程式設計師需要耗費一大部分精力去優化 SQL 語句。
  2. 資料庫遷移:針對 mysql 開發的 SQL 語句無法直接應用到 oracle 資料庫上,一旦遷移資料庫,需要考慮誇平臺的問題。

原生 SQL 和 python 的 ORM 程式碼對比。

原生 SQL 操作:

# 建立表
create table student(
    id int primary key auto_increment,
    name varchar(20) not null,
    age int default 18 not null,
    birthday date not null
);

# 新增記錄
insert into student(name,age,birthday)
	values ('小楊',18,'1998-11-1');
	
# 刪除記錄
delete from student where id=1;

# 更新記錄
update student set birthday='1999-12-22' where id=1;

# 查詢記錄
select * from student;

ORM 的 python 類,和物件

# 類建立表
class Student(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    age = models.IntegerField(default=18)
    birthday = models.DateField()
    
# 新增記錄
obj = Student(name='小楊', birthday='1928-2-6')
obj.save()

# 刪除記錄
Student.objects.filter(id=1).delete()
    
# 更新記錄
Student.objects.filter(id=2).update(birthday='2000-3-22')

# 查詢記錄
obj = models.Student.objects.filter(id=2)

二、單表操作

在python中的ORM的對應關係有三種:

類:————> 表

類物件:——> 一行資料

類屬性:——> 欄位

表建立

在建立表之前需要做好如下配置

1、必須事先建立好資料庫

2、在 settings.py 檔案中的 DATABASES 配置項中增加或者修改成如下配置

DATABASES = {
    # 這是預設的資料庫
    'default': {
        'ENGINE': 'django.db.backends.mysql',	# 使用mysql資料庫
        'HOST': '127.0.0.1',			# mysql伺服器主機IP
        'USER': 'root',					# 連結資料庫的使用者名稱
        'PASSWORD': '123',				# 連結資料庫的密碼
        'PORT': 3306,					# mysql監聽埠
        'NAME': 'orm',					# 要連結的資料庫(必須先建立)
        'ATOMIC_REQUEST': True,			# True 代表同一個http請求所對應的所有SQL都放在一個事物中
        								# 執行(要麼所有都成功,要麼所有都失敗),這是全域性配置
    }
    # 也可以為每個app都配置自己的資料庫,並且資料庫還可以是別的資料庫
    'app01': {
        'ENGINE': 'django.db.backends.sqlite',	# 使用sqlite資料庫
        'HOST': '127.0.0.1',			
        'USER': 'root',					
        'PASSWORD': '123',				
        'PORT': 3306,					
        'NAME': 'orm',					
        'ATOMIC_REQUEST': True,	
	}
}

3、在Django專案下的__init__.py檔案中寫入這樣一句話,因為Django的ORM底層運算元據庫的python模組預設是 mysqldb 而非 pymysql ,而python三支援的是 pymysql 模組,所以要修改預設運算元據庫的模組:

# __init__.py 檔案
import pymysql
pymysql.install_as_MySQLdb()

4、在 Django中 app專案裡的 models.py 檔案中建立模型

from django.db import models

# 建立表
class Student(models.Model):					# 表名——> app名_Student
    id = models.AutoField(primary_key=True)		# 欄位id
    name = models.CharField(max_length=20)		# 欄位name
    age = models.IntegerField(default=18)		# 欄位age
    birthday = models.DateField()				# 欄位birthday

5、最後在命令列中執行這兩條資料庫遷移命名,就可以在指定的資料庫 orm 中建立表:

$ python manage.py makemigrations
$ python manage.py migrate

注意:
1、makemigrations 只是生成一個資料庫遷移記錄的檔案,而 migrate 才是將更改真正提交到資料庫執行
2、資料庫遷移記錄的檔案存放於app01下的 migrations 資料夾裡
3、瞭解:使用命令 python manage.py showmigrations 可以檢視沒有執行 migrate 的檔案

新增記錄

檢視函式中對錶進行操作

# views.py 檔案
from django.shortcuts import HttpResponse
from app01 import models    # 匯入需要操作的表


def index(request):
    """
    :param request: http請求資訊
    :return: 響應資訊/頁面
    對錶的操作:如下:插入一條記錄
    """
    obj = models.Student(name='波波', birthday='2010-5-21')
    obj.save()
    return HttpResponse('Hello World...')

建立記錄方式一:

obj = models.Student(
    name='波波', 
    birthday='2010-5-21'
)
obj.save()	# 就是pymysql的commit提交

建立記錄方式二:create方法(用的多)

date = datetime.datetime.now()	# 當前時間
# birthday欄位可以給時間型別資料
obj = models.Student.objects.create(name='艾倫', birthday=date)
# 可以基於這個物件來取屬性的值
print(obj.name)

#------------------------------------------
dic1 = {'name': '大魚', 'age': 10, 'birthday': '1998-9-24'}
obj = models.Student.objects.create(**dic1)

"""
表單資料如下:
<QueryDict: {'csrfmiddlewaretoken': ['20..sUe'], 'user': ['123']}>

可以把表單提交的資料轉成字典 request.POST.dict()
{'csrfmiddlewaretoken': '20...sUe', 'user': '123'}

在作用於create資料操作上

"""

建立記錄方式三:批量建立

objs_list = []
for i in range(10):
    obj = models.Student(
        name='xiaoyang',
        age=20+i,
        birthday='2010-3-9'
    )
    objs_list.append(obj)	
models.Student.objects.bulk_create(objs_list)	# 批量插入,速度快

建立記錄方式四:update_or_create 有就更新,沒有就建立

models.Student.objects.update_or_create(
    id=10, 			# 篩選條件
    defaults={		# 新增或更新的資料
        'name': '超人',
        'age': 40,
        'birthday': '2001-9-3',
    }
)

刪除記錄

刪除 delete :queryset 和 model 物件都可以呼叫

# 刪除id=14的記錄
models.Student.objects.filter(id=14).delete()	# ---> queryset 物件
# 刪除所有記錄
models.Student.objects.filter().delete()		# ---> queryset 物件

models.Student.objects.get(id=14).delete()		# ---> model 物件

修改記錄

更新update方法model物件不能呼叫更新方法:

只能queryset物件呼叫:

# 修改id=13的name欄位和age欄位
models.Student.objects.filter(id=13).update(name='牛牛', age=20)

三、查詢API

  • all( )

查詢所有結果,結果是queryset型別

obj = models.Student.objects.all()
print(obj)
# 輸出	queryset集合,類似於列表
"""
在類中加入__str__方法:方便如下檢視
def __str__(self):
	return self.name
"""
<QuerySet [<Student: 小紅>, <Student: 波波>, <Student: 艾倫>, <Student: 牛牛>]>
  • filter(**kwargs)

返回的也是queryset集合,查詢不到內容不會報錯,返回一個<Queryset []>空的queryset,裡面可以加入多個條件,用逗號分開,是and關係。

obj = models.Student.objects.filter(id=4)   # 找到id=4的那條記錄
print(obj)	# ——> <QuerySet [<Student: 艾倫>]>
  • get(**kwargs)

返回的是行記錄(model)物件,而且get方法有且必須只有一個結果

obj = models.Student.objects.get(id=4)      # 找到id=4的那條記錄
print(obj)	# ——> 艾倫
  • exclude(**kwargs)

排除的意思,篩選調價不匹配的物件,沒有不等於的操作,返回一個queryset物件

# 返回id不等於1的所有物件
models.Student.objects.filter().exclude(id=1)
  • order_by(*field)

queryset型別的資料來調傭,對查詢結果排序,預設是按照id來升序排列的,返回值還是queryset物件型別

models.Student.objects.filter().order_by('price', id)

# 直接寫price預設是按照price升序排列
# 按照欄位降序排列,就寫個負號就行了order_by('-price')
# order_by('price','id')是多條件排序
# 按照price進行升序,price相同的資料,按照id進行升序
  • reverse()

queryset型別的資料來呼叫,對查詢結果反向排序,返回值是queryset型別

# 排序之後反轉
models.Student.objects.all().arder_by('id').revers()
  • count()

queryset型別的資料來呼叫,返回資料庫中匹配查詢(QuerySet)的物件數量。

models.Student.objects.filter().count()
  • first()

queryset型別的資料來呼叫,返回第一條記錄,結果是model物件

models.Student.objects.filter().first()
  • last()

queryset型別的資料來呼叫,返回最後一條記錄,結果是model物件

models.Student.objects.filter().last()
  • exists()

queryset型別的資料來呼叫,如果QuerySet包含資料,就返回True,否則返回False

models.Student.objects.all().exists()
  • values(*field)

queryset型別的資料來呼叫返回一個ValuesQuerySet--一個特殊的QuerySet,執行後得到的並不是一系列model的例項化物件,而是一個可迭代的字典列,只要是返回的queryset型別,就可以繼續鏈式呼叫queryset型別的其他查詢方法,其他方法也是一樣的。

obj1 = models.Student.objects.filter().values()
obj2 = models.Student.objects.values('name')
print(obj1)		
# <QuerySet [{'id': 2, 'name': '小紅', 'age': 18, 'birthday': ...},{...},...]>

print(obj2)
<QuerySet [{'name': '小紅'}, {'name': '波波'}, {'name': '艾倫'}]>
  • values_list(*field)

它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列。

obj1 = models.Student.objects.filter().values_list()
obj2 = models.Student.objects.values_list('name')
print(obj1)
# <QuerySet [(2, '小紅', 18, datetime.date(2000, 3, 22)), (3, '波波', 18, datetime.date(2010, 5, 21)), (4, '艾倫', 18, datetime.date(2021, 6, 10))]>

print(obj2)
# <QuerySet [('小紅',), ('波波',), ('艾倫',)]>
  • distinct()

從values或values_list的返回結果中剔除重複的記錄物件,返回值為QuerySet物件

models.Student.objects.values_list('name').distinct()

四、基於雙下滑線的模糊查詢

Book.objects.filter(price__in=[100,200,300]) 	# price值等於這三個裡面的任意一個的物件
Book.objects.filter(price__gt=100)  			# 大於,大於等於是price__gte=100,別寫price>100,這種引數不支援
Book.objects.filter(price__lt=100)
Book.objects.filter(price__range=[100,200])  	# sql的between and,大於等於100,小於等於200
Book.objects.filter(title__contains="python")  	# title值中包含python的
Book.objects.filter(title__icontains="python") 	# 不區分大小寫
Book.objects.filter(title__startswith="py") 	# 以什麼開頭,istartswith  不區分大小寫
Book.objects.filter(pub_date__year=2012)

日期查詢示例:

all_books = models.Book.objects.filter(pub_date__year=2012) 	# 找2012年的所有書籍
all_books = models.Book.objects.filter(pub_date__year__gt=2012)	# 找大於2012年的所有書籍

all_books = models.Book.objects.filter(pub_date__year=2019,pub_date__month=2)
# 找2019年月份的所有書籍,如果明明有結果,你卻查不出結果,是因為mysql資料庫的時區和我們們django的時區不同導致的,瞭解一下就行了,你需要做的就是將django中的settings配置檔案裡面的USE_TZ = True改為False,就可以查到結果了,以後這個值就改為False,而且就是因為我們們用的mysql資料庫才會有這個問題,其他資料庫沒有這個問題。

相關文章