如何向 Django 模型添加计算字段

问题描述 投票:0回答:6

我有一个简单的

Employee
模型,其中包括
firstname
lastname
middlename
字段。

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

lastname, firstname middlename

对我来说,执行此操作的逻辑位置是在模型中创建计算字段,如下所示:

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)

最终我认为我需要的是获取名称字段的值作为字符串。我收到的错误是

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)
python django-models django-admin
6个回答
101
投票

这不是你作为一个领域所做的事情。即使该语法有效,它也只会在定义类时给出值,而不是在访问它时给出值。您应该将其作为一种方法来执行,并且可以使用

@property
装饰器使其看起来像普通属性。

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

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


69
投票

Daniel Roseman 的解决方案使计算字段成为

Model
的属性,但是它 无法通过 QuerySet 方法 访问它(例如. .
all()
.values()
)。这是因为 QuerySet 方法直接调用数据库,绕过了 django
Model

由于 QuerySet 直接访问数据库,解决方案是通过附加计算字段来覆盖

Manager
.get_queryset()
方法。计算字段是使用
.annotate()
创建的。最后,您将
objects
中的
Model
管理器设置为新的
Manager

这里有一些代码演示了这一点:

模型.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()

现在,您将能够调用

.values()
.all()
并访问新计算的
link
属性,如
Manager
中声明的那样。

也可以在 .annotate() 中使用

其他功能
,例如
F()

我相信该属性在

object._meta.get_fields()
中仍然不可用。我相信您可以在此处添加它,但我还没有探索如何添加 - 任何编辑/评论都会有帮助。


56
投票

好吧...丹尼尔·罗斯曼的回答似乎应该有效。与往常一样,您会在发布问题后找到所需内容。

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)

16
投票

我最近开发了一个库,可以很轻松地解决您遇到的问题。

https://github.com/brechin/django-compulated-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')

3
投票

在这种情况下,如果您只想使用该字段在管理站点中表示和此类问题,您最好考虑重写该类的 str() 或 unicode() 方法,如 django 文档中提到的那样这里

class Employee(models.Model):
    # fields definitions
    def __str__(self):
        return self.lastname + ' ,' + self.firstname + ' ' + self.middlename

0
投票

从 Django 5.0 开始,现在可以使用新的 GeneratedField

对于这个具体问题/情况,如下所示:

from django.db.models import F

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)
    full_name= models.GeneratedField(
        expression=(F('lastname') + ', ' + F('firstname') + ' ' + F('middlename')),
        output_field=models.FloatField,
        db_persist=True)

    class Meta:
        ordering = ['name']

从这里开始,对于几乎所有意图和目的,

full_name
可以像任何其他模型字段一样使用(即排序、管理等)。

© www.soinside.com 2019 - 2024. All rights reserved.