英文:
How to include custom model method in Django admin custom filter?
问题
我试图做什么:
我试图在Django管理员中使用SimpleFilter
创建自定义过滤器,以便在list_filter
中使用。
我尝试过的:
下面是我在admin.py
文件中编写的代码。
我使用了SimpleFilter
来创建名为RoomScoreFilter
的自定义过滤器。
RoomScoreFilter
会将平均分数在1.00
到1.99
之间的项目筛选为非常差
等等。
class RoomScoreFilter(admin.SimpleListFilter):
title = _("房间评分")
parameter_name = "score"
def lookups(self, request, model_admin):
return [
("1", _("非常差")),
("2", _("差")),
("3", _("一般")),
("4", _("好")),
("5", _("优秀")),
]
def queryset(self, request, queryset):
if self.value() == "1":
return queryset.filter(get_average_rating__gte=1, get_average_rating__lt=2)
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
# ...(以下为其余代码)
以下是我的模型。
class Review(CommonDateTimeModel):
# ...(以下为其余代码)
所以get_average_rating()
方法的基本功能是将我列举的所有6个字段(例如cleanliness
、check_in
等)相加,然后除以6并保留两位小数。
我遇到的错误:
然而,它输出了一个错误,这可能是我预料到的:
无法将关键字'get_average_rating'解析为字段。可选项是:accuracy、check_in、cleanliness、comment、communication、created_at、customer、customer_id、id、location、room、room_id、updated_at、value
是否有解决此问题的方法?
英文:
What I am trying to do:
I am trying to make custom filter in Django admin using SimpleFilter
for using in list_filter
.
What I tried:
Below is the code that I wrote down in admin.py
file.
I used SimpleFilter
for creating custom filter called RoomScoreFilter
.
RoomScoreFilter
filters that if average score is 1.00
to 1.99
, it will filter as Very poor
and so forth.
class RoomScoreFilter(admin.SimpleListFilter):
title = _("Room Score")
parameter_name = "score"
def lookups(self, request, model_admin):
return [
("1", _("Very poor")),
("2", _("Poor")),
("3", _("Normal")),
("4", _("Good")),
("5", _("Excellent")),
]
def queryset(self, request, queryset):
if self.value() == "1":
return queryset.filter(get_average_rating__gte=1, get_average_rating__lt=2)
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
empty_value_display = "-----"
fieldsets = [
("Room & Customer", {"fields": ["room", "customer"]}),
(
"Evaluation",
{"fields": ["get_average_rating", "comment"], "classes": "wide"},
),
(
"Individual Scores",
{
"fields": [
"cleanliness",
"accuracy",
"location",
"communication",
"check_in",
"value",
]
},
),
]
list_display = (
"room",
"customer",
"cleanliness",
"accuracy",
"location",
"communication",
"check_in",
"value",
"get_average_rating",
"comment",
)
list_display_links = ("room",)
list_per_page = 20
list_filter = [RoomScoreFilter]
search_fields = ("room", "user")
search_help_text = _("Searchable by room name and user ID.")
readonly_fields = ("room", "customer", "comment", "get_average_rating")
and below is my model.
class Review(CommonDateTimeModel):
"""Review model Definition"""
cleanliness = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Cleanliness"),
help_text=_("How clean a room was."),
)
accuracy = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Accuracy"),
help_text=_("How much did host provide accurate information about room."),
)
location = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Location"),
help_text=_("Was location good or fair enough to reach out?"),
)
communication = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Communication"),
help_text=_("How well did room host communicate with customers?"),
)
check_in = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Check In"),
help_text=_("How easy was it for checking in?"),
)
value = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(5)],
verbose_name=_("Value"),
help_text=_("Was it valuable enough compared to the price per night?"),
)
room = models.ForeignKey(
"rooms.Room",
on_delete=models.DO_NOTHING,
related_name="reviews",
verbose_name=_("Room"),
)
customer = models.ForeignKey(
"users.User",
on_delete=models.DO_NOTHING,
related_name="reviews",
verbose_name=_("Customer"),
)
comment = models.TextField(verbose_name=_("Comment"), null=True, blank=True)
def get_average_rating(self):
total_scores = (
self.cleanliness
+ self.accuracy
+ self.location
+ self.communication
+ self.check_in
+ self.value
)
average_score = round(total_scores / 6, 2)
return average_score
get_average_rating.short_description = _("Total Rating")
def __str__(self):
return str(f"{self.customer}'s review on {self.room}")
so basically what get_average_rating()
method does is simply add up all 6 fields that I wrote down (cleanliness
, check_in
, etc) and divide by 6 with rounding up to 2 digits.
The error that I got:
However, it spits the error which I possibly expected though:
Cannot resolve keyword 'get_average_rating' into field. Choices are: accuracy, check_in, cleanliness, comment, communication, created_at, customer, customer_id, id, location, room, room_id, updated_at, value
Are there any solutions for solving this problem?
答案1
得分: 3
你不能在查询集中使用属性或方法:查询集会转换成SQL查询,数据库不知道这些属性或方法,它只"理解"列。
你可以将表达式转换为SQL表达式:
from django.db.models import F
# ...
def queryset(self, request, queryset):
queryset = queryset.alias(
avg_rating=(
F('cleanliness')
+ F('accuracy')
+ F('location')
+ F('communication')
+ F('check_in')
+ F('value')
)
/ 6
)
if self.value() == '1':
return queryset.filter(avg_rating__gte=1, avg_rating__lt=2)
你也可以避免除法运算,它只会浪费计算资源并可能导致舍入误差:
from django.db.models import F
# ...
def queryset(self, request, queryset):
queryset = queryset.alias(
sum_rating=(
F('cleanliness')
+ F('accuracy')
+ F('location')
+ F('communication')
+ F('check_in')
+ F('value')
)
)
if self.value() == '1':
return queryset.filter(sum_rating__gte=6, sum_rating__lt=12)
英文:
You can not use properties or methods in a queryset: querysets are converted into an SQL query, and the database does not know anything about these properties or methods, it just "understands" columns.
You can however convert the expression to an SQL expression with:
<pre><code>from django.db.models import F
…
def queryset(self, request, queryset):
queryset = queryset.alias(
<b>avg_rating=</b>(
F('cleanliness')
+ F('accuracy')
+ F('location')
+ F('communication')
+ F('check_in')
+ F('value')
)
/ 6
)
if self.value() == '1':
return queryset.filter(<b>avg_rating__gte=1, avg_rating__lt=2</b>)</code></pre>
You can also avoid the division, which only wastes computational effort and can result in rounding errors:
<pre><code>from django.db.models import F
…
def queryset(self, request, queryset):
queryset = queryset.alias(
sum_rating=(
F('cleanliness')
+ F('accuracy')
+ F('location')
+ F('communication')
+ F('check_in')
+ F('value')
)
)
if self.value() == '1':
return queryset.filter(<b>sum_rating__gte=6, sum_rating__lt=12</b>)
# &hellip;</code></pre>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论