在django中将子类模型实例转换为另一个子类模型实例?

问题描述 投票:0回答:4

我有一个ModelBase,还有ModelA、ModelB。

我想将 ModelA 实例更改为 ModelB 实例。 (我可以处理他们属性的差异)

我看过相关问题,但对我来说不太有用。

如何从现有的基础模型实例创建继承的 django 模型实例?
更改 django 模型上孩子的班级

  • 编辑

当您有场所 - 餐厅/酒吧关系时,
我觉得能把餐厅改成酒吧是很合理的。

django model
4个回答
4
投票

我必须处理同样的问题,yuvi 和 arctelix 的答案都不适合我。 yuvi 解决方案给出错误,arctelix 解决方案使用新 pk 创建新对象。

这里的目标是更改子类模型,同时保持原始超类与旧的 pk 一样。

首先:删除old子类并保留超类。检查Django文档

第二: 添加新的子类及其字段并将超类传递给它。 检查这个q

示例: 一个地点可以是餐厅或咖啡馆,而您想将餐厅地点更改为咖啡馆;如下:

class Place(models.Model):
            name = models.CharField(max_length=50)
            address = models.CharField(max_length=80)

class Caffe(Place):
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)

class Restaurant(Place):
    serves_tea = models.BooleanField(default=False)
    serves_coffee = models.BooleanField(default=False)

# get the objecte to be changed
rest = Restaurant.objects.get(pk=1) #arbitrary number
#delete the subclass while keeping the parent
rest.delete(keep_parents=True)

place = Place.objects.get(pk=1) # the primary key must be the same as the deleted restaurant

# Create a caffe and pass the original place
caffee = Caffe(place_ptr_id=place.pk) #this will empty the parent field

#update parent fields
caffee.__dict__.update(place.__dict__)

#add other field
........

#save the caffe
caffee.save()

3
投票

我将创建第二个模型的一个全新实例,其共享属性具有相同的值,然后删除旧的实例。对我来说似乎是最干净的方式。

如果 ModelBase 是抽象的:

instance = ModelA.objects.get(pk=1) #arbitrary        

# find parent class fields:
fields = [f.name for f in ModelBase._meta.fields]

# get the values from the modelA instance
values = dict( [(x, getattr(instance, x)) for x in fields] )

#assign same values to new instance of second model
new_instance = ModelB(**values) 

#add any additional information to new instance here

new_instance.save() #save new one
instance.delete() # remove the old one

但是,如果 ModelBase 不是抽象,则您必须采取额外的解决方法:

fields = [f.name for f in ModelBase._meta.fields if f.name != 'id']
#... other parts are the same...

new_instance.modelbase_ptr = instance.modelbase_ptr #re-assign related parent
instance.delete() #delete this first!
new_instance.save()

0
投票

在 yuvi 的回答中,手动分配 modelbase_ptr 并保存失败,因为在保存之前删除了 instance.modelbase_ptr 。

以 yuvi 的答案为基础,这里有一个更明确的示例,并且一般适用于以下抽象和非抽象转换:

  • 模型库 -> 模型子级
  • 模型子级 -> 模型库
  • 模型子级 -> 模型子级

可以选择保留原始 ID,这遵循 django 文档推荐的方法。

ex_model = ModelA
new_model = ModelB

ex_instance = ex_model.objects.get(pk=1) #arbitrary

# find fields required for new_model:
new_fields = [f.name for f in new_model._meta.fields]

# make new dict of existing field : value
new_fields_dict = dict( [(x, getattr(ex_instance, x, None)) for x in new_fields] )

# Save temp copy as new_model with new id
# modelbase_ptr will be created automatically as required
new_fields_dict.pop('project_ptr', None)
temp_instance = new_model(**new_fields_dict) 
temp_instance.pk = None
temp_instance.id = None
temp_instance.save()
# you must set all your related fields here
temp_instance.copy_related(ex_instance)

ex_instance.delete() 

# (optional) Save final copy as new_model with original id
final_instance = new_model(**new_fields_dict)
final_instance.save()
final_instance.copy_related(temp_instance)
temp_instance.delete()

# here are the removed fields, handle as required
removed_fields = [f.name for f in ex_model._meta.fields if f.name not in new_fields_dict.keys()]
removed_fields_dict = dict( [(x, getattr(ex_instance, x, None)) for x in removed_fields] )

在类模型库中:

def copy_related(self, from):
    # include all your related fields here
    self.related_field = from.related_field.all()
    self.related_field_a = from.related_field_a.all()

0
投票

也许自从提出这个问题以来,Django 模型 API 已经发生了变化,但这是我轻松处理它的方法,其解决方案的优点是在转换过程中保持父数据库条目不变,确保 M2M 或 O2M 中的所有相关对象被保留:

考虑以下型号:

class Parent(Model):
    pass

class Child(Parent):
   child_field = models.CharField()


class Child2(Parent):
   child2_field = models.CharField()

在此配置中,Django 创建了 3 个表

parent
child
child2
,其中
parent_ptr_id
OneToOne
child
parent
parent_ptr_id
OneToOne
child2
 parent

我们可以将其表示为:

class Parent(Model):
    pass

class Child(Model):
   parent_ptr = models.OneToOneField(Parent)
   child_field = models.CharField()

class Child2(Model):
   parent_ptr = models.OneToOneField(Parent)
   child2_field = models.CharField()

如果我们考虑要将

Child
模型实例转换为
Child2
模型实例,最好的选择是:

  • 保持
    parent
    表中的记录不变
  • child
    表中删除记录(child_field值显然会丢失)
  • child2
    表中创建一条新记录,引用
    parent
    表中的记录

这可以通过以下步骤完成:

object_id = 123 # Child object id
child_object = Child.objects.get(id=object_id)

# Delete the record in the child table, keeping the record in the parent table :
child_object.delete(keep_parents=True)

# Create the record in the child2 table :
child2_object = Child2(parent_ptr_id=object_id))
child2_object.save_base(raw=True)
© www.soinside.com 2019 - 2024. All rights reserved.