我正在使用Django和Django Rest Framework构建REST API和服务器。我们正在使用一个postgres数据库。
我需要简化一个设计不良的关系。我们有一个模型(House
)与另一个(ManyToMany
)有City
关系。实际上,如果这是ForeignKey
关系,就足够了。
我用Google搜索,但找不到任何博客文章或文档,说明如何朝着这个方向正确迁移。我只能找到其他方式(从FK到M2M)。
我有98%的把握确保服务器上的所有数据都符合FK关系(这意味着我敢肯定,所有房屋都只有一个城市)。我们出于几种原因需要更改关系,但无法保留M2M。
恐怕只是更改模型并运行makemigrations
和migrate
。我想知道,您如何正确地从M2M迁移到FK?有什么需要注意的警告事项吗?如果令人惊讶的是有多个城市的房屋,我该如何处理数据?如果重要的话,数据集仍然很小(少于1万个条目)。
非常感谢您。
答案 0 :(得分:2)
编辑,首先创建数据库备份
首先创建一个新的临时FK关系
_city = models.ForeignKey(...)
并进行迁移
python manage.py makemigration
python manage.py migrate
然后您需要通过创建一个空的迁移文件在相关的应用中创建一个new data migration:
python manage.py makemigration --empty myhouseapp
,然后在该文件中手动将新关系从M2M分配给FK。看起来像这样:
from django.db import migrations
def save_city_fk(apps, schema):
City = apps.get_model('myapp', 'City')
House = apps.get_model('myapp', 'City')
for house in House.objects.all():
house._city = house.cities.all().first() # Or whatever criterea you want
house._city.save()
def save_city_m2m(apps, schema):
# This should do the reverse
City = apps.get_model('myapp', 'City')
House = apps.get_model('myapp', 'City')
for house in House.objects.all():
if house._city:
house.cities.add(house._city)
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.RunPython(save_city_fk, save_city_m2m)
]
删除M2M字段,然后创建另一个迁移。
python manage.py makemigrations
将FK从_city
重命名为city
并创建最终迁移
python manage.py makemigrations
然后迁移:
python manage.py migrate
答案 1 :(得分:1)
首先,显然,您需要确保(通过进行适当的查询)每个房屋实际上只有一个城市。如果有多个城市的房屋,则需要通过从关系中删除城市,分割房屋等来解决冲突。
在那之后,您可以分步骤进行:
House
中创建一个新的FK,并使用旧的m2m关系中的一个城市ID进行迁移并填充该城市答案 2 :(得分:0)
class Designation(models.Model):
desig_id = models.AutoField(primary_key=True)
title = models.CharField(max_length=60, unique=True)
job_discription = models.CharField(max_length=2000)
def __str__(self):
return self.title
class Employee(AbstractUser):
middle_name = models.CharField(max_length=20, blank=True, null=True)
basic_salary = models.FloatField(default=1)
designation = models.ManyToManyField(Designation, default=None, blank=True,
through='EmployeeDesignation')
certification = models.ManyToManyField(Certification, default=None, null=True, blank=True,
through='EmployeeCertification')
emp_img = models.FileField(default=None,upload_to='employees')
leaves_allowed = models.IntegerField(default=25)
leave_balance = models.IntegerField(default=25)
leave_count = models.IntegerField(default=0)
objects = EmployeeManager()
def __str__(self):
return self.first_name+' '+self.last_name
class EmployeeDesignation(models.Model):
desig = models.ForeignKey(Designation, on_delete=models.CASCADE)
emp = models.ForeignKey(Employee, on_delete=models.CASCADE)
class Meta:
unique_together = (('emp', 'desig'),)
答案 3 :(得分:0)
我添加了一个像city = models.ForeignKey(City, related_name='has_houses', blank=True, null=True)
这样的字段,以避免related_name
的反向关系并使FK空白。然后我运行了makemigrations
和migrate
。
接下来,我运行python manage.py makemigrations --empty houses
是因为我的应用名为houses
。
我添加了迁移代码(请参见下文)。然后我跑了migrate
。
我删除了M2M字段以及related_name
,null
和blank
约束,最后一次运行了makemigrations
和migrate
。
迁移代码:
# -*- coding: utf-8 -*-
# Generated by Django 1.11.15 on 2019-02-15 09:09
from __future__ import unicode_literals
from django.db import migrations
def save_city_fk(apps, schema):
City = apps.get_model('cities', 'City')
House = apps.get_model('houses', 'House')
for house in House.objects.all():
house.city = house.cities.all().first()
house.save()
class Migration(migrations.Migration):
dependencies = [
('houses', '0003_auto_20190215_0949'),
]
operations = [
migrations.RunPython(save_city_fk),
]