将Django字段描述从现有模型复制到新模型 [英] Copying a Django Field description from an existing Model to a new one

查看:84
本文介绍了将Django字段描述从现有模型复制到新模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据现有模型的字段动态生成一个新的模型。两者都在 /apps/main/models.py 中定义。现有的模型看起来像这样:

I'm trying to dynamically generate a new Model, based on fields from an existing Model. Both are defined in /apps/main/models.py. The existing model looks something like this:

from django.db import models

class People(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    height = models.IntegerField()

我有一个包含我要复制的字段名称的列表:

I have a list containing the names of fields that I would like to copy:

target_fields = ["name", "age"]

想要生成一个新模型,它具有在 target_fields 中命名的所有字段,但在这种情况下,它们应该被编入索引( db_index = True )。

I want to generate a new model the has all of the Fields named in target_fields, but in this case they should be indexed (db_index = True).

我最初希望能够遍历人的类属性并使用 copy.copy 复制在其上定义的字段说明。像这样:

I originally hoped that I would just be able to iterate over the class properties of People and use copy.copy to copy the field descriptions that are defined on it. Like this:

from copy import copy

d = {}
for field_name in target_fields:
    old_field = getattr(People, field_name) # alas, AttributeError
    new_field = copy(old_field)
    new_field.db_index = True
    d[field_name] = new_field

IndexedPeople = type("IndexedPeople", (models.Model,), d)

I不确定是否 copy.copy() ing Fields可以正常工作,但是我没有足够的发现:类定义中列出的字段不实际上并不包括在类对象的属性中。我假设它们被用于某些元类shenanigans。

I wasn't sure if copy.copy()ing Fields would work, but I didn't get far enough to find out: the fields listed in the class definition don't aren't actually included as properties on the class object. I assume they're used for some metaclass shenanigans instead.

在调试器中戳了一下之后,我发现了一些类型的中列出的Field对象._meta.local_fields 。然而,这些不仅仅是简单的描述,可以是 copy.copy() ed,用于描述另一个模型。例如,它们包括一个 .model 属性,引用 People

After poking around in the debugger, I found some type of Field objects listed in People._meta.local_fields. However, these aren't just simple description that can be copy.copy()ed and used to describe another model. For example, they include a .model property referring to People.

如何根据现有模型的字段为新模型创建字段说明?

How can I create a field description for a new model based on a field of an existing model?

推荐答案

从在调试器和源代码中戳:所有Django模型都使用 ModelBase 元类1.4.1 / django的/分贝/模型/ base.py> /db/models/base.py 。对于模型类定义中的每个字段, ModelBase .add_to_class 方法将调用字段的 .contribute_to_class 方法。

From poking around in the debugger and the source: all Django models use the ModelBase metaclass defined in /db/models/base.py. For each field in a model's class definition, ModelBase's .add_to_class method will call the field's .contribute_to_class method.

Field.contribute_to_class /db/models/fields/__init__.py ,它是负责将字段定义与特定模型相关联的。通过添加 .model 属性并通过使用模型中使用的名称调用 .set_attributes_from_name 方法来修改该字段类定义。这反过来又添加了 .attname .column 属性并设置 .name .verbose_name 如有必要

Field.contribute_to_class is defined in /db/models/fields/__init__.py and it is what's responsible for associating a field definition with a particular model. The field is modified by adding the .model property and by calling the .set_attributes_from_name method with the name used in the model's class definition. This in turn adds adds the .attname and .column properties and sets .name and .verbose_name if necessary.

当我检查 __ dict__ 新定义的 CharField 的属性,并将其与 CharField 的属性进行比较已经与模型相关联,我也看到这些是唯一的区别:

When I inspect the __dict__ property of a newly-defined CharField and compare it with that of a CharField that was already associated with a model, I also see that these are the only differences:


  • .creation_counter 属性对于每个实例都是唯一的。

  • .attrname .column .model 新实例上不存在属性。

  • .name .verbose_name 属性是新实例上的

  • The .creation_counter property is unique for each instance.
  • The .attrname, .column and .model properties do not exist on the new instance.
  • The .name and .verbose_name properties is None on the new instance.

似乎无法区分 .name / .verbose_name 为构造函数手动指定的属性和自动生成的属性。您需要选择始终重置它们,忽略任何手动指定的值,或永远不要清除它们,这将导致它们始终忽略在新模型中给出的任何新名称。我想使用与原始字段相同的名称,所以我不会碰它们。

It doesn't seem possible to distinguish between .name/.verbose_name properties that were manually specified to the constructor and ones that were automatically generated. You'll need to chose either to always reset them, ignoring any manually-specified values, or never clear them, which would cause them to always ignore any new name they were given in the new model. I want to use the same name as the original fields, so I am not going to touch them.

知道有什么区别,我使用 copy.copy()来克隆现有的实例,然后应用这些更改使其表现得像一个新的实例。

Knowing what differences exist, I am using copy.copy() to clone the existing instance, then apply these changes to make it behave like a new instance.

import copy
from django.db import models

def copy_field(f):
    fp = copy.copy(f)

    fp.creation_counter = models.Field.creation_counter
    models.Field.creation_counter += 1

    if hasattr(f, "model"):
        del fp.attname
        del fp.column
        del fp.model

        # you may set .name and .verbose_name to None here

    return fp

鉴于此功能,我创建了以下新的模型:

Given this function, I create the new Model with the following:

target_field_name = "name"

target_field = People._meta.get_field_by_name(target_field_name)[0]
model_fields = {}

model_fields["value"] = copy_field(target_field)
model_fields["value"].db_index = True
model_fields["__module__"] = People.__module__

NewModel = type("People_index_" + field_name, (models.Model,), model_fields)

它的工作原理

这篇关于将Django字段描述从现有模型复制到新模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆