将Django字段描述从现有模型复制到新模型 [英] Copying a Django Field description from an existing Model to a new one
问题描述
我正在尝试根据现有模型的字段动态生成一个新的模型。两者都在 /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 isNone
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屋!