Django从原始查询创建管理列表

时间:2016-05-03 23:16:16

标签: python django admin

我愿意在admin

中创建查询集列表模板

查询集是通过cursor.execute()获取的,因此可能需要伪模型。

基本上我只想利用Django管理列表提供的分页和过滤功能。

这是我的代码的相关部分

models.py

class Query(object):

   def __init__(self, sql):

       self.sql = sql


   def execute_query(self):

        cursor = connection.cursor()
        start_time = time()

        try:
            cursor.execute(self.sql)
        except DatabaseError as e:
            cursor.close()
            raise e

        return cursor, ((time() - start_time) * 1000)

admin.py

 class QueryAdmin(admin.ModelAdmin):
 ....


admin.site.register(Query, QueryAdmin)

2 个答案:

答案 0 :(得分:4)

我刚刚实现了此功能。您需要新的视图RawChangeList从Django扩展ChangeList视图:

from django.contrib.admin.views.main import ChangeList
from django.db import connections

class RawChangeList(ChangeList):
    """
    Extended Django ChangeList to be able show data from RawQueryset.
    """
    def get_count(self):
        connection = connections[self.queryset.db]
        with connection.cursor() as c:
            if connection.vendor == 'microsoft':  # CTE in subquery is not working in SQL Server
                c.execute(self.queryset.raw_query)
                c.execute('SELECT @@ROWCOUNT')
            else:
                query = 'SELECT COUNT(*) FROM ({query}) AS sq'
                c.execute(query.format(query=self.queryset.raw_query))

            return c.fetchone()[0]

    def get_queryset_slice(self):
        connection = connections[self.queryset.db]
        if connection.vendor == 'microsoft':
            # SQL Server needs ordered query for slicing
            if hasattr(self.queryset, 'ordered') and self.queryset.ordered:
                query = '{query}'
            else:
                query = '{query} ORDER BY 1'
            query += ' OFFSET {offset} ROWS FETCH NEXT {limit} ROWS ONLY'
        else:
            query = '{query} LIMIT {limit} OFFSET {offset}'

        return self.queryset.model.objects.raw(
            query.format(
                query=self.queryset.raw_query,
                offset=self.page_num * self.list_per_page,
                limit=(self.page_num + 1) * self.list_per_page - self.page_num * self.list_per_page,
            )
        )

    def get_queryset(self, request):
        """
        Overriding to avoid applying filters in ChangeList because RawQueryset has not filter method.
        So any filters has to be applied manually for now.
        """
        qs = self.root_queryset
        if not hasattr(qs, 'count'):
            qs.count = lambda: self.get_count()
        return qs

    def get_results(self, request):
        if self.show_all:
            qs = self.queryset
        else:
            qs = self.get_queryset_slice()

        paginator = self.model_admin.get_paginator(request, self.queryset, self.list_per_page)

        self.result_count = paginator.count
        self.show_full_result_count = False
        self.show_admin_actions = True
        self.full_result_count = 0
        self.result_list = list(qs)
        self.can_show_all = True
        self.multi_page = True
        self.paginator = paginator

然后在admin:

中使用此RawChangeList
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):

    def get_changelist(self, request, **kwargs):
        return RawChangeList

    def get_queryset(self, request):
        return MyModel.objects.raw('SELECT * FROM my_app_my_model')

    def get_object(self, request, object_id, from_field=None):
        return MyModel.objects.raw('SELECT * FROM my_app_my_model WHERE id=%s', (object_id, ))[0]

答案 1 :(得分:3)

如果您只是尝试使用django管理功能来使用它的分页和过滤功能,您会发现自己花费的时间远远超过使用django pagination时的时间。在你发现的第一件事是,使用cursor.execute分页和过滤将无效

如果您确实需要某种管理功能,请继续阅读。

在django admin中使用自定义查询集的首选方法是覆盖admin类中的get_queryset方法。

  

ModelAdmin上的get_queryset方法返回所有的QuerySet   可以由管理站点编辑的模型实例。一个用例   重写此方法是显示登录用户拥有的对象:

所以我们的代码就像

class QueryAdmin(admin.ModelAdmin):
    def queryset(self, request, queryset):

        return SomeModel.objects.raw('SOME QUERY')

如果要返回自定义原始查询集,可以使用对象管理器上的原始方法调用来执行此操作。使用cursor.execute更为可取。您实际上需要这样的方法(objects.raw)而不是执行来使用一些django管理功能。

即使是现在你也会发现分页不起作用。然后你需要这样做:django pagination and RawQuerySet