【问题标题】:Django - Admin ListFilter on QuerySet witn .extra()Django - 带有 .extra() 的 QuerySet 上的管理员列表过滤器
【发布时间】:2018-02-15 09:52:51
【问题描述】:

一天多的时间试图弄清楚如何使用 .extra() 在 QuerySet 上使用 Django Admin list_filter

在 AdAdmin 中,我需要从模型 AdHist 添加一个新列“ad_hist_status_id”,以便我可以在 SomeListFilter 上使用这部分代码:

def queryset(self, request, queryset):
    return queryset.filter(ad_hist_status_id=self.value())

看起来不可能。用 sql 做这件事很简单:

select a.*, ah.ad_hist_status_id from ad_ad a
join ad_adhist ah on ah.ad_id = a.id
where
ah.datetime_end is null
order by a.id DESC

直到现在我无法在 Django Admin 中使用这个 SomeListFilter,错误是:

FieldError at /admin/ad/ad/

Cannot resolve keyword 'ad_hist_status_id' into field. 
Choices are: addetailscategories, address, adhist, adphotos, 
adprice, adscheduleinfo, age, comment, county, county_id, 
date_inserted, date_updated, description, district, district_id, 
email, id, lat, lng, name, parish, parish_id, schedule_type,
schedule_type_id, telephone, title, user_inserted, user_inserted_id,
user_updated, user_updated_id

我的问题是,如何有效地将新列添加到 QuerySet,然后如何使用新列查询这个新 QuerySet?

下面是我的部分代码

模型:

class Ad(models.Model):
    title = models.CharField(max_length=250)
    name = models.CharField(max_length=100)
    description = models.TextField()
    age = models.IntegerField(blank=True, null=True)
    telephone = models.CharField(max_length=25)
    email = models.EmailField()
    district = models.ForeignKey(District)
    county = ChainedForeignKey(County, chained_field="district", chained_model_field="district", sort=True) # smart_selects app
    parish = ChainedForeignKey(Parish, chained_field="county", chained_model_field="county", sort=True) # smart_selects app
    address = models.CharField(max_length=250, null=True, blank=True)
    lat = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
    lng = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
    schedule_type = models.ForeignKey(AdScheduleType)
    comment = models.TextField(null=True, blank=True)
    user_inserted = models.ForeignKey(User, null=True, blank=True, related_name='user_inserted_ad')
    date_inserted = models.DateTimeField(auto_now_add=True)
    user_updated = models.ForeignKey(User, null=True, blank=True, related_name='user_updated_ad')
    date_updated = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.name

class AdHist(models.Model):
    ad = models.ForeignKey(Ad)
    datetime_begin = models.DateTimeField()
    datetime_end = models.DateTimeField(null=True, blank=True)
    ad_hist_status = models.ForeignKey(AdHistStatus)
    ad_hist_change_reason = models.ForeignKey(AdHistChangeReason)
    comment = models.TextField(null=True, blank=True)
    user_inserted = models.ForeignKey(User, null=True, blank=True, related_name='user_inserted_ad_hist')
    date_inserted = models.DateTimeField(auto_now_add=True)
    user_updated = models.ForeignKey(User, null=True, blank=True, related_name='user_updated_ad_hist')
    date_updated = models.DateTimeField(null=True, blank=True)

    def __str__(self):
        return self.ad.name

管理员:

class SomeListFilter(admin.SimpleListFilter):
    title = _('Approval State')
    parameter_name = 'ad_hist_status_id'

    def lookups(self, request, model_admin):
        return (
            ('1', _('Approved')),
            ('4', _('Not Approved')),
        )

    def queryset(self, request, queryset):
        return queryset.filter(ad_hist_status_id=self.value())

class AdAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'title', 'age', 'telephone', 
                    'email', 'district', 'county', 'parish', 
                    'ad_status', 'ad_hist_change_reason', 'comment',
                    'user_inserted', 'date_inserted', 'user_updated',
                    'date_updated', 'ad_hist_status_id')

    readonly_fields = ('ad_status', 'id', 'ad_hist_change_reason',
                   'ad_hist_status_id')

    list_filter = (SomeListFilter,)

    def get_queryset(self, request):
        qs = super(AdAdmin,self).get_queryset(request).extra(select={'ad_hist_status_id': 'select ad_hist_status_id from ad_adhist where ad_adhist.ad_id = ad_ad.id and ad_adhist.datetime_end is null'},)
        return qs

    def ad_hist_status_id(self, inst):
        return inst.ad_hist_status_id

谁能给我一个线索?

最好的问候

【问题讨论】:

    标签: python django django-queryset


    【解决方案1】:

    如果我理解你的问题,你正在寻找这个:

    from django.db.models import F
    
    def queryset(self, request, queryset):
        return queryset.filter(ad_hist_status__id=F('id'))
    

    F expression 用于引用同一模型中的字段,而要引用相关模型中的字段,则需要使用下划线 (__)。

    【讨论】:

    • 你好 Ankur。感谢您的回复。没用,模型Ad,没有任何key指向AdHist,只有AdHist指向Ad。有什么线索吗?
    • 你试过这个吗? AdHist 具有 Ad 的外键,这并不意味着您不能从 Ad 访问 AdHist。请您尝试后确认。
    【解决方案2】:

    仔细查看Django field lookups。看看文档怎么说:

    基本查找关键字参数采用field__lookuptype=value 的形式。 (这是一个双下划线)。

    你想从Ad中取出AdHist相关对象,它有一个相关的AdHistStatus,并得到它的id

    所以你应该像这样访问id 字段:

    def ad_hist_status_id(self, instance):
        return instance.adhist.ad_hist_status.id
    

    也可以直接用双下划线语法列出:

    class AdAdmin(admin.ModelAdmin):
        list_display = (..., 'adhist__ad_hist_status__id')
    

    【讨论】:

    • 嗨。谢谢回复。它不起作用,给我同样的错误。如果看到模型 Ad,没有任何键指向 AdHist,只有 AdHist 指向 Ad。还有更多线索吗?
    • @André 我想我现在已经注意到了这里的问题。您的 AdHist 模型与您的 Ad 具有外键关系。这意味着“一个Ad 有多个AdHists”。因此,您的Ad 实例中有一个AdHist 条目列表,您可以通过ad.adhist_set 访问它。考虑一下您是否想要建立 OneToOne 关系,而不是 ForeignKey 关系。
    • 你好文卡基萨。 OneToOne 将不起作用,因为 AdHist 将有多个对 Ad 的键引用。还有更多线索吗?
    • @Adré 那么你的逻辑在这里有点错误。您想显示一个某些 AdHistStatus id,但您的Ad 模型中有许多AdHist 条目。所以你不能那样做。您可以尝试使用Inline 模型管理员。
    • 模型 AdHist 用于跟踪对广告的所有修改。这种方法被广泛使用,但我知道 Django 有自己的方法。我想我将创建一个 CurrentHist 模型来建立 OneToOne 关系。谢谢
    猜你喜欢
    • 2011-01-23
    • 2011-08-11
    • 2013-09-16
    • 2016-03-10
    • 2021-01-01
    • 1970-01-01
    • 2016-03-22
    • 2017-01-21
    • 2015-09-29
    相关资源
    最近更新 更多