Skip to content

Instantly share code, notes, and snippets.

@Barolina
Last active July 7, 2020 06:06
Show Gist options
  • Save Barolina/1cab69d1b9739501f1fe892382ec22f2 to your computer and use it in GitHub Desktop.
Save Barolina/1cab69d1b9739501f1fe892382ec22f2 to your computer and use it in GitHub Desktop.

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)

Это работает!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment