【问题标题】:Django Admin: show single instance of duplicate instancesDjango Admin:显示重复实例的单个实例
【发布时间】:2013-06-27 20:43:36
【问题描述】:

我正在尝试显示模型中的单个实例(db 行),其中多个实例共享多行的相同字段(列)值。为了澄清那句话,我有以下情况:

ID/Title/Slug/Modified
1   Car     A  1s ago
2   Car     A  2s ago
3   House   B  1s ago

如果上面的小表是我的数据库,我希望我的 Django 管理页面根据我的 slug 字段(列)显示不同的行显示最后编辑的版本(我有另一列时间......所以上表会显示在管理页面如下:

ID/Title/Slug/Modified
1   Car     A  1s ago
3   House   B  1s ago

虽然第 1 行和第 2 行有不同的 pk,但它们具有相同的 slug,但我只想要其中一个,然后……

我可以在我的views.py中实现如下

existing_data = MyModel.objects.filter(slug=slug).latest('modified')

但那是因为我正在寻找一个特定的蛞蝓实例。如果不是,我也可以使用 group_by...

我正在尝试在管理页面中显示此内容。我在模型管理器中尝试了以下技术,

class ModelManager(models.Manager):
    def get_query_set(self):
        return super(ModelManager, self).get_query_set().group_by('title')

但我收到此错误

'QuerySet' object has no attribute 'group_by'

然后我正在阅读this part of the Django book,他们在模型管理器中实现了原始 sql,我尝试将其复制到我的情况如下,

class ModelManager(models.Manager):
    def uniques(self):
        cursor = connection.cursor()
        cursor.execute("""
            SELECT *
            FROM eventform_event
            GROUP BY slug
            """)
        return [row[0] for row in cursor.fetchone()]

我是我的模特

objects = ModelManager()

我只是不确定如何让管理模型查看我的自定义管理器,它不会覆盖 get_query_set。当我确实使用此自定义管理器覆盖 get_query_set 时,我收到此错误

'long' object has no attribute '__getitem__'

我也尝试过测试 'values()、values_list()、distinct() 等,但它们都给我错误... distinct() 告诉我我的数据库 (mysql) 不支持此功能..现在确定我是否必须切换数据库才能实现此功能,现在我没有想法尝试...任何人都知道如何实现此功能..谢谢。

#

在我的 admin.py 页面中,我可以获得侧过滤器 (list_filter),以根据 this thread 推荐的 slug 列显示唯一条目...

不幸的是,我无法让我的模型在管理页面中显示的行基于某个列是唯一的......

【问题讨论】:

    标签: django django-models django-admin


    【解决方案1】:

    如果您将 Django 1.4+ 与 PostgreSQL 一起使用,则可以将 queryset.distinct() 与 *fields 参数一起使用。如果您使用的是旧版本的 Django 或不同的数据库引擎,请告诉我,我会想出一种不同的方法。

    如果你的模型看起来像这样:

    from django.db import models
    
    class TestModel(models.Model):
        title = models.TextField(max_length=150)
        slug = models.TextField(max_length=150)
        modified = models.DateTimeField(auto_now=True)
    

    那么你可以在ModelAdmin的queryset方法中做到这一点:

    from django.contrib import admin
    
    import models
    
    class TestModelAdmin(admin.ModelAdmin):
        list_display = ('title', 'slug', 'modified')
    
        def queryset(self, request):
            qs = super(TestModelAdmin, self).queryset(request)
            qs = qs.order_by('slug', '-modified').distinct('slug')
            return qs
    
    admin.site.register(models.TestModel, TestModelAdmin)
    

    希望对您有所帮助。

    编辑: 好的,如果您使用的是 MySQL,它就不是那么整洁了。您不能使用原始 SQL,因为 Django 管理员希望使用 QuerySet 而不是 RawQuerySet。即使我们可以使用原始 SQL,它也不会像 GROUP BY slug 那样简单,因为如果你这样做,MySQL 会按照它们在表中出现的顺序为你提供结果。因此,如果您的数据如下所示:

    +----+-------+------+---------------------+
    | id | title | slug | modified            |
    +----+-------+------+---------------------+
    |  1 | Car   | A    | 2013-06-30 20:18:06 |
    |  2 | House | B    | 2013-06-30 20:18:12 |
    |  3 | Car   | A    | 2013-06-30 21:02:51 |
    |  4 | Thing | C    | 2013-06-30 22:08:00 |
    |  5 | House | B    | 2013-06-30 22:08:05 |
    +----+-------+------+---------------------+
    

    无论任何 order by 子句如何,按 slug 分组都会为您提供 id 1、2 和 4。我们实际上想要 id 3、4 和 5,因为否则 Django 管理员将不会向我们显示最近修改的条目(这就是你所说的你想要的)。因此,在我们进行任何分组之前,我们必须从预先排序的表格中进行选择。

    我能想到的最好方法是将我给PostgreSQL的答案中的queryset方法更改为:

    def queryset(self, request):
        qs = super(TestModelAdmin, self).queryset(request)
        qs = qs.extra(where=[
            "id IN (
                SELECT id FROM (
                    SELECT * FROM myapp_testmodel ORDER BY modified DESC
                ) AS last_modified
                GROUP BY slug)"
        ])
        return qs
    

    您必须将id 更改为表的主键名称,并将myapp_testmodel 更改为表的名称。

    【讨论】:

    • 谢谢凯文。我使用 mysql 作为我的数据库引擎。我将不胜感激使用 mysql 实现此功能的任何帮助。因为我不知道此时此项目是否可以移动到 PostgreSQL...
    • 感谢@Kevin 完美运行!我不知道您可以将原始 sql 放入 django 管理查询集
    • 有人试过在 MySQL 5.7 上使用它吗?它不适合我。可能是因为这个issue
    猜你喜欢
    • 1970-01-01
    • 2018-10-28
    • 1970-01-01
    • 2018-02-07
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多