Django筆記二十之手動編寫migration檔案

XHunter發表於2023-04-09

本文首發於公眾號:Hunter後端
原文連結:Django筆記二十之手動編寫migration檔案

前面介紹過,migration 檔案主要記錄的是 Django 系統 model 的變化,然後透過 migrate 命令將變化適配到資料庫中。

比如在某個 application 下新增了某張表,或者對某張表更改了欄位,可以生成 migration 檔案,然後透過 migrate 更改到資料庫。

除了系統能夠自動生成的,我們還可以手動建立 migration 檔案來運算元據庫,這個用途主要是用於比如,建立表後,需要寫入一些初始化的資料的情況。

  1. 基礎命令
  2. migration檔案介紹
  3. 自定義migration檔案
  4. RunSQL()
  5. RunPython()

1、基礎命令

關於 migration 的命令有如下幾條:

  • makemigrations
  • migrate
  • sqlmigrate
  • showmigrations

其中 前面三條命令在第二篇筆記中已經介紹過使用方法,這裡介紹一下 showmigrations。

這個作用主要是檢視某個 application 下的migration 檔案是否已經被更改到資料庫中,可以在 Django 系統的根目錄用下面的命令測試:

python3 manage.py showmigrations blog

可以看到下面的輸出:

blog
 [X] 0001_initial
 [X] 0002_auto_20220118_0926
 [X] 0003_auto_20220121_1016

其中,前面的 [X] 表示已經被更改到資料庫中,如果我們再對 blog 的 model 進行任意修改,然後執行 makemigrations 的操作,再次執行 showmigrations 的操作,可以看到下面的輸出:

blog
 [X] 0001_initial
 [X] 0002_auto_20220118_0926
 [X] 0003_auto_20220121_1016
 [ ] 0004_alter_book_price

可以看到最下面的一條記錄 [] 中是沒有 X 的,表示這條 migration 檔案沒有被執行 migrate。

2、migration檔案介紹

每一次透過 makemigrations 生成的 migration 檔案都存在系統中,一個最基礎的 migration 檔案像下面這樣:

from django.db import migrations, models


class Migration(migrations.Migration):


    dependencies = [('blog', '0001_initial')]


    operations = [
        migrations.DeleteModel('Tribble'),
        migrations.AddField('Author', 'rating', models.IntegerField(default=0)),
    ]

一個 Migration 的類下,有兩個引數,一個是 dependencies,一個是 operations

dependencies 作用是定位上一個執行的 migration 檔案的地方,因為每一次 migrate 的執行都是按照順序的

且他的引數是一個列表,列表的元素是一個元組,裡面有兩個引數,一個是 application 的名稱,一個是上一次執行的 migration 檔案,他是可以指定到多個 application 的,意義為在某兩個 application 的 migration 檔案之後再執行

operations 的作用是 migration 裡需要執行的操作,可以是欄位的增加、刪除、修改、也可以是表的建立和刪除

一個 migration 在執行 migrate 前,我們可以手動對其修改,甚至可以完全自己來定義

3、自定義migration檔案

前面介紹了 migration 檔案的基本結構,其中有一些關於欄位和 model 的操作方法,這些操作都可以透過 makemigration 的方式自動生成。

我們自定義的 migration 檔案,與上面的保持一致即可,自定義的 migration 檔案需要修改的地方是 operations 裡的元素。

假設我們有這樣一個需求,建立一張基礎對映表後,裡面是系統執行所必需的資料,需要在建立表後立即寫入,那麼就用到了我們這個自定義的 migration 檔案。

除了對錶欄位或者表的修改,還有兩種方法實現資料的寫入,

一種是使用 SQL 語句插入,用到的migration的函式是 RunSQL()

一種是使用 Django 的 ORM 語句,寫 python 的函式來插入,函式是 RunPython

假設建立 Blog 表的migration file 是 0001_create_blog.py

現在需要對其插入兩條資料,name 和 tagline 分別是 ('name_1', 'tagline_1') 和 ('name_2', 'tagline_2')

下面用 RunSQL() 和 RunPython() 兩種方式來分別介紹。

4、RunSQL()

RunSQL() 函式接受一個字串,或者一個陣列作為引數,引數的內容都是 SQL 語句,這也是為什麼函式名為 RunSQL()。

字串的形式為完整的 SQL 語句,比如我們需要插入這兩條資料,則是:

migrations.RunSQL(
	"INSERT INTO blog_blog (name, tagline) values('name_x_4', 'tagline_1'), ('name_x_5', 'tagline_2');"
)

如果是作為陣列傳入,形式則是:

migrations.RunSQL(
    sql=[
        (
            "INSERT INTO blog_blog (name, tagline) values(%s, %s), (%s, %s);",
            ['name_x_6', 'tagline_1', 'name_x_7', 'tagline_2']
        )
    ]
)

在陣列的傳入形式中,我們將需要插入的資料都放到一個陣列中傳入

reverse_sql
RunSQL() 函式除了 sql 引數,還有一個 reverse_sql 引數,用途是 sql 引數執行的 SQL 語句沒有執行成功的情況下的一種操作,一般是用於防止資料汙染。

假設說我們的 sql 為插入資料,但是因為某種原因,這條語句沒有正確插入,報錯了,那麼系統就會執行 reverse_sql 中的語句,作為一個可逆的操作。

以下是官方的一個使用示例:

migrations.RunSQL(
    sql=[("INSERT INTO musician (name) VALUES (%s);", ['Reinhardt'])],
    reverse_sql=[("DELETE FROM musician where name=%s;", ['Reinhardt'])],
)

5、RunPython()

RunSQL() 函式操作的是 SQL 語句,RunPython() 引數則是 Python 函式,可以將我們需要寫入的資料都寫到函式的步驟裡,然後在 RunPython() 中呼叫

以下是使用示例:

def insert_blog_data(apps, schema_editor):
    Blog = apps.get_model("blog", "Blog")
    db_alias = schema_editor.connection.alias

    Blog.objects.using(db_alias).create(name="name_3", tagline="tagline_3")
    Blog.objects.using(db_alias).create(name="name_4", tagline="tagline_4")



class Migration(migrations.Migration):
    dependencies = [
        ("blog", "0001_initial"),
    ]


    operations = [
        migrations.RunPython(insert_blog_data)
    ]

其中,insert_blog_data 是需要執行的函式,在這個函式里,有兩個預設引數,apps 和 schema_editor

apps 可以用來獲取我們需要的 model,根據函式 apps.get_model(),

這個函式傳入兩個引數,一個是 application,我們這裡是 blog,

一個 model 的名稱,我們這裡是 Blog

而 schema_editor 則是可以用於獲取資料庫的 alias

然後,資料的插入的方式就和普通的 model 的操作方法一致了。

RunPython() 函式和 RunSQL 一樣,也可以輸入兩個引數,第二個引數作用也是用於操作失敗的回退操作:

migrations.RunPython(insert_blog_data, reverse_insert)

以上就是介紹 migration 的全部內容了,下一篇筆記將介紹如何在 Django 中使用原生的 SQL 來查詢資料。

如果想獲取更多後端相關文章,可掃碼關注閱讀:
image

相關文章