如何将计算字段添加到Django模型

cst*_*ton 46 python django-models django-admin

我有一个简单的Employee模型,其中包括firstname, lastnamemiddlename领域.

在管理员方面,可能在其他地方,我想将其显示为:

lastname, firstname middlename
Run Code Online (Sandbox Code Playgroud)

对我来说,这样做的合理位置是在模型中通过创建一个计算字段:

from django.db import models
from django.contrib import admin

class Employee(models.Model):
    lastname = models.CharField("Last", max_length=64)
    firstname = models.CharField("First", max_length=64)
    middlename = models.CharField("Middle", max_length=64)
    clocknumber = models.CharField(max_length=16)
    name = ''.join(
        [lastname.value_to_string(),
        ',',
         firstname.value_to_string(),
        ' ',
         middlename.value_to_string()])

    class Meta:
        ordering = ['lastname','firstname', 'middlename']

class EmployeeAdmin(admin.ModelAdmin):
    list_display = ('clocknumber','name')
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}),
        ]

admin.site.register(Employee, EmployeeAdmin)
Run Code Online (Sandbox Code Playgroud)

最终我认为我需要的是将名称字段的值作为字符串.我得到的错误是value_to_string() takes exactly 2 arguments (1 given).字符串想要的值self, obj.我不确定是什么obj意思.

必须有一个简单的方法来做到这一点,我相信我不是第一个想要这样做的人.

编辑:这是我的代码修改为丹尼尔的答案.我得到的错误是:

django.core.exceptions.ImproperlyConfigured: 
    EmployeeAdmin.list_display[1], 'name' is not a callable or an 
    attribute of 'EmployeeAdmin' of found in the model 'Employee'.


from django.db import models
from django.contrib import admin

class Employee(models.Model):
    lastname = models.CharField("Last", max_length=64)
    firstname = models.CharField("First", max_length=64)
    middlename = models.CharField("Middle", max_length=64)
    clocknumber = models.CharField(max_length=16)

    @property
    def name(self):
        return ''.join(
            [self.lastname,' ,', self.firstname, ' ', self.middlename])

    class Meta:
        ordering = ['lastname','firstname', 'middlename']

class EmployeeAdmin(admin.ModelAdmin):
    list_display = ('clocknumber','name')
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}),
]

admin.site.register(Employee, EmployeeAdmin)
Run Code Online (Sandbox Code Playgroud)

Dan*_*man 68

这不是你作为一个领域所做的事情.即使该语法有效,它也只会在定义类时给出值,而不是在您访问它时.您应该将此作为一种方法,并且您可以使用@property装饰器使其看起来像普通属性.

@property
def name(self):
    return ''.join(
        [self.lastname,' ,', self.firstname, ' ', self.middlename])
Run Code Online (Sandbox Code Playgroud)

self.lastname etc只显示为它们的值,因此无需调用任何其他方法来转换它们.

  • 这不允许您对 QuerySet 进行 filter(),因为要这样做,它必须是 Field 而不是属性。对我有用的是对一个表达式(使用 QuerySet.annotate() )进行注释,该表达式告诉数据库如何派生该值(例如,models.F(field1) - models.F(field2),然后对该注释进行过滤. (3认同)

cst*_*ton 39

好的......丹尼尔罗斯曼的回答似乎应该有效.总是这样,您在发布问题后找到了您要查找的内容.

django 1.5文档我发现这个开箱即用的例子.感谢大家的帮助.

这是有效的代码:

from django.db import models
from django.contrib import admin

class Employee(models.Model):
    lastname = models.CharField("Last", max_length=64)
    firstname = models.CharField("First", max_length=64)
    middlename = models.CharField("Middle", max_length=64)
    clocknumber = models.CharField(max_length=16)

    def _get_full_name(self):
        "Returns the person's full name."
        return '%s, %s %s' % (self.lastname, self.firstname, self.middlename)
    full_name = property(_get_full_name)


    class Meta:
        ordering = ['lastname','firstname', 'middlename']

class EmployeeAdmin(admin.ModelAdmin):
    list_display = ('clocknumber','full_name')
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}),
]

admin.site.register(Employee, EmployeeAdmin)
Run Code Online (Sandbox Code Playgroud)

  • 这不正是 @property 装饰器所做的吗? (18认同)
  • 新版本的文档使用“@property”:https://docs.djangoproject.com/en/3.1/topics/db/models/#model-methods (2认同)

Ale*_*lia 24

丹尼尔·罗斯曼的解决方案,使计算字段的属性Model,但它并不能使它通过QuerySet的方法访问(例如.all(),.values()).这是因为QuerySet方法直接调用数据库,绕过了django Model.

由于查询集直接访问数据库,解决的办法是重写Manager.get_queryset()通过附加的计算字段的方法.使用创建计算字段.annotate().最后,您将objects管理器设置为Model新的Manager.

以下是一些证明这一点的代码:

models.py

from django.db.models.functions import Value, Concat
from django.db import Model

class InvoiceManager(models.Manager):
    """QuerySet manager for Invoice class to add non-database fields.

    A @property in the model cannot be used because QuerySets (eg. return
    value from .all()) are directly tied to the database Fields -
    this does not include @property attributes."""

    def get_queryset(self):
        """Overrides the models.Manager method"""
        qs = super(InvoiceManager, self).get_queryset().annotate(link=Concat(Value("<a href='#'>"), 'id', Value('</a>')))
        return qs

class Invoice(models.Model):
    # fields

    # Overridden objects manager
    objects = InvoiceManager()
Run Code Online (Sandbox Code Playgroud)

现在,您将能够调用.values().all()访问在... link中声明的新计算的属性Manager.

这本来也一直可以使用其他的功能.annotate(),如F().

我相信该属性仍然无法使用object._meta.get_fields().我相信你可以在这里添加它,但我还没有探究过 - 任何编辑/评论都会有所帮助.


小智 8

我最近在一个图书馆工作,可以很轻松地解决您遇到的问题。

https://github.com/brechin/django-computed-property

安装它,添加到INSTALLED_APPS,然后

class Employee(models.Model):
    ...
    name = computed_property.ComputedCharField(max_length=3 * 64, compute_from='full_name')

    @property
    def full_name(self):
        return '{LAST}, {FIRST} {MIDDLE}'.format(LAST=self.lastname, FIRST=self.firstname, MIDDLE=self.middlename')
Run Code Online (Sandbox Code Playgroud)