42
mysql的安裝
navicat安裝
mysql驅動程序安裝 mysqlclient 、pymysql
43
44
45
46 ORM模型
ORM模型介紹:
隨著項目越來越大,采用寫原生SQL的方式在代碼中會出現(xiàn)大量的SQL語句,那么問題就出現(xiàn)了:
1、SQL語句重復利用率不高,越復雜的SQL語句條件越多,代碼越長。會出現(xiàn)很多相近的SQL語句;
2、很多SQL語句是在業(yè)務邏輯中拼出來的,如果有數(shù)據(jù)庫需要更改,就要去修改這些邏輯,這會很容易漏掉對某些SQL語句的修改;
3、寫SQL時容易忽略web安全問題,給給未來造成隱患。SQL注入;
如何解決上面的問題:
ORM ,全稱 Object Relational Mapping ,中文叫做對象關系映射,通過 ORM 我們可以通過類的方式去操作數(shù)據(jù)庫,而不用再寫原生的SQL語句。通過把表映射成類,把行作實例,把字段作為屬性, ORM 在執(zhí)行對象操作的時候最終還是會把對應的操作轉換為數(shù)據(jù)庫原生語句。使用 ORM 有許多優(yōu)點:
1、易用性:使用 ORM 做數(shù)據(jù)庫的開發(fā)可以有效的減少重復SQL語句的概率,寫出來的模型也更加直觀、清晰;
2、性能損耗?。?ORM 轉換成底層數(shù)據(jù)庫操作指令確實會有一些開銷。但從實際的情況來看,這種性能損耗很少(不足5%),只要不是對性能有嚴苛的要求,綜合考慮開發(fā)效率、代碼的閱讀性,帶來的好處要遠遠大于性能損耗,而且項目越大作用越明顯;
3、設計靈活:可以輕松的寫出復雜的查詢;
4、可移植性: Django 封裝了底層的數(shù)據(jù)庫實現(xiàn),支持多個關系數(shù)據(jù)庫引擎,包括流行的 MySQL 、 PostgreSQL 和 SQLite ??梢苑浅]p松的切換數(shù)據(jù)庫;
47創(chuàng)建和映射ORM模型
ORM 模型一般都是放在app的models.py文件中。每個app都可以擁有自己的模型,并且如果這個模型想要映射到數(shù)據(jù)庫中,那么這個 app 必須要放在項目的settings.py文件的INSTALLED_APP 中進行安裝。
示例代碼如下:
from django.db import models
如果要將一個普通的類變成一個可以映射到數(shù)據(jù)庫中的ORM模型
那么必須要將父類設置為models.Model或者他的子類
class Book(models.Model):
# 1. id:int類型,是自增長的。
id = models.AutoField(primary_key=True)
# 2. name:varchar(100),圖書的名字
name = models.CharField(max_length=100,null=False)
# 3. author:varchar(100),圖書的作者
author = models.CharField(max_length=100,null=False)
# 4. price:float,圖書的價格
price = models.FloatField(null=False,default=0)
class Publisher(models.Model):
name = models.CharField(max_length=100,null=False)
address = models.CharField(max_length=100,null=False)
使用makemigrations生成遷移腳本文件
python manage.py makemigrations使用migrate將新生成的遷移腳本文件映射到數(shù)據(jù)庫中
python manage.py migrate
將 ORM 模型映射到數(shù)據(jù)庫中,總結起來就是以下幾步:
總結
1、在 settings.py 中,配置好 DATABASES ,做好數(shù)據(jù)庫相關的配置;
2、在 app 中的 models.py 中定義好模型,這個模型必須繼承自 django.db.models ;
3、將這個 app 添加到 settings.py 的 INSTALLED_APP 中;
4、在命令行終端,進入到項目所在的路徑,然后執(zhí)行命令:python manage.py makemigrations 來生成遷移腳本文件。
5、同樣在命令行中,執(zhí)行命令 python manage.py migrate 來將遷移腳本文件映射到數(shù)據(jù)庫中;
48 ORM對數(shù)據(jù)庫的基本操作:
添加數(shù)據(jù):
只要使用ORM模型創(chuàng)建一個對象。然后再調用這個ORM模型的save方法就可以保存了。
示例代碼如下:
book = Book(name='西游記',author='吳承恩',price=100)
book.save()
查找數(shù)據(jù):
所有的查找工作都是使用模型上的objects屬性來完成的。當然也可以自定義查詢對象。這部分功能會在后面講到。
- 根據(jù)主鍵進行查找:使用主鍵進行查找??梢允褂?code>objects.get方法。然后傳遞
pk=xx的方式進行查找。示例代碼如下:book = Book.objects.get(pk=2) - 根據(jù)其他字段進行查找:可以使用
objects.filter方法進行查找。示例代碼如下:
使用books = Book.objects.filter(name='三國演義')filter方法返回來的是一個QuerySet對象。這個對象類似于列表。我們可以使用這個對象的first方法來獲取第一個值。
刪除數(shù)據(jù):
首先查找到對應的數(shù)據(jù)模型。然后再執(zhí)行這個模型的delete方法即可刪除。示例代碼如下:
book = Book.objects.get(pk=1)
book.delete()
修改數(shù)據(jù):
首先查找到對應的數(shù)據(jù)模型。然后修改這個模型上的屬性的值。再執(zhí)行save方法即可修改完成。示例代碼如下:
book = Book.objects.get(pk=2)
book.price = 200
book.save()
49 常用Field
AutoField 自增長
BigAutoField 范圍更大的
BooleanField true/false
CharField 小于254的字符使用這個
50 關于時間的我Field
navie時間和aware時間:
什么是navie時間?什么是aware時間?
- navie時間:不知道自己的時間表示的是哪個時區(qū)的。也就是不知道自己幾斤幾兩。比較幼稚。
- aware時間:知道自己的時間表示的是哪個時區(qū)的。也就是比較清醒。
pytz庫:
專門用來處理時區(qū)的庫。這個庫會經(jīng)常更新一些時區(qū)的數(shù)據(jù),不需要我們擔心。并且這個庫在安裝Django的時候會默認的安裝。如果沒有安裝,那么可以通過pip install pytz的方式進行安裝。
astimezone方法:
將一個時區(qū)的時間轉換為另外一個時區(qū)的時間。這個方法只能被aware類型的時間調用。不能被navie類型的時間調用。
示例代碼如下:
import pytz
from datetime import datetime
now = datetime.now() # 這是一個navie類型的時間
utc_timezone = pytz.timezone("UTC") # 定義UTC的時區(qū)對象
utc_now = now.astimezone(utc_timezone) # 將當前的時間轉換為UTC時區(qū)的時間
>> ValueError: astimezone() cannot be applied to a naive datetime # 會拋出一個異常,原因就是因為navie類型的時間不能調用astimezone方法
now = now.replace(tzinfo=pytz.timezone('Asia/Shanghai'))
utc_now = now.astimezone(utc_timezone)
# 這時候就可以正確的轉換。
replace方法:
可以將一個時間的某些屬性進行更改。
51#
django.utils.timezone.now方法:
會根據(jù)settings.py中是否設置了USE_TZ=True獲取當前的時間。如果設置了,那么就獲取一個aware類型的UTC時間。如果沒有設置,那么就會獲取一個navie類型的時間。
django.utils.timezone.localtime方法:
會根據(jù)setting.py中的TIME_ZONE來將一個aware類型的時間轉換為TIME_ZONE指定時區(qū)的時間。
DateField:
日期類型。在Python中是datetime.date類型,可以記錄年月日。在映射到數(shù)據(jù)庫中也是date類型。使用這個Field可以傳遞以下幾個參數(shù):
-
auto_now:在每次這個數(shù)據(jù)保存的時候,都使用當前的時間。比如作為一個記錄修改日期的字段,可以將這個屬性設置為True。 -
auto_now_add:在每次數(shù)據(jù)第一次被添加進去的時候,都使用當前的時間。比如作為一個記錄第一次入庫的字段,可以將這個屬性設置為True。
DateTimeField:
日期時間類型,類似于DateField。不僅僅可以存儲日期,還可以存儲時間。映射到數(shù)據(jù)庫中是datetime類型。這個Field也可以使用auto_now和auto_now_add兩個屬性。
TimeField:
時間類型。在數(shù)據(jù)庫中是time類型。在Python中是datetime.time類型。
navie和aware介紹以及在django中的用法:
https://docs.djangoproject.com/en/2.0/topics/i18n/timezones/
52
EmailField:
類似于CharField。在數(shù)據(jù)庫底層也是一個varchar類型。最大長度是254個字符。
FileField:
用來存儲文件的。這個請參考后面的文件上傳章節(jié)部分。
ImageField:
用來存儲圖片文件的。這個請參考后面的圖片上傳章節(jié)部分。
FloatField:
浮點類型。映射到數(shù)據(jù)庫中是float類型。
IntegerField:
整形。值的區(qū)間是-2147483648——2147483647。
BigIntegerField:
大整形。值的區(qū)間是-9223372036854775808——9223372036854775807。
PositiveIntegerField:
正整形。值的區(qū)間是0——2147483647。
SmallIntegerField:
小整形。值的區(qū)間是-32768——32767。
PositiveSmallIntegerField:
正小整形。值的區(qū)間是0——32767。
TextField:
大量的文本類型。映射到數(shù)據(jù)庫中是longtext類型。
UUIDField:
只能存儲uuid格式的字符串。uuid是一個32位的全球唯一的字符串,一般用來作為主鍵。
URLField:
類似于CharField,只不過只能用來存儲url格式的字符串。并且默認的max_length是200。
53
Field常用的參數(shù)
null:
如果設置為True,Django將會在映射表的時候指定是否為空。默認是為False。在使用字符串相關的Field(CharField/TextField)的時候,官方推薦盡量不要使用這個參數(shù),也就是保持默認值False。因為Django在處理字符串相關的Field的時候,即使這個Field的null=False,如果你沒有給這個Field傳遞任何值,那么Django也會使用一個空的字符串""來作為默認值存儲進去。因此如果再使用null=True,Django會產(chǎn)生兩種空值的情形(NULL或者空字符串)。如果想要在表單驗證的時候允許這個字符串為空,那么建議使用blank=True。如果你的Field是BooleanField,那么對應的可空的字段則為NullBooleanField。
blank:
標識這個字段在表單驗證的時候是否可以為空。默認是False。
這個和null是有區(qū)別的,null是一個純數(shù)據(jù)庫級別的。而blank是表單驗證級別的。
db_column:
這個字段在數(shù)據(jù)庫中的名字。如果沒有設置這個參數(shù),那么將會使用模型中屬性的名字。
default:
默認值。可以為一個值,或者是一個函數(shù),但是不支持lambda表達式。并且不支持列表/字典/集合等可變的數(shù)據(jù)結構。
primary_key:
是否為主鍵。默認是False。
unique:
在表中這個字段的值是否唯一。一般是設置手機號碼/郵箱等。
更多Field參數(shù)請參考官方文檔:https://docs.djangoproject.com/zh-hans/2.0/ref/models/fields/
54
模型中Meta配置:
對于一些模型級別的配置。我們可以在模型中定義一個類,叫做Meta。然后在這個類中添加一些類屬性來控制模型的作用。比如我們想要在數(shù)據(jù)庫映射的時候使用自己指定的表名,而不是使用模型的名稱。那么我們可以在Meta類中添加一個db_table的屬性。示例代碼如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
class Meta:
db_table = 'book_model'
以下將對Meta類中的一些常用配置進行解釋。
db_table:
這個模型映射到數(shù)據(jù)庫中的表名。如果沒有指定這個參數(shù),那么在映射的時候將會使用模型名來作為默認的表名。
ordering:
設置在提取數(shù)據(jù)的排序方式。后面章節(jié)會講到如何查找數(shù)據(jù)。比如我想在查找數(shù)據(jù)的時候根據(jù)添加的時間排序,那么示例代碼如下:
class Book(models.Model):
name = models.CharField(max_length=20,null=False)
desc = models.CharField(max_length=100,name='description',db_column="description1")
pub_date = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'book_model'
ordering = ['pub_date']
更多的配置后面會慢慢介紹到。
官方文檔:https://docs.djangoproject.com/en/2.0/ref/models/options/
55 #56 ORM外鍵
了解外鍵
在MySQL中,表有兩種引擎,一種是InnoDB,另外一種是myisam。如果使用的是InnoDB引擎,是支持外鍵約束的。外鍵的存在使得ORM框架在處理表關系的時候異常的強大。MySQL數(shù)據(jù)庫默認使用的也是InnoDB引擎。
使用外鍵
新建一個項目,創(chuàng)建一個article的app,添加至settings中,并且在settings中設置數(shù)據(jù)庫的連接,調至整個項目能運行為止。
類定義為class ForeignKey(to,on_delete,**options)。第一個參數(shù)是引用的是哪個模型,第二個參數(shù)是在使用外鍵引用的模型數(shù)據(jù)被刪除了,這個字段該如何處理,比如有CASCADE、SET_NULL等(外鍵刪除各個參數(shù)的意思)。這里以一個實際案例來說明。比如有一個Category和一個Article兩個模型。一個種類下可以包含多篇文章,一個Article只能有一個種類,并且通過外鍵進行引用。那么相關的示例代碼如下:
- 在同一個app中使用外鍵
在article中的models中寫入代碼:
from django.db import models
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 是由Category影響Article
category = models.ForeignKey('Category',on_delete=models.CASCADE)
這里我們就在Article中設置了一個外鍵category。
以上使用ForeignKey來定義模型之間的關系。即在article的實例中可以通過category屬性來操作對應的Category模型。這樣使用起來非常的方便。示例代碼如下:views中寫入
from django.shortcuts import render
from . import models
from django.http import HttpResponse
# Create your views here.
def index(request):
# 插入數(shù)據(jù)
# article = models.Article(title='abc',content='111')
# category = models.Category(name='最新文章')
# category.save()
# article.category = category
# article.save()
#讀取數(shù)據(jù)
article = models.Article.objects.first()
print(article.category.name)
return HttpResponse('successful')
在上面代碼中把相應的注釋去了,就能夠進行測試了。
為什么使用了ForeignKey后,就能通過category訪問到對應的Catrgory對象呢。因此在底層,Django為Article表添加了一個屬性名_id的字段(比如category的字段名稱是category_id),這個字段是一個外鍵,記錄著對應的種類的主鍵。以后通過article.category訪問的時候,實際上是先通過category_id找到對應的數(shù)據(jù),然后再提取Category表中的這條數(shù)據(jù),形成一個模型。
如果想要引用另外一個app的模型,那么應該在傳遞to參數(shù)的時候,使用app.model_name進行指定。例如,如果User和Article不是在同一個app中,那么在引用的時候的示例代碼如下:
首先新建一個user的app,并且添加至settings中,在user中的models中寫入代碼,創(chuàng)建一個User模型
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=100)
再在article中的models中添加這個外鍵,即在Article這個模型中新添加一個屬性
# 將user中的User這個模型作為外鍵
author = models.ForeignKey('user.User',on_delete=models.CASCADE,null=True)
如果模型的外鍵引用的是本身自己這個模型,那么to參數(shù)可以為self,或者是這個模型的名字。在論壇開發(fā)中,一般評論都可以進行二級評論,即可以針對另外一個評論進行評論,那么在定義模型的時候就需要使用外鍵來引用自身。示例代碼如下:
class Comment(models.Model):
content = models.TextField()
orihin_comment = models.ForeignKey('self',on_delete=models.CASCADE)
這樣我們就實現(xiàn)了添加了一個外鍵引用自身。
外間刪除操作的參數(shù)意思:
如果一個模型使用了外鍵。那么在對方那個模型被刪掉后,該進行什么樣的操作??梢酝ㄟ^on_delete來指定??梢灾付ǖ念愋腿缦拢?/p>
CASCADE:級聯(lián)操作。如果外鍵對應的那條數(shù)據(jù)被刪除了,那么這條數(shù)據(jù)也會被刪除。
PROTECT:受保護。即只要這條數(shù)據(jù)引用了外鍵的那條數(shù)據(jù),那么就不能刪除外鍵的那條數(shù)據(jù)。如果我們強行刪除,Django就會報錯。
SET_NULL:設置為空。如果外鍵的那條數(shù)據(jù)被刪除了,那么在本條數(shù)據(jù)上就將這個字段設置為空。如果設置這個選項,前提是要指定這個字段可以為空。
SET_DEFAULT:設置默認值。如果外鍵的那條數(shù)據(jù)被刪除了,那么本條數(shù)據(jù)上就將這個字段設置為默認值。如果設置這個選項,== 前提是要指定這個字段一個默認值 ==。
SET():如果外鍵的那條數(shù)據(jù)被刪除了。那么將會獲取SET函數(shù)中的值來作為這個外鍵的值。SET函數(shù)可以接收一個可以調用的對象(比如函數(shù)或者方法),如果是可以調用的對象,那么會將這個對象調用后的結果作為值返回回去。== 可以不用指定默認值 ==
DO_NOTHING:不采取任何行為。一切全看數(shù)據(jù)庫級別的約束。
以上這些選項只是Django級別的,數(shù)據(jù)級別依舊是RESTRICT!
數(shù)據(jù)庫層面的約束有四種:
RESTRICT:默認的選項,如果想要刪除父表的記錄時,而在子表中有關聯(lián)該父表的記錄,則不允許刪除父表中的記錄;
NOACTION:同 RESTRICT效果一樣,也是首先先檢查外鍵;
CASCADE:父表delete、update的時候,子表會delete、update掉關聯(lián)記錄;
SET NULL:父表delete、update的時候,子表會將關聯(lián)記錄的外鍵字段所在列設為null,所以注意在設計子表時外鍵不能設為not null;
參考鏈接:https://blog.csdn.net/yajing8/article/details/73014004
為什么ORM能越過數(shù)據(jù)庫的約束呢?
是因為ORM操作是反過來的,比如我們在ORM模型中設置了on_delete=models.CASCADE,那么在進行刪除的時候,如果發(fā)現(xiàn)在數(shù)據(jù)庫層面有父表約束著它,使他不能被刪除,那么ORM就會先去刪除父表,再來刪除指定的表,從而達到越過了數(shù)據(jù)庫層面的約束。
57 表關系
一對多
- 應用場景:比如文章和作者之間的關系。一個文章只能由一個作者編寫,但是一個作者可以寫多篇文章。文章和作者之間的關系就是典型的多對一的關系。
- 實現(xiàn)方式:一對多或者多對一,都是通過
ForeignKey來實現(xiàn)的。還是以文章和作者的案例進行講解。
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
那么以后在給Article對象指定author,就可以使用以下代碼來完成:
article = Article(title='abc',content='123')
author = User(username='zhiliao',password='111111')
# 要先保存到數(shù)據(jù)庫中
author.save()
article.author = author
article.save()
并且以后如果想要獲取某個用戶下所有的文章,可以通過article_set來實現(xiàn)。示例代碼如下:
user = User.objects.first()
# 獲取第一個用戶寫的所有文章
articles = user.article_set.all()
for article in articles:
print(article)
并且如果想要將文章添加到某個分類中??梢允褂靡幌碌姆绞剑?/p>
category = Category.objects.first()
article = Article(title='bbb',content='vvv')
article.author = FrontUser.objects.first()
category.article_set.add(article,bulk=False)
- 使用
bulk=False,那么Django會自動的保存article,而不需要在添加到category之前先保存article。 - 或者是另外一種解決方式是,在添加到
category.article_set中之前,先將article保存到數(shù)據(jù)庫中。但是如果article.category不能為空,那么就產(chǎn)生一種死循環(huán)了,article沒有category不能保存,而將article添加到cateogry.artile_set中,又需要article之前是已經(jīng)存儲到數(shù)據(jù)庫中的。 - 如果是上面的那種需求,建議使用
bulk=False的解決方案。
58
一對一:
- 在Django中一對一是通過
models.OnetToOneField來實現(xiàn)的。這個OneToOneField其實本質上就是一個外鍵,只不過這個外鍵有一個唯一約束(unique key),來實現(xiàn)一對一。 - 以后如果想要反向引用,那么是通過引用的模型的名字轉換為小寫的形式進行訪問。比如以下模型:
class FrontUser(models.Model): username = models.CharField(max_length=200) class UserExtension(models.Model): school = models.CharField(max_length=100) user = models.OneToOneField("FrontUser",on_delete=models.CASCADE) # 通過userextension來訪問UserExtension對象 user = FrontUser.objects.first() print(user.userextension)UserExtension的對象,可以通過user來訪問到對應的user對象。并且FrontUser對象可以使用userextension來訪問對應的UserExtension對象。
如果不想使用Django默認的引用屬性名字。那么可以在OneToOneField中添加一個related_name參數(shù)。示例代碼如下:
那么以后就class FrontUser(models.Model): username = models.CharField(max_length=200) class UserExtension(models.Model): school = models.CharField(max_length=100) user = models.OneToOneField("FrontUser",on_delete=models.CASCADE,related_name='extension') # 通過extension來訪問到UserExtension對象 user = FrontUser.objects.first() print(user.extension)FrontUser的對象就可以通過extension屬性來訪問到對應的UserExtension對象。
59
多對多:
應用場景:比如文章和標簽的關系。一篇文章可以有多個標簽,一個標簽可以被多個文章所引用。因此標簽和文章的關系是典型的多對多的關系。
實現(xiàn)方式:
Django為這種多對多的實現(xiàn)提供了專門的Field。叫做ManyToManyField。還是拿文章和標簽為例進行講解。示例代碼如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
tags = models.ManyToManyField("Tag",related_name="articles")
class Tag(models.Model):
name = models.CharField(max_length=50)
在數(shù)據(jù)庫層面,實際上Django是為這種多對多的關系建立了一個中間表。這個中間表分別定義了兩個外鍵,引用到article和tag兩張表的主鍵。
60ORM查詢條件詳解-準備工作
61 pycharm連接數(shù)據(jù)庫
62 查詢操作
查找是數(shù)據(jù)庫操作中一個非常重要的技術。查詢一般就是使用filter、exclude以及get三個方法來實現(xiàn)。我們可以在調用這些方法的時候傳遞不同的參數(shù)來實現(xiàn)查詢需求。在ORM層面,這些查詢條件都是使用field+__+condition的方式來使用的。以下將那些常用的查詢條件來一一解釋。
exact:在底層會被翻譯成
=。-
iexact:在底層會被翻譯成
LIKE。- LIKE和=:大部分情況下都是等價的,只有少數(shù)情況下是不等價的。
- exict和iexact:他們的區(qū)別其實就是LIKE和=的區(qū)別,因為exact會被翻譯成=,而iexact會被翻譯成LIKE。
- 因為
field__exact=xxx其實等價于filed=xxx,因此我們直接使用filed=xxx就可以了,并且因為大部分情況exact和iexact又是等價的,因此我們以后直接使用field=xxx就可以了。
63
QuerySet.query:
query可以用來查看這個ORM查詢語句最終被翻譯成的SQL語句。但是query只能被用在QuerySet對象上,不能用在普通的ORM模型上。因此如果你的查詢語句是通過get來獲取數(shù)據(jù)的,那么就不能使用query,因為get返回的是滿足條件的ORM模型,而不是QuerySet。如果你是通過filter等其他返回QuerySet的方法查詢的,那么就可以使用query。contains:使用大小寫敏感的判斷,某個字符串是否在指定的字段中。這個判斷條件會使用大小敏感,因此在被翻譯成
SQL語句的時候,會使用like binary,而like binary就是使用大小寫敏感的。icontains:使用大小寫不敏感的判斷,某個字符串是否被包含在指定的字段中。這個查詢語句在被翻譯成
SQL的時候,使用的是like,而like在MySQL層面就是不區(qū)分大小寫的。contains和icontains:在被翻譯成
SQL的時候使用的是%hello%,就是只要整個字符串中出現(xiàn)了hello都能過夠被找到,而iexact沒有百分號,那么意味著只有完全相等的時候才會被匹配到。
64
-
in:可以直接指定某個字段的是否在某個集合中。示例代碼如下:
articles = Article.objects.filter(id__in=[1,2,3])也可以通過其他的表的字段來判斷是否在某個集合中。示例代碼如下:
categories = Category.objects.filter(article__id__in=[1,2,3])如果要判斷相關聯(lián)的表的字段,那么也是通過
__來連接。并且在做關聯(lián)查詢的時候,不需要寫models_set,直接使用模型的名字的小寫化就可以了。比如通過分類去查找相應的文章,那么通過article__id__in就可以了,而不是寫成article_set__id__in的形式。當然如果你不想使用默認的形式,可以在外鍵定義的時候傳遞related_query_name來指定反向查詢的名字。示例代碼如下:class Category(models.Model): name = models.CharField(max_length=100) class Meta: db_table = 'category' class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() cateogry = models.ForeignKey("Category",on_delete=models.CASCADE,null=True,related_query_name='articles') class Meta: db_table = 'article'因為在
cateogry的ForeignKey中指定了related_query_name為articles,因此你不能再使用article來進行反向查詢了。這時候就需要通過articles__id__in來進行反向查詢。反向查詢是將模型名字小寫化。比如
article__in??梢酝ㄟ^related_query_name來指定自己的方式,而不使用默認的方式。
反向引用是將模型名字小寫化,然后再加上_set,比如article_set,可以通過related_name來指定自己的方式,而不是用默認的方式。并且,如果在做反向查詢的時候,如果查詢的字段就是模型的主鍵,那么可以省略掉這個字段,直接寫成
article__in就可以了,不需要這個id了。in不僅僅可以指定列表/元組,還可以為QuerySet。比如要查詢“文章標題中包含有hello的所有分類”,那么可以通過以下代碼來實現(xiàn):articles = Article.objects.filter(title__icontains='hello') categories = Category.objects.filter(articles__in=articles) for cateogry in categories: print(cateogry)
65
- gt、gte、lt、lte:代表的是大于、大于等于、小于、小于等于的條件。示例代碼如下:
articles = Article.objects.filter(id__lte=3)
66
- startswith、istartswith、endswith、iendswith:表示以某個值開始,不區(qū)分大小寫的以某個值開始、以某個值結束、不區(qū)分大小寫的以某個值結束。示例代碼如下:
articles = Article.objects.filter(title__endswith="hello")
67
- 關于時間的查詢條件:
- range:可以指定一個時間段。并且時間應該標記為
aware時間,不然django會報警告。示例代碼如下:start_time = make_aware(datetime(year=2018,month=4,day=4,hour=17,minute=0,second=0)) end_time = make_aware(datetime(year=2018,month=4,day=4,hour=18,minute=0,second=0)) articles = Article.objects.filter(create_time__range=(start_time,end_time)) print(articles.query) print(articles)
- range:可以指定一個時間段。并且時間應該標記為
68
- date:用年月日來進行過濾。如果想要使用這個過濾條件,那么前提必須要在
MySQL中添加好那些時區(qū)文件。如何添加呢?參考教案。示例代碼如下:articles = Article.objects.filter(create_time__date=datetime(year=2018,month=4,day=4)) - year/month/day:表示根據(jù)年/月/日進行查找。示例代碼如下:
articles = Article.objects.filter(create_time__year__gte=2018) - week_day:根據(jù)星期來進行查找。1表示星期天,7表示星期六,2-6代表的是星期一到星期五。比如要查找星期三的所有文章,那么可以通過以下代碼來實現(xiàn):
articles = Article.objects.filter(create_time__week_day=4) - time:根據(jù)分時秒來進行查找。如果要具體到秒,一般比較難匹配到,可以使用區(qū)間的方式來進行查找。區(qū)間使用
range條件。比如想要獲取17時/10分/27-28秒之間的文章,那么可以通過以下代碼來實現(xiàn):start_time = time(hour=17,minute=10,second=27) end_time = time(hour=17,minute=10,second=28) articles = Article.objects.filter(create_time__time__range=(start_time,end_time))
69
isnull和regex
70聚合函數(shù)準備
71
- 所有的聚合函數(shù)都是放在
django.db.models下面。 - 聚合函數(shù)不能夠單獨的執(zhí)行,需要放在一些可以執(zhí)行聚合函數(shù)的方法下面中去執(zhí)行。比如
aggregate。示例代碼如下:result = Book.objects.aggregate(Avg("price")) - 聚合函數(shù)執(zhí)行完成后,給這個聚合函數(shù)的值取個名字。取名字的規(guī)則,默認是
filed+__+聚合函數(shù)名字形成的。比如以上代碼形成的名字叫做price__avg。如果不想使用默認的名字,那么可以在使用聚合函數(shù)的時候傳遞關鍵字參數(shù)進去,參數(shù)的名字就是聚合函數(shù)執(zhí)行完成的名字。實示例代碼如下:
以上傳遞了關鍵字參數(shù)result = Book.objects.aggregate(avg=Avg("price"))avg=Avg("price"),那么以后Avg聚合函數(shù)執(zhí)行完成的名字就叫做avg。 -
aggregate:這個方法不會返回一個QuerySet對象,而是返回一個字典。這個字典中的key就是聚合函數(shù)的名字,值就是聚合函數(shù)執(zhí)行后的結果。
72
-
aggregate和annotate的相同和不同:- 相同:這兩個方法都可以執(zhí)行聚合函數(shù)。
- 不同:
-
aggregate返回的是一個字典,在這個字典中存儲的是這個聚合函數(shù)執(zhí)行的結果。而annotate返回的是一個QuerySet對象,并且會在查找的模型上添加一個聚合函數(shù)的屬性。 -
aggregate不會做分組,而annotate會使用group by子句進行分組,只有調用了group by子句,才能對每一條數(shù)據(jù)求聚合函數(shù)的值。
-
73
-
Count:用來求某個數(shù)據(jù)的個數(shù)。比如要求所有圖書的數(shù)量,那么可以使用以下代碼:
并且result = Book.objects.aggregate(book_nums=Count("id"))Count可以傳遞distinct=True參數(shù),用來剔除那些重復的值,只保留一個。比如要獲取作者表中,不同郵箱的個數(shù),那么這時候可以使用distinct=True。示例代碼如下:result = Author.objects.aggregate(email_nums=Count('email',distinct=True))
74
-
Max和Min:求指定字段的最大值和最小值。示例代碼如下:result = Author.objects.aggregate(max=Max("age"),min=Min("age"))
75
-
Sum:求某個字段值的總和。示例代碼如下:result = BookOrder.objects.aggregate(total=Sum('price'))aggregate和annotate方法可以在任何的QuerySet對象上調用。因此只要是返回了QuerySet對象,那么就可以進行鏈式調用。比如要獲取2018年度的銷售總額,那么可以先過濾年份,再求聚合函數(shù)。示例代碼如下:BookOrder.objects.filter(create_time__year=2018).aggregate(total=Sum('price'))
76
-
F表達式: 動態(tài)的獲取某個字段上的值。并且這個F表達式,不會真正的去數(shù)據(jù)庫中查詢數(shù)據(jù),他相當于只是起一個標識的作用。比如想要將原來每本圖書的價格都在原來的基礎之上增加10元,那么可以使用以下代碼來實現(xiàn):from django.db.models import F Book.objects.update(price=F("price")+10)
77
-
Q表達式:使用Q表達式包裹查詢條件,可以在條件之間進行多種操作。與/或非等,從而實現(xiàn)一些復雜的查詢操作。例子如下:- 查找價格大于100,并且評分達到4.85以上的圖書:
# 不使用Q表達式的 books = Book.objects.filter(price__gte=100,rating__gte=4.85) # 使用Q表達式的 books = Book.objects.filter(Q(price__gte=100)&Q(rating__gte=4.85)) - 查找價格低于100元,或者評分低于4分的圖書:
books = Book.objects.filter(Q(price__gte=100)&Q(rating__gte=4.85)) - 獲取價格大于100,并且圖書名字中不包含”傳“字的圖書:
books = Book.objects.filter(Q(price__gte=100)&~Q(name__icontains='傳'))
- 查找價格大于100,并且評分達到4.85以上的圖書:
78objects對象所屬類原理剖析
79-93 queryset api 介紹
94-97 ORM作業(yè)講解
98
ORM模型遷移
遷移命令:
1、makemigrations:將模型生成遷移腳本。模型所在的app,必須放在settings.py中的INSTALLED_APPS中。這個命令有以下幾個常用選項:
app_label:后面可以跟一個或者多個app,那么就只會針對這幾個app生成遷移腳本。如果沒有任何的app_label,那么會檢查INSTALLED_APPS中所有的app下的模型,針對每一個app都生成響應的遷移腳本。
--name:給這個遷移腳本指定一個名字。
--empty:生成一個空的遷移腳本。如果你想寫自己的遷移腳本,可以使用這個命令來實現(xiàn)一個空的文件,然后自己再在文件中寫遷移腳本。
99
2、migrate:將新生成的遷移腳本。映射到數(shù)據(jù)庫中。創(chuàng)建新的表或者修改表的結構。以下一些常用的選項:
- app_label:將某個
app下的遷移腳本映射到數(shù)據(jù)庫中。如果沒有指定,那么會將所有在INSTALLED_APPS中的app下的模型都映射到數(shù)據(jù)庫中。 - app_label migrationname:將某個
app下指定名字的migration文件映射到數(shù)據(jù)庫中。 - --fake:可以將指定的遷移腳本名字添加到數(shù)據(jù)庫中。但是并不會把遷移腳本轉換為SQL語句,修改數(shù)據(jù)庫中的表。
- --fake-initial:將第一次生成的遷移文件版本號記錄在數(shù)據(jù)庫中。但并不會真正的執(zhí)行遷移腳本。
3、showmigrations:查看某個app下的遷移文件。如果后面沒有app,那么將查看INSTALLED_APPS中所有的遷移文件。
4、sqlmigrate:查看某個遷移文件在映射到數(shù)據(jù)庫中的時候,轉換的SQL語句。
migrations中的遷移版本和數(shù)據(jù)庫中的遷移版本對不上怎么辦?
- 找到哪里不一致,然后使用
python manage.py --fake [版本名字],將這個版本標記為已經(jīng)映射。 - 刪除指定
app下migrations和數(shù)據(jù)庫表django_migrations中和這個app相關的版本號,然后將模型中的字段和數(shù)據(jù)庫中的字段保持一致,再使用命令python manage.py makemigrations重新生成一個初始化的遷移腳本,之后再使用命令python manage.py makemigrations --fake-initial來將這個初始化的遷移腳本標記為已經(jīng)映射。以后再修改就沒有問題了。
更多關于遷移腳本的。請查看官方文檔:https://docs.djangoproject.com/en/2.0/topics/migrations/
migrate怎么判斷哪些遷移腳本需要執(zhí)行:
他會將代碼中的遷移腳本和數(shù)據(jù)庫中django_migrations中的遷移腳本進行對比,如果發(fā)現(xiàn)數(shù)據(jù)庫中,沒有這個遷移腳本,那么就會執(zhí)行這個遷移腳本。
migrate做了什么事情:
- 將相關的遷移腳本翻譯成SQL語句,在數(shù)據(jù)庫中執(zhí)行這個SQL語句。
- 如果這個SQL語句執(zhí)行沒有問題,那么就會將這個遷移腳本的名字記錄到
django_migrations中。
執(zhí)行migrate命令的時候報錯的解決辦法:
原因:
執(zhí)行migrate命令會報錯的原因是。數(shù)據(jù)庫的django_migrations表中的遷移版本記錄和代碼中的遷移腳本不一致導致的。
解決辦法:
使用--fake參數(shù):
首先對比數(shù)據(jù)庫中的遷移腳本和代碼中的遷移腳本。然后找到哪個不同,之后再使用--fake,將代碼中的遷移腳本添加到django_migrations中,但是并不會執(zhí)行sql語句。這樣就可以避免每次執(zhí)行migrate的時候,都執(zhí)行一些重復的遷移腳本。
終極解決方案:
如果代碼中的遷移腳本和數(shù)據(jù)庫中的遷移腳本實在太多,就是搞不清了。那么這時候就可以使用以下終極解決方案:
- 終極解決方案原理:就是將之前的那些遷移腳本都不用了。重新來過。要將出問題的app下的所有模型和數(shù)據(jù)庫中表保持一致,重新映射。
- 將出問題的app下的所有模型,都和數(shù)據(jù)庫中的表保持一致。
- 將出問題的app下的所有遷移腳本文件都刪掉。再在
django_migrations表中將出問題的app相關的遷移記錄都刪掉。 - 使用
makemigrations,重新將模型生成一個遷移腳本。 - 使用
migrate --fake-initial參數(shù),將剛剛生成的遷移腳本,標記為已經(jīng)完成(因為這些模型相對應的表,其實都已經(jīng)在數(shù)據(jù)庫中存在了,不需要重復執(zhí)行了。) - 可以做其他的映射了。
100 根據(jù)已有的表自動生成模型:
在實際開發(fā)中,有些時候可能數(shù)據(jù)庫已經(jīng)存在了。如果我們用Django來開發(fā)一個網(wǎng)站,讀取的是之前已經(jīng)存在的數(shù)據(jù)庫中的數(shù)據(jù)。那么該如何將模型與數(shù)據(jù)庫中的表映射呢?根據(jù)舊的數(shù)據(jù)庫生成對應的ORM模型,需要以下幾個步驟:
Django給我們提供了一個inspectdb的命令,可以非常方便的將已經(jīng)存在的表,自動的生成模型。想要使用inspectdb自動將表生成模型。首先需要在settings.py中配置好數(shù)據(jù)庫相關信息。不然就找不到數(shù)據(jù)庫。示例代碼如下:
DATABASES = {
‘default‘: {
‘ENGINE‘: ‘django.db.backends.mysql‘,
‘NAME‘: "migrations_demo",
‘HOST‘: ‘127.0.0.1‘,
‘PORT‘: ‘3306‘,
‘USER‘: ‘root‘,
‘PASSWORD‘: ‘root‘
}
}
比如有以下表:




那么通過python manage.py inspectdb,就會將表轉換為模型后的代碼,顯示在終端:
from django.db import models
class ArticleArticle(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(blank=True, null=True)
create_time = models.DateTimeField(blank=True, null=True)
author = models.ForeignKey('FrontUserFrontuser', models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'article_article'
class ArticleArticleTags(models.Model):
article = models.ForeignKey(ArticleArticle, models.DO_NOTHING)
tag = models.ForeignKey('ArticleTag', models.DO_NOTHING)
class Meta:
managed = False
db_table = 'article_article_tags'
unique_together = (('article', 'tag'),)
class ArticleTag(models.Model):
name = models.CharField(max_length=100)
class Meta:
managed = False
db_table = 'article_tag'
class FrontUserFrontuser(models.Model):
username = models.CharField(max_length=100)
telephone = models.CharField(max_length=11)
class Meta:
managed = False
db_table = 'front_user_frontuser'
以上代碼只是顯示在終端。如果想要保存到文件中。那么可以使用>重定向輸出到指定的文件。比如讓他輸出到models.py文件中。示例命令如下:
python manage.py inspectdb > models.py
以上的命令,只能在終端執(zhí)行,不能在pycharm->Tools->Run manage.py Task...中使用。如果只是想要轉換一個表為模型。那么可以指定表的名字。示例命令如下:
python manage.py inspectdb article_article
2、修正模型:新生成的ORM模型有些地方可能不太適合使用。比如模型的名字,表之間的關系等等。那么以下選項還需要重新配置一下:
- 模型名:自動生成的模型,是根據(jù)表的名字生成的,可能不是你想要的。這時候模型的名字你可以改成任何你想要的。
- 模型所屬app:根據(jù)自己的需要,將相應的模型放在對應的app中。放在同一個app中也是沒有任何問題的。只是不方便管理。
- 模型外鍵引用:將所有使用
ForeignKey的地方,模型引用都改成字符串。這樣不會產(chǎn)生模型順序的問題。另外,如果引用的模型已經(jīng)移動到其他的app中了,那么還要加上這個app的前綴。 - 讓Django管理模型:將
Meta下的managed=False刪掉,如果保留這個,那么以后這個模型有任何的修改,使用migrate都不會映射到數(shù)據(jù)庫中。 - 當有多對多的時候,應該也要修正模型。將中間表注視了,然后使用
ManyToManyField來實現(xiàn)多對多。并且,使用ManyToManyField生成的中間表的名字可能和數(shù)據(jù)庫中那個中間表的名字不一致,這時候肯定就不能正常連接了。那么可以通過db_table來指定中間表的名字。示例代碼如下:
class Article(models.Model):
title = models.CharField(max_length=100, blank=True, null=True)
content = models.TextField(blank=True, null=True)
author = models.ForeignKey('front.User', models.SET_NULL, blank=True, null=True)
# 使用ManyToManyField模型到表,生成的中間表的規(guī)則是:article_tags
# 但現(xiàn)在已經(jīng)存在的表的名字叫做:article_tag
# 可以使用db_table,指定中間表的名字
tags = models.ManyToManyField("Tag",db_table='article_tag')
class Meta:
db_table = 'article'
- 表名:切記不要修改表的名字。不然映射到數(shù)據(jù)庫中,會發(fā)生找不到對應表的錯誤。
3、執(zhí)行命令python manage.py makemigrations生成初始化的遷移腳本。方便后面通過ORM來管理表。這時候還需要執(zhí)行命令python manage.py migrate --fake-initial,因為如果不使用--fake-initial,那么會將遷移腳本會映射到數(shù)據(jù)庫中。這時候遷移腳本會新創(chuàng)建表,而這個表之前是已經(jīng)存在了的,所以肯定會報錯。此時我們只要將這個0001-initial的狀態(tài)修改為已經(jīng)映射,而不真正執(zhí)行映射,下次再migrate的時候,就會忽略他。
4、將Django的核心表映射到數(shù)據(jù)庫中:Django中還有一些核心的表也是需要創(chuàng)建的。不然有些功能是用不了的。比如auth相關表。如果這個數(shù)據(jù)庫之前就是使用Django開發(fā)的,那么這些表就已經(jīng)存在了??梢圆挥霉芰?。如果之前這個數(shù)據(jù)庫不是使用Django開發(fā)的,那么應該使用migrate命令將Django中的核心模型映射到數(shù)據(jù)庫中。