Подтвердить что ты не робот

Как добавить вычисляемое поле в модель 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)
4b9b3361

Ответ 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')