Đây là bài viết thứ hai trong loạt bài về di cư Django của chúng tôi:
- Phần 1:Django Migrations:A Primer
- Phần 2:Tìm hiểu sâu hơn về các cuộc di cư Django (bài viết hiện tại)
- Phần 3:Di chuyển dữ liệu
- Video:Django 1.7 Migrations - A Primer
Trong phần trước của loạt bài này, bạn đã biết về mục đích của việc di chuyển Django. Bạn đã làm quen với các mẫu sử dụng cơ bản như tạo và áp dụng di chuyển. Bây giờ đã đến lúc tìm hiểu sâu hơn về hệ thống di chuyển và xem xét một số cơ chế cơ bản của nó.
Đến cuối bài viết này, bạn sẽ biết:
- Cách Django theo dõi quá trình di cư
- Cách di chuyển biết hoạt động cơ sở dữ liệu nào cần thực hiện
- Cách xác định sự phụ thuộc giữa các lần di chuyển
Sau khi hoàn thành phần này của hệ thống di chuyển Django, bạn sẽ chuẩn bị tốt để tạo các di chuyển tùy chỉnh của riêng mình. Hãy bắt đầu ngay nơi chúng ta đã dừng lại!
Bài viết này sử dụng bitcoin_tracker
Dự án Django được xây dựng trong Django Migrations:A Primer. Bạn có thể tạo lại dự án đó bằng cách làm qua bài viết đó hoặc bạn có thể tải xuống mã nguồn:
Tải xuống mã nguồn: Nhấp vào đây để tải xuống mã cho dự án di chuyển Django mà bạn sẽ sử dụng trong bài viết này.
Làm thế nào Django biết nên áp dụng phương thức di chuyển nào
Hãy tóm tắt lại bước cuối cùng của bài viết trước trong loạt bài này. Bạn đã tạo một lần di chuyển và sau đó áp dụng tất cả các lần di chuyển có sẵn với python manage.py migrate
.Nếu lệnh đó chạy thành công, thì các bảng cơ sở dữ liệu của bạn bây giờ khớp với định nghĩa của mô hình của bạn.
Điều gì xảy ra nếu bạn chạy lại lệnh đó? Hãy dùng thử:
$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, historical_data, sessions
Running migrations:
No migrations to apply.
Không có chuyện gì xảy ra! Khi quá trình di chuyển đã được áp dụng cho cơ sở dữ liệu, Django sẽ không áp dụng lần di chuyển này cho cơ sở dữ liệu cụ thể đó nữa. Đảm bảo rằng quá trình di chuyển chỉ được áp dụng một lần yêu cầu phải theo dõi các quá trình di chuyển đã được áp dụng.
Django sử dụng bảng cơ sở dữ liệu có tên django_migrations
. Django tự động tạo bảng này trong cơ sở dữ liệu của bạn vào lần đầu tiên bạn áp dụng bất kỳ di chuyển nào. Đối với mỗi lần di chuyển được áp dụng hoặc giả mạo, một hàng mới sẽ được chèn vào bảng.
Ví dụ:đây là bảng này trông như thế nào trong bitcoin_tracker
của chúng tôi dự án:
ID | Ứng dụng | Tên | Đã áp dụng |
---|---|---|---|
1 | contenttypes | 0001_initial | 2019-02-05 20:23:21.461496 |
2 | auth | 0001_initial | 2019-02-05 20:23:21.489948 |
3 | admin | 0001_initial | 2019-02-05 20:23:21.508742 |
4 | admin | 0002_logentry_remove... | 2019-02-05 20:23:21.531390 |
5 | admin | 0003_logentry_add_ac... | 2019-02-05 20:23:21.564834 |
6 | contenttypes | 0002_remove_content_... | 2019-02-05 20:23:21.597186 |
7 | auth | 0002_alter_permissio... | 2019-02-05 20:23:21.608705 |
8 | auth | 0003_alter_user_emai... | 2019-02-05 20:23:21.628441 |
9 | auth | 0004_alter_user_user... | 2019-02-05 20:23:21.646824 |
10 | auth | 0005_alter_user_last... | 2019-02-05 20:23:21.661182 |
11 | auth | 0006_require_content... | 2019-02-05 20:23:21.663664 |
12 | auth | 0007_alter_validator... | 2019-02-05 20:23:21.679482 |
13 | auth | 0008_alter_user_user... | 2019-02-05 20:23:21.699201 |
14 | auth | 0009_alter_user_last... | 2019-02-05 20:23:21.718652 |
15 | historical_data | 0001_initial | 2019-02-05 20:23:21.726000 |
16 | sessions | 0001_initial | 2019-02-05 20:23:21.734611 |
19 | historical_data | 0002_switch_to_decimals | 2019-02-05 20:30:11.337894 |
Như bạn có thể thấy, có một mục nhập cho mỗi lần di chuyển được áp dụng. Bảng không chỉ chứa các di chuyển từ historical_data
của chúng tôi ứng dụng, mà còn là di chuyển từ tất cả các ứng dụng đã cài đặt khác.
Trong lần di chuyển tiếp theo được chạy, Django sẽ bỏ qua các di chuyển được liệt kê trong bảng cơ sở dữ liệu. Điều này có nghĩa là, ngay cả khi bạn thay đổi thủ công tệp của quá trình di chuyển đã được áp dụng, Django sẽ bỏ qua những thay đổi này, miễn là đã có mục nhập cho nó trong cơ sở dữ liệu.
Bạn có thể lừa Django chạy lại quá trình di chuyển bằng cách xóa hàng tương ứng khỏi bảng, nhưng điều này hiếm khi là một ý tưởng hay và có thể khiến bạn bị hỏng hệ thống di chuyển.
Tệp di chuyển
Điều gì sẽ xảy ra khi bạn chạy python manage.py makemigrations <appname>
? Django tìm kiếm các thay đổi được thực hiện đối với các mô hình trong ứng dụng của bạn <appname>
. Nếu nó tìm thấy bất kỳ, chẳng hạn như một mô hình đã được thêm vào, thì nó sẽ tạo một tệp di chuyển trong migrations
thư mục con. Tệp di chuyển này chứa danh sách các thao tác để đồng bộ hóa lược đồ cơ sở dữ liệu với định nghĩa mô hình của bạn.
Lưu ý: Ứng dụng của bạn phải được liệt kê trong INSTALLED_APPS
và nó phải chứa migrations
thư mục có __init__.py
tập tin. Nếu không, Django sẽ không tạo bất kỳ quá trình di chuyển nào cho nó.
migrations
thư mục được tạo tự động khi bạn tạo ứng dụng mới với startapp
lệnh quản lý, nhưng rất dễ quên khi tạo ứng dụng theo cách thủ công.
Các tệp di chuyển chỉ là Python, vì vậy hãy xem tệp di chuyển đầu tiên trong historical_prices
ứng dụng. Bạn có thể tìm thấy nó tại historical_prices/migrations/0001_initial.py
. Nó sẽ trông giống như sau:
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = []
operations = [
migrations.CreateModel(
name='PriceHistory',
fields=[
('id', models.AutoField(
verbose_name='ID',
serialize=False,
primary_key=True,
auto_created=True)),
('date', models.DateTimeField(auto_now_add=True)),
('price', models.DecimalField(decimal_places=2, max_digits=5)),
('volume', models.PositiveIntegerField()),
('total_btc', models.PositiveIntegerField()),
],
options={
},
bases=(models.Model,),
),
]
Như bạn có thể thấy, nó chứa một lớp duy nhất được gọi là Migration
kế thừa từ django.db.migrations.Migration
. Đây là lớp mà khung di chuyển sẽ tìm kiếm và thực thi khi bạn yêu cầu nó áp dụng di chuyển.
Migration
lớp chứa hai danh sách chính:
-
dependencies
-
operations
Hoạt động di chuyển
Hãy xem xét các operations
liệt kê đầu tiên. Bảng này chứa các thao tác sẽ được thực hiện như một phần của quá trình di chuyển. Các phép toán là lớp con của lớp django.db.migrations.operations.base.Operation
. Dưới đây là các hoạt động phổ biến được tích hợp vào Django:
Lớp hoạt động | Mô tả |
---|---|
CreateModel | Tạo một mô hình mới và bảng cơ sở dữ liệu tương ứng |
DeleteModel | Xóa một mô hình và xóa bảng cơ sở dữ liệu của nó |
RenameModel | Đổi tên mô hình và đổi tên bảng cơ sở dữ liệu của nó |
AlterModelTable | Đổi tên bảng cơ sở dữ liệu cho một mô hình |
AlterUniqueTogether | Thay đổi các ràng buộc duy nhất của một mô hình |
AlterIndexTogether | Thay đổi chỉ mục của một mô hình |
AlterOrderWithRespectTo | Tạo hoặc xóa _order cột cho một mô hình |
AlterModelOptions | Thay đổi các tùy chọn mô hình khác nhau mà không ảnh hưởng đến cơ sở dữ liệu |
AlterModelManagers | Thay đổi các trình quản lý có sẵn trong quá trình di chuyển |
AddField | Thêm một trường vào một mô hình và cột tương ứng trong cơ sở dữ liệu |
RemoveField | Xóa một trường khỏi mô hình và xóa cột tương ứng khỏi cơ sở dữ liệu |
AlterField | Thay đổi định nghĩa của trường và thay đổi cột cơ sở dữ liệu của trường đó nếu cần |
RenameField | Đổi tên trường và nếu cần, cả cột cơ sở dữ liệu của trường đó |
AddIndex | Tạo chỉ mục trong bảng cơ sở dữ liệu cho mô hình |
RemoveIndex | Xóa chỉ mục khỏi bảng cơ sở dữ liệu cho mô hình |
Lưu ý cách các hoạt động được đặt tên sau các thay đổi được thực hiện đối với định nghĩa mô hình, không phải các hành động được thực hiện trên cơ sở dữ liệu. Khi bạn áp dụng một quá trình di chuyển, mỗi thao tác có trách nhiệm tạo ra các câu lệnh SQL cần thiết cho cơ sở dữ liệu cụ thể của bạn. Ví dụ:CreateModel
sẽ tạo CREATE TABLE
Câu lệnh SQL.
Ngoài ra, di chuyển có hỗ trợ cho tất cả các cơ sở dữ liệu tiêu chuẩn mà Django hỗ trợ. Vì vậy, nếu bạn tuân theo các thao tác được liệt kê ở đây, thì bạn có thể thực hiện nhiều hơn hoặc ít hơn bất kỳ thay đổi nào đối với mô hình của mình mà bạn muốn mà không cần phải lo lắng về SQL cơ bản. Đó là tất cả những gì đã làm cho bạn.
Lưu ý: Trong một số trường hợp, Django có thể không phát hiện chính xác các thay đổi của bạn. Nếu bạn đổi tên một mô hình và thay đổi một số trường của nó, thì Django có thể nhầm điều này với một mô hình mới.
Thay vì RenameModel
và một số AlterField
hoạt động, nó sẽ tạo ra một DeleteModel
và một CreateModel
hoạt động. Thay vì đổi tên bảng cơ sở dữ liệu cho mô hình, nó sẽ loại bỏ nó và tạo một bảng mới với tên mới, xóa tất cả dữ liệu của bạn một cách hiệu quả!
Hãy tạo thói quen kiểm tra các lần di chuyển đã tạo và kiểm tra chúng trên bản sao cơ sở dữ liệu của bạn trước khi chạy chúng trên dữ liệu sản xuất.
Django cung cấp thêm ba lớp hoạt động cho các trường hợp sử dụng nâng cao:
-
RunSQL
cho phép bạn chạy SQL tùy chỉnh trong cơ sở dữ liệu. -
RunPython
cho phép bạn chạy bất kỳ mã Python nào. -
SeparateDatabaseAndState
là một hoạt động chuyên biệt cho các mục đích sử dụng nâng cao.
Với các thao tác này, về cơ bản bạn có thể thực hiện bất kỳ thay đổi nào bạn muốn đối với cơ sở dữ liệu của mình. Tuy nhiên, bạn sẽ không tìm thấy các thao tác này trong quá trình di chuyển được tạo tự động với makemigrations
lệnh quản lý.
Kể từ Django 2.0, cũng có một số hoạt động dành riêng cho PostgreSQL có sẵn trong django.contrib.postgres.operations
mà bạn có thể sử dụng để cài đặt các phần mở rộng PostgreSQL khác nhau:
-
BtreeGinExtension
-
BtreeGistExtension
-
CITextExtension
-
CryptoExtension
-
HStoreExtension
-
TrigramExtension
-
UnaccentExtension
Lưu ý rằng quá trình di chuyển có chứa một trong các thao tác này yêu cầu người dùng cơ sở dữ liệu có đặc quyền siêu người dùng.
Cuối cùng nhưng không kém phần quan trọng, bạn cũng có thể tạo các lớp hoạt động của riêng mình. Nếu bạn muốn xem xét vấn đề đó, hãy xem tài liệu Django về cách tạo các hoạt động di chuyển tùy chỉnh.
Sự phụ thuộc khi di chuyển
Các phần phụ thuộc dependencies
danh sách trong một lớp di chuyển chứa bất kỳ quá trình di chuyển nào phải được áp dụng trước khi có thể áp dụng quá trình di chuyển này.
Trong 0001_initial.py
quá trình di chuyển mà bạn đã thấy ở trên, không có gì phải được áp dụng trước vì vậy không có phụ thuộc. Hãy xem lần di chuyển thứ hai trong historical_prices
ứng dụng. Trong tệp 0002_switch_to_decimals.py
, dependencies
thuộc tính của Migration
có một mục:
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('historical_data', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='pricehistory',
name='volume',
field=models.DecimalField(decimal_places=3, max_digits=7),
),
]
Phần phụ thuộc ở trên cho biết rằng việc di chuyển 0001_initial
của ứng dụng historical_data
phải được chạy trước. Điều đó có ý nghĩa, bởi vì việc di chuyển 0001_initial
tạo bảng chứa trường mà việc di chuyển 0002_switch_to_decimals
muốn thay đổi.
Quá trình di chuyển cũng có thể phụ thuộc vào quá trình di chuyển từ một ứng dụng khác, như thế này:
class Migration(migrations.Migration):
...
dependencies = [
('auth', '0009_alter_user_last_name_max_length'),
]
Điều này thường là cần thiết nếu một mô hình có Khóa ngoại trỏ đến một mô hình trong ứng dụng khác.
Ngoài ra, bạn cũng có thể thực thi quá trình di chuyển được chạy trước một lần di chuyển khác sử dụng thuộc tính run_before
:
class Migration(migrations.Migration):
...
run_before = [
('third_party_app', '0001_initial'),
]
Các phụ thuộc cũng có thể được kết hợp để bạn có thể có nhiều phụ thuộc. Chức năng này cung cấp rất nhiều tính linh hoạt, vì bạn có thể chứa các khóa ngoại phụ thuộc vào kiểu máy từ các ứng dụng khác nhau.
Tùy chọn xác định rõ ràng sự phụ thuộc giữa các lần di chuyển cũng có nghĩa là việc đánh số các lần di chuyển (thường là 0001
, 0002
, 0003
,…) Không đại diện chính xác thứ tự áp dụng quá trình di chuyển. Bạn có thể thêm bất kỳ phần phụ thuộc nào bạn muốn và do đó kiểm soát thứ tự mà không cần phải đánh số lại tất cả các lần di chuyển.
Xem quá trình di chuyển
Bạn thường không phải lo lắng về SQL mà quá trình di chuyển tạo ra. Nhưng nếu bạn muốn kiểm tra kỹ xem SQL được tạo có hợp lý hay chỉ tò mò nó trông như thế nào, thì Django sẽ giúp bạn bao gồm sqlmigrate
lệnh quản lý:
$ python manage.py sqlmigrate historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
CREATE TABLE "historical_data_pricehistory" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"date" datetime NOT NULL,
"price" decimal NOT NULL,
"volume" integer unsigned NOT NULL
);
COMMIT;
Làm điều đó sẽ liệt kê ra các truy vấn SQL cơ bản sẽ được tạo ra bởi quá trình di chuyển được chỉ định, dựa trên cơ sở dữ liệu trong settings.py
của bạn tập tin. Khi bạn chuyển tham số --backwards
, Django tạo SQL để hủy áp dụng việc di chuyển:
$ python manage.py sqlmigrate --backwards historical_data 0001
BEGIN;
--
-- Create model PriceHistory
--
DROP TABLE "historical_data_pricehistory";
COMMIT;
Khi bạn thấy đầu ra của sqlmigrate
đối với quá trình di chuyển phức tạp hơn một chút, bạn có thể đánh giá cao rằng bạn không cần phải tạo tất cả SQL này bằng tay!
Cách Django phát hiện các thay đổi đối với mô hình của bạn
Bạn đã thấy tệp di chuyển trông như thế nào và danh sách Operation
của nó như thế nào các lớp xác định các thay đổi được thực hiện đối với cơ sở dữ liệu. Nhưng làm cách nào Django biết chính xác những thao tác nào sẽ đi vào tệp di chuyển? Bạn có thể mong đợi rằng Django so sánh các mô hình của bạn với lược đồ cơ sở dữ liệu, nhưng không phải vậy.
Khi chạy makemigrations
, Django không không kiểm tra cơ sở dữ liệu của bạn. Nó cũng không so sánh tệp mô hình của bạn với một phiên bản trước đó. Thay vào đó, Django thực hiện tất cả các lần di chuyển đã được áp dụng và xây dựng trạng thái dự án về các mô hình sẽ trông như thế nào. Trạng thái dự án này sau đó được so sánh với các định nghĩa mô hình hiện tại của bạn và danh sách các hoạt động được tạo, khi được áp dụng, sẽ cập nhật trạng thái dự án với các định nghĩa mô hình.
Chơi cờ với Django
Bạn có thể coi mô hình của mình giống như một bàn cờ vua, và Django là một đại kiện tướng cờ vua đang xem bạn thi đấu với chính mình. Nhưng kiện tướng không theo dõi từng bước di chuyển của bạn. Kiện tướng chỉ nhìn vào bảng khi bạn hô makemigrations
.
Vì chỉ có một số nước đi hạn chế có thể xảy ra (và kiện tướng là đại kiện tướng), cô ấy có thể nghĩ ra các nước đi đã xảy ra kể từ lần cuối cùng cô ấy nhìn vào bảng. Cô ấy ghi chú một số và cho phép bạn chơi cho đến khi bạn hét lên makemigrations
một lần nữa.
Khi nhìn vào bàn cờ lần sau, kiện tướng không nhớ bàn cờ trông như thế nào lần trước, nhưng bà có thể xem qua ghi chép của mình về các nước đi trước đó và xây dựng mô hình tinh thần về bàn cờ trông như thế nào.
Bây giờ, khi bạn hét migrate
, kiện tướng sẽ chơi lại tất cả các nước đi đã ghi trên một bàn cờ vua khác và ghi chú vào một bảng tính mà các kỷ lục của cô ấy đã được áp dụng. Bàn cờ thứ hai này là cơ sở dữ liệu của bạn và bảng tính là django_migrations
bảng.
Sự tương tự này khá phù hợp, bởi vì nó minh họa một cách độc đáo một số hành vi của việc di cư Django:
-
Di chuyển Django cố gắng hiệu quả: Giống như kiện tướng giả định rằng bạn thực hiện số lần di chuyển ít nhất, Django sẽ cố gắng tạo ra những lần di chuyển hiệu quả nhất. Nếu bạn thêm trường có tên
A
thành một mô hình, sau đó đổi tên nó thànhB
, rồi chạymakemigrations
, sau đó Django sẽ tạo một quá trình di chuyển mới để thêm một trường có tênB
. -
Di chuyển Django có giới hạn của chúng: Nếu bạn thực hiện nhiều nước đi trước khi để kiện tướng nhìn vào bàn cờ, thì bà ta có thể không tập lại được động tác chính xác của từng quân cờ. Tương tự, Django có thể không đưa ra phương án di chuyển chính xác nếu bạn thực hiện quá nhiều thay đổi cùng một lúc.
-
Di chuyển Django mong muốn bạn chơi theo các quy tắc: Khi bạn làm bất cứ điều gì không mong muốn, chẳng hạn như lấy một quân cờ ngẫu nhiên ra khỏi bảng hoặc lộn xộn với các ghi chú, ban đầu, kiện tướng có thể không nhận thấy, nhưng sớm hay muộn, bà ấy sẽ bó tay và từ chối tiếp tục. Điều tương tự cũng xảy ra khi bạn sử dụng
django_migrations
bảng hoặc thay đổi lược đồ cơ sở dữ liệu của bạn bên ngoài di chuyển, chẳng hạn bằng cách xóa bảng cơ sở dữ liệu cho một mô hình.
Hiểu SeparateDatabaseAndState
Bây giờ bạn đã biết về trạng thái dự án mà Django xây dựng, đã đến lúc xem xét kỹ hơn hoạt động SeparateDatabaseAndState
. Thao tác này có thể thực hiện chính xác những gì tên ngụ ý:nó có thể tách trạng thái dự án (mô hình tinh thần mà Django xây dựng) khỏi cơ sở dữ liệu của bạn.
SeparateDatabaseAndState
được khởi tạo với hai danh sách hoạt động:
-
state_operations
chứa các hoạt động chỉ được áp dụng cho trạng thái dự án. -
database_operations
chứa các hoạt động chỉ được áp dụng cho cơ sở dữ liệu.
Thao tác này cho phép bạn thực hiện bất kỳ loại thay đổi nào đối với cơ sở dữ liệu của mình, nhưng bạn có trách nhiệm đảm bảo rằng trạng thái dự án phù hợp với cơ sở dữ liệu sau đó. Các trường hợp sử dụng mẫu cho SeparateDatabaseAndState
đang di chuyển một mô hình từ ứng dụng này sang ứng dụng khác hoặc tạo chỉ mục trên cơ sở dữ liệu khổng lồ mà không cần thời gian ngừng hoạt động.
SeparateDatabaseAndState
là một hoạt động nâng cao và bạn sẽ không cần vào ngày đầu tiên làm việc với di chuyển và có thể không bao giờ. SeparateDatabaseAndState
tương tự như phẫu thuật tim. Nó mang lại khá nhiều rủi ro và không phải là điều bạn làm chỉ để giải trí, nhưng đôi khi nó là một thủ tục cần thiết để giữ cho bệnh nhân sống sót.
Kết luận
Điều này kết thúc chuyến đi sâu của bạn vào cuộc di cư Django. Xin chúc mừng! Bạn đã đề cập đến khá nhiều chủ đề nâng cao và hiện đã có hiểu biết vững chắc về những gì xảy ra trong quá trình di cư.
Bạn đã học được rằng:
- Django theo dõi các lần di chuyển đã áp dụng trong bảng di chuyển Django.
- Di chuyển Django bao gồm các tệp Python thuần túy có chứa
Migration
lớp học. - Django biết những thay đổi nào cần thực hiện từ các thao tác
dependencies
danh sách trongMigration
lớp học. - Django so sánh các mô hình của bạn với trạng thái dự án mà nó xây dựng từ quá trình di chuyển.
Với kiến thức này, bây giờ bạn đã sẵn sàng giải quyết phần thứ ba của loạt bài về di chuyển Django, nơi bạn sẽ tìm hiểu cách sử dụng di chuyển dữ liệu để thực hiện các thay đổi một lần đối với dữ liệu của mình một cách an toàn. Hãy theo dõi!
Bài viết này đã sử dụng bitcoin_tracker
Dự án Django được xây dựng trong Django Migrations:A Primer. Bạn có thể tạo lại dự án đó bằng cách làm qua bài viết đó hoặc bạn có thể tải xuống mã nguồn:
Tải xuống mã nguồn: Nhấp vào đây để tải xuống mã cho dự án di chuyển Django mà bạn sẽ sử dụng trong bài viết này.