Как добавить вычисляемое поле в модель Django
У меня есть простая 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)
Ответы
Ответ 1
Хорошо... Даниэль Роземан ответил, что это должно сработать. Как всегда, вы найдете то, что ищете, после того как вы разместите вопрос.
Из django 1.5 docs я нашел этот пример, который работал прямо из коробки. Спасибо всем за вашу помощь.
Вот код, который работал:
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 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)
Ответ 2
Это не то, что вы делаете как поле. Даже если этот синтаксис работал, он будет давать значение только тогда, когда класс был определен, а не в момент его доступа. Вы должны сделать это как метод, и вы можете использовать декоратор @property
, чтобы он выглядел как обычный атрибут.
@property
def name(self):
return ''.join(
[self.lastname,' ,', self.firstname, ' ', self.middlename])
self.lastname
и т.д. отображаются как их значения, поэтому не нужно вызывать какой-либо другой метод для их преобразования.
Ответ 3
Решение Daniel Roseman делает вычисленное поле атрибутом Model
, однако не делает его доступным с помощью методов QuerySet (например, all()
, .values()
). Это связано с тем, что методы QuerySet напрямую ссылаются на базу данных, обходя django Model
.
Так как QuerySets напрямую обращаются к базе данных, решение должно переопределить метод Manager
.get_queryset()
, добавив ваше вычисленное поле. Вычисленное поле создается с помощью .annotate()
. Наконец, вы установите objects
Manager в свой 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()
Теперь вы можете вызвать .values()
или .all()
и получить доступ к недавно вычисленному атрибуту link
, как указано в Manager
.
Было бы также возможно использовать другие функции в .annotate()
, например F()
.
Я считаю, что атрибут все равно не будет доступен в object._meta.get_fields()
. Я считаю, вы можете добавить его здесь, но я не изучил, как - любые изменения/комментарии будут полезны.
Ответ 4
В этом случае, если вы только собираетесь использовать поле для представления на сайте администратора и такие проблемы, лучше рассмотреть переопределение str() или unicode ( ) метод класса, как упоминается в документации django здесь:
class Employee(models.Model):
# fields definitions
def __str__(self):
return self.lastname + ' ,' + self.firstname + ' ' + self.middlename
Ответ 5
Недавно я работал над библиотекой, которая может решить проблему, с которой вы легко справляетесь.
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')