Django“ValueError:无法批量创建多表继承模型”

fla*_*son 5 django django-models django-model-utils

问题

我正在使用 django-model-utils InheritanceManager。我有一个超级通知,我用它来创建多个通知子类,如(models.Model)类PostNotification(Notification)CommentNotification(Notification)等,并试图运行时CommentNotification.objects.bulk_create(list_of_comment_notification_objects),我得到以下回溯:

File "/home/me/.virtualenvs/project/local/lib/python2.7/site-packages/django/db/models/query.py", line 429, in bulk_create
    raise ValueError("Can't bulk create a multi-table inherited model")
ValueError: Can't bulk create a multi-table inherited model
Run Code Online (Sandbox Code Playgroud)

在检查 query.py 文件时,我们发现这会导致错误:

for parent in self.model._meta.get_parent_list():
      if parent._meta.concrete_model is not self.model._meta.concrete_model:
           raise ValueError("Can't bulk create a multi-table inherited model")
Run Code Online (Sandbox Code Playgroud)

环境 Django Model Utils 版本:3.1.1 Django 版本:1.11.7 Python 版本:2.7.3

例子

PostNotification.objects.bulk_create(
   [PostNotification(related_user=user, post=instance) for user in users]
)
Run Code Online (Sandbox Code Playgroud)

抛出上述异常

我已经尝试过但最初是成功的:

我虽然只是运行: BaseClass.objects.bulk_create(list_of_SubClass_objects)而不是SubClass.objects.bulk_create(list_of_SubClass_objects)会工作并返回子类值的列表,但随后运行SubClass.objects.all()会返回一个空结果。bulk_create() 只会为列表中的每个项目创建一个 Notification 基类对象。

saj*_*jid 2

找到了一个 hacky 解决方案。我希望它适用于你的情况。诀窍是动态创建一个具有某些元(db_table)集的模型(不是继承的模型)。并使用这个动态模型批量创建Child对象(换句话说,写入Child的DB表)。

    class Parent(models.Model):
        name = models.CharField(max_length=10)


    class Child(Parent):
        phone = models.CharField(max_length=12)
Run Code Online (Sandbox Code Playgroud)
# just an example. Should be expanded to work properly.
field_type_mapping = {
    'OneToOneField': models.IntegerField,
    'CharField': models.CharField,
}

def create_model(Model, app_label='children', module='', options=None):
    """
    Create specified model
    """
    model_name = Model.__name__
    class Meta:
       managed = False
       db_table = Model._meta.db_table

    if app_label:
        # app_label must be set using the Meta inner class
        setattr(Meta, 'app_label', app_label)

    # Update Meta with any options that were provided
    if options is not None:
        for key, value in options.iteritems():
            setattr(Meta, key, value)

    # Set up a dictionary to simulate declarations within a class
    attrs = {'__module__': module, 'Meta': Meta}

    # Add in any fields that were provided
    fields = dict()
    for field in Model._meta.fields:
        if field.attname == 'id':
            continue
        if field.model.__name__ == model_name:
            field_class_name = type(field).__name__
            print(field.attname)
            fields[field.attname] = field_type_mapping[field_class_name]()
    # Create the class, which automatically triggers ModelBase processing
    attrs.update(fields)

    model = type(f'{model_name}Shadow', (models.Model,), attrs)


    return model

mod = create_model(Child)
parents = [Parent(name=i) for i in range(15)]
parents = Parent.objects.bulk_create(parents)
children = [mod(phone=parent.name, parent_ptr_id=parent.id) for parent in parents]
mod.objects.bulk_create(children)
Run Code Online (Sandbox Code Playgroud)