использовать и учитьсяDjango
При кадрировании я обнаружил, что многие люди, в том числе и я,Django
Когда проект управляется версиями, обычноmigrations
файл добавлен в.gitignore
середина.
Автору всегда было интересно, верен ли такой подход, поэтому я пошел проверять официальную документацию и нашел следующий абзац.
оригинал:
You should think of migrations as a version control system for your database schema. makemigrations is responsible for packaging up your model changes into individual migration files - analogous to commits - and migrate is responsible for applying those to your database.
Файлы миграции для каждого приложения находятся в каталоге «миграции» внутри этого приложения и предназначены для фиксации и распространения как части его кодовой базы.Вы должны создать их один раз на своем компьютере для разработки, а затем запустить тот же миграции на компьютерах ваших коллег, ваших промежуточных компьютерах и, в конечном итоге, на ваших производственных компьютерах.
Китайский перевод:
Вы можете представить миграцию как систему контроля версий для вашей базы данных. Команда makemigrations отвечает за сохранение изменений вашей модели в файле миграции — так же, как коммиты — в то время как migrate отвечает за фиксацию изменений в базе данных.
Файл миграции для каждого приложения сохраняется в папке «миграции» каждого соответствующего приложения и подготавливает способ его выполнения в виде распределенной базы кода. Вы должны воссоздавать эти файлы каждый раз, когда запускаете одну и ту же миграцию на своем компьютере для разработки или на компьютере вашего коллеги, а затем и на рабочем компьютере.
Согласно официальной документации,migrations
Практика фиксации в репозитории неверна.
и если вы хотите использоватьdjango
Поставляется упакованнымTestCase
выполнять модульные тесты,migrations
также должны быть сохранены.
В следующей статье автор также представитdjango
серединаTestCase
опыт использования.
Далее в проектеmigrations
Некоторый опыт и некоторые проблемы, с которыми столкнулись.
Инициализация данных с миграциями
теперь у нас естьBook
Модель, которую я хочу использовать вmigrate
После этого инициализируйте некоторые данные.
class Book(models.Model):
name = models.CharField(max_length=32)
Например: сгенерируйте три имени какHamlet
,Tempest
,The Little Prince
книга.
в исполненииpython manage.py makemigrations
послеmigrations
папка будет создана0001_initial.py
документ.
файл содержитBook
Некоторый код для инициализации этой модели.
Знакомство с использованиемmigrations
При инициализации данных сначала введитеmigrations
Две часто используемые операции:
RunSQL
,RunPython
Как следует из названия, выполнитьSQL语句
иPython函数
.
Ниже я используюmigrations
серединаRunPython
для инициализации данных.
в соответствующем приложении
migrations
файл новый0002_init_book_data.py
migrations/.
├── 0001_initial.py.
└── 0002_init_book_data.py.затем увеличить
Migration
наследование классовdjango.db.migrations.Migration
, И вoperations
Добавьте код, который необходимо выполнить.
from django.db import migrations
"""
make_good_use_of_migrations 是App的名字
"""
def init_book_data(apps, schema_editor):
Book = apps.get_model('make_good_use_of_migrations', 'Book')
init_data = ['Hamlet', 'Tempest', 'The Little Prince']
for name in init_data:
book = Book(name=name)
book.save()
class Migration(migrations.Migration):
dependencies = [
('make_good_use_of_migrations', '0001_initial'),
]
# 这里要注意dependencies为上一次migrations的文件名称
operations = [
migrations.RunPython(init_book_data)
]
- бегать
python manage.py migrate
, вы можете видеть, что данные были сгенерированы в базе данных.
Восстановление данных с помощью миграции
Мы часто сталкиваемся с такой ситуацией, например мне нужно датьBook
Модель добавляет поле внешнего ключа, и это поле не может быть пустым, поэтому старые данные нужно обработать и исправить, мы можем это сделать.
- Поля, которые необходимо добавить в первую очередь
null
свойство установлено наTrue
, затем выполнитеmakemigrations
class Author(models.Model):
name = models.CharField(max_length=32)
class Book(models.Model):
name = models.CharField(max_length=32)
author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)
- в соответствующем приложении
migrations
файл новый0004_fix_book_data.py
migrations/.
├── 0001_initial.py.
├── 0002_init_book_data.py.
├── 0003_auto_20181204_0533.py.
└── 0004_fix_book_data.py.
from django.db import migrations
def fix_book_data(apps, schema_editor):
Book = apps.get_model('make_good_use_of_migrations', 'Book')
Author = apps.get_model('make_good_use_of_migrations', 'Author')
for book in Book.objects.all():
author, _ = Author.objects.get_or_create(name='%s author' % book.name)
book.author = author
book.save()
class Migration(migrations.Migration):
dependencies = [
('make_good_use_of_migrations', '0003_auto_20181204_0533'),
]
operations = [
migrations.RunPython(fix_book_data)
]
-
Наконец,
Book
в моделиauthor
Свойства поляnull
установить какFalse
и выполнитьmakemigrations
. После выполнения появится,You are trying to change the nullable field 'author' on book to non-nullable without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous data migration) 3) Quit, and let me add a default in models.py Select an option:
Выберите здесь
2
элемент, что означает игнорировать данные о том, что поле уже пусто, используйтеRunPython
илиRunSQL
Сделай сам.Выполнить после завершения выбора
python manage.py migrate
, вы обнаружите, что данные в базе данных будут обрабатываться так, как мы ожидали.
Устранение конфликтов, вызванных миграциями во время разработки с участием нескольких человек.
Чтобы смоделировать многоотраслевую разработку с участием нескольких человек, создайте новыйmaster-2
, и версия создаетсяAuthor
перед уроком и вBook
добавил в модельremark
поле.
model.py
Содержимое файла следующее:
class Book(models.Model):
name = models.CharField(max_length=32)
remark = models.CharField(max_length=32, null=True)
migrations
Каталог файлов выглядит следующим образом:
migrations/.
├── 0001_initial.py.
├── 0002_init_book_data.py.
└──0003_book_remark.py.
когда мы ставимmaster-2
Код объединен вmaster
, ты найдешьmigrations
Повторяющиеся номера появляются в0003
и они совместно зависят от0002_init_book_data
.
migrations/.
├── 0001_initial.py.
├── 0002_init_book_data.py.
├── 0003_auto_20181204_0533.py
├── 0003_book_remark.py
├── 0004_fix_book_data.py
└──0005_auto_20181204_0610.py.
Затем нужно использовать команду:
python manage.py makemigrations --merge
затем вmigrations
каталог для создания0006_merge_20181204_0622.py
документ
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('make_good_use_of_migrations', '0005_auto_20181204_0610'),
('make_good_use_of_migrations', '0003_book_remark'),
]
operations = [
]
В это время выполняетсяpython manage.py migrate
Вот и все.
Вопросы, требующие внимания при использовании миграции. RunPython
Невозможно вызвать функцию класса модели в функции
предположим вBook
В модели определены две функцииprint_name
и функции классаprint_class_name
class Book(models.Model):
name = models.CharField(max_length=32)
author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)
remark = models.CharField(max_length=32, null=True)
def print_name(self):
print(self.name)
@classmethod
def print_class_name(cls):
print(cls.__name__)
существуетmigrations
Его нельзя назвать средним, да и автор не изучал его внимательно.Book
При инициализации класса инициализируются только поля.
from django.db import migrations
def fix_book_data(apps, schema_editor):
Book = apps.get_model('make_good_use_of_migrations', 'Book')
Author = apps.get_model('make_good_use_of_migrations', 'Author')
for book in Book.objects.all():
author, _ = Author.objects.get_or_create(name='%s author' % book.name)
book.author = author
"""
book.print_name()
book.print_class_name()
这样调用会报错
"""
book.save()
class Migration(migrations.Migration):
dependencies = [
('make_good_use_of_migrations', '0003_auto_20181204_0533'),
]
operations = [
migrations.RunPython(fix_book_data)
]
Метод сохранения, переопределенный классом модели в функции, недействителен, включая унаследованный метод сохранения.
существуетmigrations
Все переписано вsave
методы не будут работать, например:
class Book(models.Model):
name = models.CharField(max_length=32)
author = models.ForeignKey(to=Author, on_delete=models.CASCADE, null=False)
remark = models.CharField(max_length=32, null=True)
def print_name(self):
print(self.name)
@classmethod
def print_class_name(cls):
print(cls.__name__)
def save(self, *args, **kwargs):
if not self.remark:
self.remark = 'This is a book.'
Окончательная инициализация сгенерированных данныхremark
Значение поля по-прежнему пусто.
Все сигналы, зарегистрированные моделью в функции, недействительны
Хотя даваяBook
модель зарегистрированаsignal
,Но когдаmigrations
все равно не будет работать
@receiver(pre_save, sender=Book)
def generate_book_remark(sender, instance, *args, **kwargs):
print(instance)
if not instance.remark:
instance.remark = 'This is a book.'
Не помещайте обработку данных в файл миграции для изменений модели
При восстановлении данных или создании данных инициализации не помещайте функции обработчика в автоматически сгенерированные изменения или сгенерированные поля или модели.migrations
файл, например:
class Migration(migrations.Migration):
dependencies = [
('make_good_use_of_migrations', '0004_fix_book_data'),
]
operations = [
migrations.AlterField(
model_name='book',
name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE,
to='make_good_use_of_migrations.Author'),
),
"""
migrations.RunPython(xxx) 不要把数据处理放到模型变更中
"""
]
Основная причина не ставить их вместе заключается в том, что когдаRunPython
Логика обработки функции не может выполняться вниз после возникновения исключения.
django_migrations
Эта транзакция не будет зарегистрирована,Но изменения в структуре таблицы выполнены!
Это тожеDjango migrations
Плохая часть, правильная должна быть откатом базы данных при возникновении исключения.
Как только это произойдет, вы сможете только вручнуюmigrations
имя как0005_auto_20181204_0610
, записать в таблицу базы данныхdjango_migrations
, тоRunPython
Входная логика вынесена отдельно.
Суммировать
Вышеуказанный автор использовал в проектеDjango
обрамленныйmigrations
опыт, следующая статья познакомитDjango
обрамленныйTestCase
.
Исходный код этой статьи будет размещен вgithub
начальство,GitHub.com/ELFGuangzhouP/Великое ограбление…
本人博客原文地址:elfgzp.cn/2018/12/04/…