What I am trying to do:

我试图使自定义过滤器在Django管理使用SimpleFilterlist_filter中使用.

What I tried:

下面是我在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位.

The error that I got:

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

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

enter image description here

有什么办法可以解决这个问题吗?

推荐答案

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

Django相关问答推荐

Django Model邮箱字段是必需的,即使在我将其设置为NULL=True和BLACK=True之后也是如此

构造一个定制字符串(实体、年份和字母数字的组合)作为Django中的Postgres数据库的主键是否明智?

Django ORM多表一对多关系问题

如何从基于类的视图将用户保存在 django 模型中

Django:获取每组最新的N条记录

Django 表单字段必填和可选配置

在 Django 模板中呈现标签名称而不是整数字段

如何根据查询集中的条件返回多个聚合?

在 Django 的 ORM 中访问存储过程的最佳方法是什么

Apache + mod_wsgi 与 nginx + gunicorn

django 有条件地过滤对象

Django:从视图中添加 non_field_error?

Django:何时使用 QuerySet 无

Django manage.py:在其依赖之前应用迁移

如何使 Django QuerySet 批量删除()更高效

如何从 Django 的 TabularInline 管理视图中省略对象名称?

如何获取经过身份验证的用户列表?

1 个 django 应用程序中约有 20 个模型

如何从 django 请求中获取完整的 url

获取'str'对象在Django中没有属性'get'