77

I have a simple Employee model that includes firstname, lastname and middlename fields.

On the admin side and likely elsewhere, I would like to display that as:

lastname, firstname middlename

To me the logical place to do this is in the model by creating a calculated field as such:

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)

Ultimately what I think I need is to get the value of the name fields as strings. The error I am getting is value_to_string() takes exactly 2 arguments (1 given). Value to string wants self, obj. I am not sure what obj means.

There must be an easy way to do this, I am sure I am not the first to want to do this.

Edit: Below is my code modified to Daniel's answer. The error I get is:

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)
4

5 回答 5

94

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

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

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

于 2013-07-16T17:01:12.513 回答
59

Daniel Roseman 的解决方案使计算字段成为 a 的属性Model,但是它不能通过 QuerySet 方法(例如 . all().values())访问它。这是因为 QuerySet 方法直接调用数据库,绕过了 django Model

由于 QuerySets 直接访问数据库,因此解决方案是通过附加计算字段来覆盖Manager'方法。.get_queryset()计算字段是使用 创建的.annotate()。最后,您将objectsManager设置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(). 我相信您可以在此处添加它,但我还没有探索如何 - 任何编辑/评论都会有所帮助。

于 2017-02-27T17:14:20.990 回答
52

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

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)
于 2013-07-16T18:07:37.827 回答
14

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

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')
于 2018-02-12T15:38:44.967 回答
3

在这种情况下,如果您只打算在管理站点和此类问题中使用该字段表示,您最好考虑覆盖类的str () 或unicode () 方法,因为它在此处的 django 文档中提到:

class Employee(models.Model):
    # fields definitions
    def __str__(self):
        return self.lastname + ' ,' + self.firstname + ' ' + self.middlename
于 2017-05-03T20:29:37.757 回答