5

既存のモデルのフィールドに基づいて、新しいモデルを動的に生成しようとしています。どちらも で定義されてい/apps/main/models.pyます。既存のモデルは次のようになります。

from django.db import models

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

コピーしたいフィールドの名前を含むリストがあります。

target_fields = ["name", "age"]

で名前が付けられたすべてのフィールドを持つ新しいモデルを生成したいのですtarget_fieldsが、この場合はインデックスを作成する必要があります ( db_index = True)。

私は当初、 のクラス プロパティを反復処理し、定義されているフィールドの説明をコピーするためにPeople使用できることを望んでいました。copy.copyこのような:

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)

ing Fields が機能するかどうかはcopy.copy()わかりませんでしたが、調べるには十分ではありませんでした: クラス定義にリストされているフィールドは、実際にはクラス オブジェクトのプロパティとして含まれていません。代わりに、メタクラスの悪ふざけに使用されていると思います。

デバッガーを調べてみたところ、 にリストされているタイプの Field オブジェクトが見つかりましたPeople._meta.local_fieldscopy.copy()ただし、これらは、編集して別のモデルを説明するために使用できる単純な説明ではありません。たとえば、 を.model参照するプロパティが含まれPeopleます。

既存のモデルのフィールドに基づいて新しいモデルのフィールド記述を作成するにはどうすればよいですか?

4

2 に答える 2

6

デバッガーとソースを調べてみると、すべての Django モデルは でModelBase定義されているメタクラスを使用しています/db/models/base.py。モデルのクラス定義の各フィールドに対して、ModelBase.add_to_classメソッドはフィールドのメソッドを呼び出します.contribute_to_class

Field.contribute_to_classで定義されて/db/models/fields/__init__.pyおり、フィールド定義を特定のモデルに関連付ける責任があります。フィールドは、プロパティを追加し、モデルのクラス定義で使用されている名前でメソッドを.model呼び出すことによって変更されます。これにより、プロパティとセット.set_attributes_from_nameが追加され、必要に応じて、.attnameおよびが追加されます。.column.name.verbose_name

__dict__新しく定義された のプロパティを調べて、既にモデルに関連付けられているのプロパティCharFieldと比較すると、これらが唯一の違いであることがわかります。CharField

  • プロパティは.creation_counterインスタンスごとに一意です。
  • 、およびプロパティは.attrname、新しいインスタンスには存在しません。.column.model
  • およびプロパティは新しいインスタンスにあります.name.verbose_nameNone

.name/.verbose_nameコンストラクターに手動で指定されたプロパティと、自動的に生成されたプロパティを区別することはできないようです。手動で指定した値を無視して常にリセットするか、決してクリアしないかを選択する必要があります。これにより、新しいモデルで指定された新しい名前が常に無視されます。元のフィールドと同じ名前を使用したいので、それらには触れません。

どのような違いがあるかを知っているのでcopy.copy()、既存のインスタンスのクローンを作成し、これらの変更を適用して新しいインスタンスのように動作させます。

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

この関数を使用して、次の新しいモデルを作成します。

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)

できます!

于 2012-09-01T17:03:31.423 に答える