Django:如何将原始sql查询的结果映射到管理列表视图中的模型实例?

时间:2011-12-31 11:14:33

标签: django django-models django-admin

考虑简单模型:

class Item(models.Model):
    name                = models.CharField(...)
    unit_cost           = models.DecimalField(...)
    unit_price          = models.DecimalField(...)

它有以下管理员类:

class ItemAdmin(admin.ModelAdmin):
    def queryset(self, request):
        qs = self.model._default_manager.get_query_set()
        qs2 = self.model._default_manager.raw('''
        SELECT
            "stock_item"."id",
            "stock_item"."name",
            "stock_item"."unit_cost",
            "stock_item"."unit_price"
        FROM
            "stock_item"
        ''')
        qs3 = RawQuerySet('''
        SELECT
            "stock_item"."id",
            "stock_item"."name",
            "stock_item"."unit_cost",
            "stock_item"."unit_price"
        FROM
            "stock_item"
        ''', self.model)
        return qs   # WORKS
        return qs2  # DOESN'T WORK
        return qs3  # DOESN'T WORK

我重写了queryset()方法,以便控制管理列表视图的行为。我想在queryset()中执行原始sql查询,在将结果发送到列表视图之前将结果映射回Item模型。 问题是返回qs2或qs3会在模板中生成以下错误(不会抛出异常):

Database error
Something's wrong with your database installation. Make sure the appropriate database tables have been created, and make sure the database is readable by the appropriate user.

请注意,在单独的脚本中运行原始查询有效,例如:

items = Item._default_manager.raw('''
    SELECT
        "stock_item"."id",
        "stock_item"."name",
        "stock_item"."unit_cost",
        "stock_item"."unit_price"
    FROM
        "stock_item"
    ''')        

for item in items:
    print item.name, item.unit_price # WORKS!

事实上,我有一个更大的抱负,能够创建一种不应该有相应数据库表的“虚拟模型”,其目的是在其管理类中封装sql投影查询(以便模型可以显示在它的管理列表视图中。

我尝试过另一种方法,根本不使用ItemAdmin,而是使用:

class ItemManager(models.Manager):
    def get_query_set(self):
        return Item._default_manager.raw('''
        SELECT
            "stock_item"."id",
            "stock_item"."name",
            "stock_item"."unit_cost",
            "stock_item"."unit_price"
        FROM
            "stock_item"
        ''')

使用:

class Item(models.Model):
    objects = ItemManager()
    etc...

但现在我在管理列表视图中收到模板异常:

'RawQuerySet'对象没有属性'complex_filter'

怎么办?感谢...

2 个答案:

答案 0 :(得分:1)

解决。

最简单的方法是在sqlite3(我正在使用的db后端)中创建数据库视图,然后添加一个名称与视图兼容的普通模型。模型必须具有一组字段,这些字段的名称与视图返回的字段的名称相似。我的目的不需要管理员或经理。

查看创建数据库脚本:

CREATE VIEW <view_name> AS SELECT etc...

查看删除数据库脚本(如果需要重新创建视图,则作为预备步骤运行):

DROP VIEW <view_name>

创建视图时,唯一需要注意的是确保设置:

class Meta:
    managed = False
    #ordering = ( '-date', )

以便在运行syncdb时不创建表。

还有一件事,你需要在视图中选择一个唯一的字段作为主键(并在模型中反映出来),否则django会抱怨像:

no such column: stock_itemdbview.id

干杯!

答案 1 :(得分:0)

好好看src,QuerySet和RawQuerySet是类似但非常不同的类,其中QuerySet在提供的功能方面胜过RawQuerySet。

如果我理解正确的话,你提到的最后一个错误是可以理解的,因为RawQuerySet没有complex_filter方法,我认为你在调用model.objects.filter的某个点,我相信它会调用前面提到的。

如何迭代RawQuerySet也是可以理解的,因为,由于缺少更好的术语,“核心模型”仍然在RawQuerySet中表示,就像在QuerySet中一样。

使用extra()是不够的?

如果/当我找到第一个问题,我仍然会思考第一个问题。