FROM overcoder
От толкания в отладчике и источнике: все модели 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_name - это None в новом экземпляре.
Невозможно различать свойства .name/.verbose_name, которые были вручную указаны для конструктора, и те, которые были автоматически сгенерированы. Вам нужно выбрать либо всегда reset их, игнорируя любые заданные вручную значения, либо никогда не очищать их, что приведет к тому, что они всегда будут игнорировать любое новое имя, которое они получили в новой модели. Я хочу использовать то же имя, что и исходные поля, поэтому я не буду трогать их.
Зная, какие различия существуют, я использую 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)
Это работает!