Django: добавление отношения FK к существующей модели - PullRequest
1 голос
/ 17 января 2020

У меня есть проект, в котором мне нужно изменить обычное поле на поле FK, и это в нескольких моделях. Проблема в том, что проект продуктивен, а база данных (Postgres) полна записей.

Я начал локально, просто бездумно изменив тип поля на forgein_key () и попытавшись выполнить миграцию. Кажется, что Django настолько умен, что пытается обновить это поле, но спотыкается из-за значений в модели, которых нет в связанной модели. Как я могу пропустить эти ошибки? Могу ли я сказать Django, чтобы установить FK == NULL, если соответствующая запись не существует?

Вот какой-то код:

from persons.models import Customer, Employee

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer = models.ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    employee_id = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    objects = models.Manager()

До тех полей, где поля char и отношение были сделано в коде бэкэнда. Причина, по которой я пытаюсь это сделать, заключается в том, что мне нужны функции из Django, такие как select / prefech_related для функции поиска в таблице страхования внешнего интерфейса. Пользователь должен иметь возможность искать также имя застрахованного лица, а не только его идентификатор.

Это ошибка, вызванная отсутствием записей:

django.db.utils.IntegrityError: insert or update on table "insurance_insurance" violates foreign key constraint "insurance_insur_customer_id_98e84761_fk_person_"
DETAIL:  Key (customer_id)=(100883) is not present in table "person_customer".

Может кто-нибудь сказать мне solid безопасный способ сделать эти отношения?

Заранее спасибо!

Ответы [ 2 ]

2 голосов
/ 17 января 2020

Вы можете выполнить следующие шаги:

1) Создать дубликаты полей с помощью FK,

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer = models.CharField()
    employee_id = models.CharField()
    customer_new = models.ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    employee_id_new = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    objects = models.Manager()

2) Создать скрипт для переноса данных из старых полей в новые поля как ForeignKey

3) Удалить старые поля

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer_new = models.ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    employee_id_new = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    objects = models.Manager()

4) Переименовать новые поля

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer = models.ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    employee_id = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
    objects = models.Manager()

Ps

Не забудьте сделать резервную копию БД перед каждой миграцией Откат в случае проблем.

0 голосов
/ 17 января 2020

Вы, вероятно, должны сделать это до , превращая IntegerField (?) В ForeignKey. Сначала мы можем сделать IntegerField обнуляемым:

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    <b>customer = models.IntegerField(null=True, db_column='customer_id')</b>
    employee_id = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)

. Затем можно выполнить миграцию с помощью:

python3 manage.py <b>makemigrations</b>

В файле перенос данных [Django -doc] , затем мы можем запустить запрос, чтобы установить значения, для которых нет клиентов, NULL:

from django.db.models import Q

def set_invalid_customers_to_null(apps, schema_editor):
    Customer = apps.get_model('app', 'Customer')
    Insurance = apps.get_model('app', 'Insurance')
    Insurance.objects.filter(
        <b>~Q(customer__in=Customer.objects.values_list('pk', flat=True))</b>
    ).update(customer=None)

. Вы можете выполнить отдельную миграцию или просто добавьте эту функцию миграции в файл миграции, созданный в результате изменения модели.

Затем мы можем изменить тип столбца на ForeignKey:

class Insurance(models.Model):
    id = models.BigAutoField(primary_key=True)
    customer = <b>models.ForeignKey(</b>Customer, null=True, db_column='customer_id'<b>)</b>
    employee_id = ForeignKey(Customer, models.SET_NULL, blank=True, null=True)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...