Закрыть

Для эффективной работы на сайте используются cookie и обработка персональных данных. Пользуясь этим сайтом, вы соглашаетесь с правилами использования сайта. Подробнее

Цитата дня

Vivazzi.ru

Личный сайт Мальцева Артема

Вы счастливы или несчастны не благодаря тому, что вы имеете, и не в связи с тем, кем являетесь, где находитесь или что делаете; ваше состояние определяется тем, что вы обо всем этом думаете.

Дейл Карнеги

Миграции в Django. Примеры

18 февраля 2016 г. 23:59

Добавление внешнего ключа к существующей модели по типу один-ко-многим. Внешний ключ не должен быть пустым

Пусть у нас есть товар:

# models.py
class Product(nodels.Model):
    product_name = models.CharField('Product Name', max_length=255)
    unit_price = models.DecimalField('Unit price'), decimal_places=0)

Наша задача добавить категорию для товара, причём товар не может быть без категории:

# models.py
class Category(models.Model):
    title = models.CharField('Название', max_length=20)

class Product(nodels.Model):
    category = models.ForeignKey(Category, verbose_name='Category', related_name='products')
    product_name = models.CharField('Product Name', max_length=255)
    unit_price = models.DecimalField('Unit price'), decimal_places=0)

Для выполнения задачи нужно сначала добавить category с null=True, blank=True к модели Product:

# models.py
class Category(models.Model):
    title = models.CharField('Название', max_length=20)

class Product(nodels.Model):
    category = models.ForeignKey(Category, verbose_name='Category', null=True, blank=True, related_name='products')
    product_name = models.CharField('Product Name', max_length=255)
    unit_price = models.DecimalField('Unit price'), decimal_places=0)

И выполнить команду makemigration.

Затем уберём из поля category параметры null=True, blank=True:

# models.py
class Category(models.Model):
    title = models.CharField('Название', max_length=20)

class Product(nodels.Model):
    category = models.ForeignKey(Category, verbose_name='Category', related_name='products')
    product_name = models.CharField('Product Name', max_length=255)
    unit_price = models.DecimalField('Unit price'), decimal_places=0)

И снова выполним makemigration, указывая, что мы сами обработаем null строки. В созданной миграции нужно добавить код, создающий категорию, которая будет записываться для наших товаров:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def set_category(apps, schema_editor):
    Category = apps.get_model('myshop', 'Category')
    category = Category(title='Food')
    category.save()

    Product = apps.get_model('myshop', 'Product')
    for product in Product.objects.all():
        product.category = category
        product.save()


class Migration(migrations.Migration):
    dependencies = [
        ('myshop', '0003_auto_20160217_1721'),
    ]

    operations = [
        migrations.RunPython(set_category),
        migrations.AlterField(
            model_name='product',
            name='category',
            field=models.ForeignKey(verbose_name='Category', to='myshop.Category'),
        ),
    ]

Добавление поля с атрибутом unique=True

Допустим, нам нужно добавить к модели поля slug и order, которые не должны быть пустыми. Причём slug должен иметь атрибут unique=True.

Сначала нужно добавить необходимые поля без unique=True:

class Project(models.Model):
    title = models.CharField('Название', max_length=255)
    slug = models.SlugField('Путь', max_length=80, help_text=slug_ht)
    order = models.PositiveIntegerField('Порядок', db_index=True)

Теперь вызываем makemigration. Django спросит нас, какими данными заполнить добавляемые поля существующих записей в базе данных. Пока можно заполнить произвольными данными:

You are trying to add a non-nullable field 'order' to project 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)
 2) Quit, and let me add a default in models.py
Select an option:  1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now()
>>>  0
You are trying to add a non-nullable field 'slug' to project 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)
 2) Quit, and let me add a default in models.py
Select an option:  1
Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now()
>>>  'slug'

Теперь добавляем к полю slug атрибут unique=True:

class Project(models.Model):
    title = models.CharField('Название', max_length=255)
    slug = models.SlugField('Путь', max_length=80, unique=True, help_text=slug_ht)
    order = models.PositiveIntegerField('Порядок', db_index=True)

Вызываем makemigration, открываем созданную миграцию и добавляем свой код правки данных, например, так:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import cms.models.fields


def generate_slug_and_order(apps, schema_editor):
    Project = apps.get_model('projects', 'Project')
    for i, obj in enumerate(Project.objects.all(), start=1):
        obj.slug = '{}_{}'.format(obj.slug, i)
        obj.order = i
        obj.save()


class Migration(migrations.Migration):

    dependencies = [
        ('projects', '0005_auto_20160328_1839'),
    ]

    operations = [
        migrations.RunPython(generate_slug_and_order),
        migrations.AlterField(
            model_name='project',
            name='slug',
            field=models.SlugField(unique=True, max_length=80, verbose_name='\u041f\u0443\u0442\u044c'),
        ),
    ]

По необходимости теперь можно ручками завершить правку данных в админке, то что кодом было бы дольше.

Отдельные примеры

Задать порядок (order) для модели

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
import cms.models.fields


def generate_order(apps, schema_editor):
    Project = apps.get_model('projects', 'Project')
    for i, obj in enumerate(Project.objects.all(), start=1):
        obj.order = i
        obj.save()


class Migration(migrations.Migration):

    dependencies = [
        ('projects', '0005_auto_20160328_1839'),
    ]

    operations = [
        migrations.RunPython(generate_order),
        migrations.AlterField(
            model_name='project',
            name='order',
            field=models.PositiveIntegerField(verbose_name='\u041f\u043e\u0440\u044f\u0434\u043e\u043a', db_index=True),
        ),
    ]

Установить slug

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from sb_core.utils.sb_utils import transliterate


def set_slug(apps, schema_editor):
    Receipt = apps.get_model('products', 'Receipt')
    for obj in Receipt.objects.all():
        obj.slug = transliterate(obj.title, is_slug=True)
        obj.save()


class Migration(migrations.Migration):

    dependencies = [
        ('products', '0007_receipt_slug'),
    ]

    operations = [
        migrations.RunPython(set_slug),
    ]

Задать статус заказам

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def paid(obj):
    return sum([payment.amount for payment in obj.payments.all()])


def is_fully_paid(obj):
    return paid(obj) >= obj.amount


def set_status(apps, schema_editor):
    Order = apps.get_model('orders', 'Order')
    for obj in Order.objects.all():
        if paid(obj) == 0: obj.status ='waiting_payment'
        elif is_fully_paid(obj): obj.status = 'fully_paid'
        else: obj.status = 'partially_paid'

        obj.save()


class Migration(migrations.Migration):

    dependencies = [
        ('orders', '0025_order_status'),
    ]

    operations = [
        migrations.RunPython(set_status),
    ]

Убрать null=True

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


def set_blank(apps, schema_editor):
    MyModel = apps.get_model('my_app', 'MyModel')
    for obj in MyModel.objects.all():
        obj.content = obj.content or ''
        obj.template = obj.template or ''
        obj.title = obj.title or ''
        obj.save()


class Migration(migrations.Migration):

    dependencies = [
        ('my_app', '0007_auto_20160509_1225'),
    ]

    operations = [
        migrations.RunPython(set_blank),
        migrations.AlterField(
            model_name='mymodel',
            name='content',
            field=models.TextField(default='', blank=True),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='mymodel',
            name='template',
            field=models.CharField(default='', max_length=255, blank=True),
            preserve_default=False,
        ),
        migrations.AlterField(
            model_name='mymodel',
            name='title',
            field=models.CharField(default='', max_length=100, blank=True),
            preserve_default=False,
        ),
    ]

Оцените статью

0 из 5 (всего 0 оценок)

Поля, отмеченные звёздочкой ( * ) , являются обязательными.

Спасибо за ваш отзыв!

Автор статьи

Права на использование данной статьи, расположенной на настоящей странице http://vivazzi.ru/it/migration-examples/:

Разрешается копировать статью с указанием её автора и ссылки на оригинал без использования параметра rel="nofollow" в теге <a>. Использование:

Автор статьи: Мальцев Артём
Ссылка на статью: <a href="http://vivazzi.ru/it/migration-examples/">http://vivazzi.ru/it/migration-examples/</a>

Подробнее: Правила использования сайта

Вам нужно саморазвиваться или вы хотите зарабатывать деньги?

Или вы ищите хорошие IT сервисы или книги? Сохраните свое время и взгляните на мою подборку рекомендаций, которыми постоянно пользуюсь.
Посмотреть рекомендации

Комментариев: 0

Вы можете оставить комментарий как незарегистрированный пользователь. Но, зарегистрировавшись, вы сможете получать оповещения об ответах, а также иметь доступ к своему личному аккаунту для просмотра своих комментариев.

Чтобы оставить комментарий от своего имени войдите или зарегистрируйтесь обычным способом или через социальные сети:

Отправить

На данный момент нет специального поиска, поэтому я предлагаю воспользоваться обычной поисковой системой, например, Google, добавив "vivazzi" после своего запроса.

Попробуйте