如何在Django管理员自定义过滤器中包含自定义模型方法?

huangapple go评论59阅读模式
英文:

How to include custom model method in Django admin custom filter?

问题

我试图做什么:

我试图在Django管理员中使用SimpleFilter创建自定义过滤器,以便在list_filter中使用。

我尝试过的:

下面是我在admin.py文件中编写的代码。

我使用了SimpleFilter来创建名为RoomScoreFilter的自定义过滤器。
RoomScoreFilter会将平均分数在1.001.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个字段(例如cleanlinesscheck_in等)相加,然后除以6并保留两位小数。

我遇到的错误:

然而,它输出了一个错误,这可能是我预料到的:

无法将关键字'get_average_rating'解析为字段。可选项是:accuracy、check_in、cleanliness、comment、communication、created_at、customer、customer_id、id、location、room、room_id、updated_at、value

如何在Django管理员自定义过滤器中包含自定义模型方法?

是否有解决此问题的方法?

英文:

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

如何在Django管理员自定义过滤器中包含自定义模型方法?

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

&hellip;

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() == &#39;1&#39;:
return queryset.filter(&lt;b&gt;avg_rating__gte=1, avg_rating__lt=2&lt;/b&gt;)&lt;/code&gt;&lt;/pre&gt;

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

&hellip;

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() == &#39;1&#39;:
return queryset.filter(&lt;b&gt;sum_rating__gte=6, sum_rating__lt=12&lt;/b&gt;)
# &amp;hellip;&lt;/code&gt;&lt;/pre&gt;

huangapple
  • 本文由 发表于 2023年6月25日 20:03:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76550296.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定