如何覆盖 django admin LogEntry 模型

Nic*_*ick 1 python django django-models django-admin

我想更改用于默认 LogEntry 类的模型,以便它在数据库中为属性“object_id”创建一个 varchar 而不是一个 clob

原始模型定义在

django/contrib/admin/models.py

class LogEntry(models.Model):
    action_time = models.DateTimeField(_('action time'), auto_now=True)
    user = models.ForeignKey(settings.AUTH_USER_MODEL)
    content_type = models.ForeignKey(ContentType, blank=True, null=True)
    object_id = models.TextField(_('object id'), blank=True, null=True)
    object_repr = models.CharField(_('object repr'), max_length=200)
    action_flag = models.PositiveSmallIntegerField(_('action flag'))
    change_message = models.TextField(_('change message'), blank=True)
Run Code Online (Sandbox Code Playgroud)

我想将 object_id 的定义更改为

object_id = models.Charfield(_('object id'), max_length=1000,  blank=True, null=True)
Run Code Online (Sandbox Code Playgroud)

我知道,如果有人在大于 varchar(1000) 的实体上定义主键,这可能会导致问题,但我不希望实体的 PK 定义为这样,因此我对限制感到满意.

这将大大提高访问历史日志时的查询效率。

我真的不想破解实际的模型定义,但我无法找到如何优雅地覆盖模型定义。

有任何想法吗?

小智 5

Django 的模型字段提供了一种未公开的contribute_to_class方法。我们可以使用的 Django 的另一个特性是class_prepared信号。

from django.db.models import CharField
from django.db.models.signals import class_prepared

def add_field(sender, **kwargs):
    """
    class_prepared signal handler that checks for the model named
    MyModel as the sender, and adds a CharField
    to it.
    """
    if sender.__name__ == "MyModel":
        field = CharField("New field", max_length=100)
        field.contribute_to_class(sender, "new_field")

class_prepared.connect(add_field)
Run Code Online (Sandbox Code Playgroud)

要覆盖字段,您可以简单地从模型中删除原始字段:

from django.db.models import CharField
from django.db.models.signals import class_prepared

def override_field(sender, **kwargs):
    if sender.__name__ == "LogEntry":
        field = CharField('object id', max_length=1000, blank=True, null=True)
        sender._meta.local_fields = [f for f in sender._meta.fields if f.name != "object_id"]
        field.contribute_to_class(sender, "object_id")

class_prepared.connect(override_field)
Run Code Online (Sandbox Code Playgroud)

我刚刚通过将此代码放入__init__.py我的应用程序中来测试此解决方案。您还需要编写自定义迁移:

from django.db import migrations, models

class Migration(migrations.Migration):
    def __init__(self, name, app_label):
        # overriding application operated upon
        super(Migration, self).__init__(name, 'admin')

    dependencies = [
        ('my_app', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='logentry',
            name='object_id',
            field=models.CharField('object id', max_length=1000, blank=True, null=True),
        ),
    ]
Run Code Online (Sandbox Code Playgroud)

看起来它有效,但使用它需要您自担风险。

您可以在此处阅读更多内容。