Python 如何将计算字段添加到 Django 模型
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17682567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to add a calculated field to a Django model
提问by cstrutton
I have a simple Employee
model that includes firstname
, lastname
and middlename
fields.
我有一个简单的Employee
模型,其中包括firstname
, lastname
和middlename
字段。
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.
最终我认为我需要的是将名称字段的值作为字符串获取。我得到的错误是value_to_string() takes exactly 2 arguments (1 given)
。字符串想要的值self, obj
。我不确定是什么obj
意思。
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'.
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)
采纳答案by cstrutton
Ok... Daniel Roseman's answer seemed like it should have worked. As is always the case, you find what your looking for after you post the question.
好的......丹尼尔罗斯曼的回答似乎应该有效。与往常一样,您会在发布问题后找到您要找的内容。
From the django 1.5 docs I found this examplethat worked right out of the box. Thanks to all for your help.
从django 1.5 文档中,我发现这个例子开箱即用。感谢大家的帮助。
Here is the code that worked:
这是有效的代码:
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)
回答by Daniel Roseman
That's not something you do as a field. Even if that syntax worked, it would only give the value when the class was defined, not at the time you access it. You should do this as a method, and you can use the @property
decorator to make it look like a normal attribute.
这不是你作为一个领域所做的事情。即使该语法有效,它也只会在定义类时给出值,而不是在您访问它时。你应该把它作为一个方法来做,你可以使用@property
装饰器让它看起来像一个普通的属性。
@property
def name(self):
return ''.join(
[self.lastname,' ,', self.firstname, ' ', self.middlename])
self.lastname
etc appear as just their values, so no need to call any other method to convert them.
self.lastname
etc 仅显示为它们的值,因此无需调用任何其他方法来转换它们。
回答by Alex Petralia
Daniel Roseman's solution makes a calculated field an attribute of a Model
, however it does not make it accessible via QuerySet methods(eg. .all()
, .values()
). This is because QuerySet methods call the database directly, circumventing the django Model
.
Daniel Roseman 的解决方案使计算字段成为 a 的属性Model
,但是它不能通过 QuerySet 方法(例如 . all()
, .values()
)访问它。这是因为 QuerySet 方法直接调用数据库,绕过了 django Model
。
Since QuerySets access the database directly, the solution is to override the Manager
's .get_queryset()
method by appending your calculated field. The calculated field is created using .annotate()
. Finally, you set the objects
Manager in your Model
to your new Manager
.
由于 QuerySets 直接访问数据库,因此解决方案是通过附加您的计算字段来覆盖Manager
的.get_queryset()
方法。计算字段是使用.annotate()
. 最后,您将objects
Manager设置Model
为您的新Manager
.
Here is some code demonstrating this:
下面是一些代码来证明这一点:
models.py
模型.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()
Now, you will be able to call .values()
or .all()
and access the newly calculated link
attribute as declared in the Manager
.
现在,您将能够调用.values()
或.all()
访问link
在Manager
.
It would have also been possible to use other functionsin .annotate()
, such as F()
.
这本来也一直可以使用其他的功能的.annotate()
,如F()
。
I believe the attribute would still not be available in object._meta.get_fields()
. I believe you can add it here, but I haven't explored how - any edits/comments would be helpful.
我相信该属性在object._meta.get_fields()
. 我相信你可以在这里添加它,但我还没有探索如何 - 任何编辑/评论都会有所帮助。
回答by Farzad Vertigo
In this case if you are only going to use the field for representation in admin site and such issues, you might better to consider overriding str() or unicode() method of the class as it is mentioned in django documentation here:
在这种情况下,如果您只想使用该字段在管理站点中进行表示和此类问题,您最好考虑覆盖类的str() 或unicode() 方法,因为它在此处的django 文档中提到:
class Employee(models.Model):
# fields definitions
def __str__(self):
return self.lastname + ' ,' + self.firstname + ' ' + self.middlename
回答by brechin
I recently worked on a library that may solve the problem you're having quite easily.
我最近在一个可以很容易地解决您遇到的问题的库上工作。
https://github.com/brechin/django-computed-property
https://github.com/brechin/django-computed-property
Install that, add to INSTALLED_APPS and then
安装它,添加到 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')