Phiên bản ngắn
Bạn KHÔNG nên sử dụng loaddata
lệnh quản lý trực tiếp trong quá trình di chuyển dữ liệu.
# Bad example for a data migration
from django.db import migrations
from django.core.management import call_command
def load_fixture(apps, schema_editor):
# No, it's wrong. DON'T DO THIS!
call_command('loaddata', 'your_data.json', app_label='yourapp')
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]
Phiên bản dài
loaddata
sử dụng django.core.serializers.python.Deserializer
mô hình sử dụng các mô hình cập nhật nhất để giải mã dữ liệu lịch sử trong quá trình di chuyển. Đó là hành vi không đúng.
Ví dụ: giả sử rằng có một sự di chuyển dữ liệu sử dụng loaddata
lệnh quản lý để tải dữ liệu từ một vật cố định và nó đã được áp dụng trên môi trường phát triển của bạn.
Sau đó, bạn quyết định thêm một trường bắt buộc mới vào mô hình tương ứng, vì vậy bạn thực hiện và thực hiện chuyển đổi mới đối với mô hình đã cập nhật của mình (và có thể cung cấp giá trị một lần cho trường mới khi ./manage.py makemigrations
bạn nhắc).
Bạn chạy lần di chuyển tiếp theo và tất cả đều ổn.
Cuối cùng, bạn đã phát triển xong ứng dụng Django của mình và bạn triển khai nó trên máy chủ sản xuất. Bây giờ đã đến lúc bạn chạy toàn bộ quá trình di chuyển từ đầu trên môi trường sản xuất.
Tuy nhiên, việc di chuyển dữ liệu không thành công . Đó là vì loaddata
không thể lưu mô hình deserialized from , đại diện cho mã hiện tại, với dữ liệu trống cho trường bắt buộc mới mà bạn đã thêm. Vật cố định ban đầu thiếu dữ liệu cần thiết cho nó!
Nhưng ngay cả khi bạn cập nhật lịch thi đấu với dữ liệu cần thiết cho trường mới, việc di chuyển dữ liệu vẫn không thành công . Khi quá trình di chuyển dữ liệu đang chạy, quá trình di chuyển tiếp theo thêm cột tương ứng vào cơ sở dữ liệu vẫn chưa được áp dụng. Bạn không thể lưu dữ liệu vào một cột không tồn tại!
Kết luận: trong quá trình di chuyển dữ liệu,loaddata
lệnh dẫn đến sự mâu thuẫn tiềm ẩn giữa mô hình và cơ sở dữ liệu. Bạn chắc chắn KHÔNG nênsử dụng nó trực tiếp trong quá trình di chuyển dữ liệu.
Giải pháp
loaddata
lệnh dựa vào django.core.serializers.python._get_model
hàm để lấy mô hình tương ứng từ một vật cố định, sẽ trả về phiên bản cập nhật nhất của mô hình. Chúng ta cần phải vá nó để nó trở thành mô hình lịch sử.
(Đoạn mã sau hoạt động cho Django 1.8.x)
# Good example for a data migration
from django.db import migrations
from django.core.serializers import base, python
from django.core.management import call_command
def load_fixture(apps, schema_editor):
# Save the old _get_model() function
old_get_model = python._get_model
# Define new _get_model() function here, which utilizes the apps argument to
# get the historical version of a model. This piece of code is directly stolen
# from django.core.serializers.python._get_model, unchanged. However, here it
# has a different context, specifically, the apps variable.
def _get_model(model_identifier):
try:
return apps.get_model(model_identifier)
except (LookupError, TypeError):
raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)
# Replace the _get_model() function on the module, so loaddata can utilize it.
python._get_model = _get_model
try:
# Call loaddata command
call_command('loaddata', 'your_data.json', app_label='yourapp')
finally:
# Restore old _get_model() function
python._get_model = old_get_model
class Migration(migrations.Migration):
dependencies = [
# Dependencies to other migrations
]
operations = [
migrations.RunPython(load_fixture),
]