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

falq053o  于 2023-07-01  发布在  Go
关注(0)|答案(1)|浏览(125)

我想做的是:

我正在尝试在Django admin中使用SimpleFilter进行自定义过滤器,用于list_filter

我尝试了:

下面是我写在admin.py文件中的代码。
我使用SimpleFilter创建了名为RoomScoreFilter的自定义过滤器。RoomScoreFilter过滤器,如果平均得分为1.001.99,则将过滤为Very poor,依此类推。

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")

下面是我的模型。

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}")

所以基本上get_average_rating()方法所做的就是简单地将我写下的所有6个字段(cleanlinesscheck_in等)相加,然后除以6,四舍五入到2位。

我得到的错误:

然而,它吐出了我可能预料到的错误:

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

有没有解决这个问题的办法?

tvz2xvvm

tvz2xvvm1#

不能在查询集中使用属性或方法:查询集被转换成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)
    # …

相关问题