django學習筆記5-數(shù)據(jù)庫 (42-100)

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)
  1. 使用makemigrations生成遷移腳本文件
    python manage.py makemigrations

  2. 使用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屬性來完成的。當然也可以自定義查詢對象。這部分功能會在后面講到。

  1. 根據(jù)主鍵進行查找:使用主鍵進行查找??梢允褂?code>objects.get方法。然后傳遞pk=xx的方式進行查找。示例代碼如下:
    book = Book.objects.get(pk=2)
    
  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時間?

  1. navie時間:不知道自己的時間表示的是哪個時區(qū)的。也就是不知道自己幾斤幾兩。比較幼稚。
  2. 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ù):

  1. auto_now:在每次這個數(shù)據(jù)保存的時候,都使用當前的時間。比如作為一個記錄修改日期的字段,可以將這個屬性設置為True。
  2. auto_now_add:在每次數(shù)據(jù)第一次被添加進去的時候,都使用當前的時間。比如作為一個記錄第一次入庫的字段,可以將這個屬性設置為True。

DateTimeField:

日期時間類型,類似于DateField。不僅僅可以存儲日期,還可以存儲時間。映射到數(shù)據(jù)庫中是datetime類型。這個Field也可以使用auto_nowauto_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的時候,即使這個Fieldnull=False,如果你沒有給這個Field傳遞任何值,那么Django也會使用一個空的字符串""來作為默認值存儲進去。因此如果再使用null=True,Django會產(chǎn)生兩種空值的情形(NULL或者空字符串)。如果想要在表單驗證的時候允許這個字符串為空,那么建議使用blank=True。如果你的FieldBooleanField,那么對應的可空的字段則為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只能有一個種類,并且通過外鍵進行引用。那么相關的示例代碼如下:

  1. 在同一個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 表關系

一對多

  1. 應用場景:比如文章和作者之間的關系。一個文章只能由一個作者編寫,但是一個作者可以寫多篇文章。文章和作者之間的關系就是典型的多對一的關系。
  2. 實現(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

一對一:

  1. 在Django中一對一是通過models.OnetToOneField來實現(xiàn)的。這個OneToOneField其實本質上就是一個外鍵,只不過這個外鍵有一個唯一約束(unique key),來實現(xiàn)一對一。
  2. 以后如果想要反向引用,那么是通過引用的模型的名字轉換為小寫的形式進行訪問。比如以下模型:
    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

多對多:

  1. 應用場景:比如文章和標簽的關系。一篇文章可以有多個標簽,一個標簽可以被多個文章所引用。因此標簽和文章的關系是典型的多對多的關系。

  2. 實現(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是為這種多對多的關系建立了一個中間表。這個中間表分別定義了兩個外鍵,引用到articletag兩張表的主鍵。

60ORM查詢條件詳解-準備工作


61 pycharm連接數(shù)據(jù)庫


62 查詢操作

查找是數(shù)據(jù)庫操作中一個非常重要的技術。查詢一般就是使用filter、exclude以及get三個方法來實現(xiàn)。我們可以在調用這些方法的時候傳遞不同的參數(shù)來實現(xiàn)查詢需求。在ORM層面,這些查詢條件都是使用field+__+condition的方式來使用的。以下將那些常用的查詢條件來一一解釋。

  1. exact:在底層會被翻譯成=。

  2. iexact:在底層會被翻譯成LIKE。

    • LIKE和=:大部分情況下都是等價的,只有少數(shù)情況下是不等價的。
    • exict和iexact:他們的區(qū)別其實就是LIKE和=的區(qū)別,因為exact會被翻譯成=,而iexact會被翻譯成LIKE。
    • 因為field__exact=xxx其實等價于filed=xxx,因此我們直接使用filed=xxx就可以了,并且因為大部分情況exactiexact又是等價的,因此我們以后直接使用field=xxx就可以了。

63

  1. QuerySet.query:query可以用來查看這個ORM查詢語句最終被翻譯成的SQL語句。但是query只能被用在QuerySet對象上,不能用在普通的ORM模型上。因此如果你的查詢語句是通過get來獲取數(shù)據(jù)的,那么就不能使用query,因為get返回的是滿足條件的ORM模型,而不是QuerySet。如果你是通過filter等其他返回QuerySet的方法查詢的,那么就可以使用query

  2. contains:使用大小寫敏感的判斷,某個字符串是否在指定的字段中。這個判斷條件會使用大小敏感,因此在被翻譯成SQL語句的時候,會使用like binary,而like binary就是使用大小寫敏感的。

  3. icontains:使用大小寫不敏感的判斷,某個字符串是否被包含在指定的字段中。這個查詢語句在被翻譯成SQL的時候,使用的是like,而likeMySQL層面就是不區(qū)分大小寫的。

  4. contains和icontains:在被翻譯成SQL的時候使用的是%hello%,就是只要整個字符串中出現(xiàn)了hello都能過夠被找到,而iexact沒有百分號,那么意味著只有完全相等的時候才會被匹配到。


64

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

    因為在cateogryForeignKey中指定了related_query_namearticles,因此你不能再使用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

  1. gt、gte、lt、lte:代表的是大于、大于等于、小于、小于等于的條件。示例代碼如下:
    articles = Article.objects.filter(id__lte=3)
    

66

  1. startswith、istartswith、endswith、iendswith:表示以某個值開始,不區(qū)分大小寫的以某個值開始、以某個值結束、不區(qū)分大小寫的以某個值結束。示例代碼如下:
    articles = Article.objects.filter(title__endswith="hello")
    

67

  1. 關于時間的查詢條件:
    • 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)
      

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

  1. 所有的聚合函數(shù)都是放在django.db.models下面。
  2. 聚合函數(shù)不能夠單獨的執(zhí)行,需要放在一些可以執(zhí)行聚合函數(shù)的方法下面中去執(zhí)行。比如aggregate。示例代碼如下:
    result = Book.objects.aggregate(Avg("price"))
    
  3. 聚合函數(shù)執(zhí)行完成后,給這個聚合函數(shù)的值取個名字。取名字的規(guī)則,默認是filed+__+聚合函數(shù)名字形成的。比如以上代碼形成的名字叫做price__avg。如果不想使用默認的名字,那么可以在使用聚合函數(shù)的時候傳遞關鍵字參數(shù)進去,參數(shù)的名字就是聚合函數(shù)執(zhí)行完成的名字。實示例代碼如下:
    result = Book.objects.aggregate(avg=Avg("price"))
    
    以上傳遞了關鍵字參數(shù)avg=Avg("price"),那么以后Avg聚合函數(shù)執(zhí)行完成的名字就叫做avg。
  4. aggregate:這個方法不會返回一個QuerySet對象,而是返回一個字典。這個字典中的key就是聚合函數(shù)的名字,值就是聚合函數(shù)執(zhí)行后的結果。

72

  1. aggregateannotate的相同和不同:
    • 相同:這兩個方法都可以執(zhí)行聚合函數(shù)。
    • 不同:
      • aggregate返回的是一個字典,在這個字典中存儲的是這個聚合函數(shù)執(zhí)行的結果。而annotate返回的是一個QuerySet對象,并且會在查找的模型上添加一個聚合函數(shù)的屬性。
      • aggregate不會做分組,而annotate會使用group by子句進行分組,只有調用了group by子句,才能對每一條數(shù)據(jù)求聚合函數(shù)的值。

73

  1. 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

  1. MaxMin:求指定字段的最大值和最小值。示例代碼如下:
    result = Author.objects.aggregate(max=Max("age"),min=Min("age"))
    

75

  1. Sum:求某個字段值的總和。示例代碼如下:
    result = BookOrder.objects.aggregate(total=Sum('price'))
    
    aggregateannotate方法可以在任何的QuerySet對象上調用。因此只要是返回了QuerySet對象,那么就可以進行鏈式調用。比如要獲取2018年度的銷售總額,那么可以先過濾年份,再求聚合函數(shù)。示例代碼如下:
    BookOrder.objects.filter(create_time__year=2018).aggregate(total=Sum('price'))
    

76

  1. 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

  1. 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='傳'))
      

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ù)庫中的遷移版本對不上怎么辦?

  1. 找到哪里不一致,然后使用python manage.py --fake [版本名字],將這個版本標記為已經(jīng)映射。
  2. 刪除指定appmigrations和數(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做了什么事情:

  1. 將相關的遷移腳本翻譯成SQL語句,在數(shù)據(jù)庫中執(zhí)行這個SQL語句。
  2. 如果這個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ù)庫中的遷移腳本實在太多,就是搞不清了。那么這時候就可以使用以下終極解決方案:

  1. 終極解決方案原理:就是將之前的那些遷移腳本都不用了。重新來過。要將出問題的app下的所有模型和數(shù)據(jù)庫中表保持一致,重新映射。
  2. 將出問題的app下的所有模型,都和數(shù)據(jù)庫中的表保持一致。
  3. 將出問題的app下的所有遷移腳本文件都刪掉。再在django_migrations表中將出問題的app相關的遷移記錄都刪掉。
  4. 使用makemigrations,重新將模型生成一個遷移腳本。
  5. 使用migrate --fake-initial參數(shù),將剛剛生成的遷移腳本,標記為已經(jīng)完成(因為這些模型相對應的表,其實都已經(jīng)在數(shù)據(jù)庫中存在了,不需要重復執(zhí)行了。)
  6. 可以做其他的映射了。

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‘
}
}
比如有以下表:


20190123223059415220.png
20190123223059552906.png
20190123223059666180.png
20190123223059778478.png

那么通過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ù)庫中。

原文地址:https://www.cnblogs.com/zheng-weimin/p/10311847.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容