将列表转换为queryset

时间:2015-09-23 20:44:41

标签: python django django-rest-framework mongoengine

为了提高性能,在我的项目中,大多数模型实例都作为列表值存储在缓存中。但是Django Rest Framework中的所有通用视图都希望它们是查询集对象。如何将列表中的值转换为类似于对象的查询集,以便我可以使用通用视图。

说,我有一个像

这样的功能
def cache_user_articles(user_id):
    key = "articles_{0}".format(user_id)

    articles = cache.get(key)

    if articles is None:
        articles = list(Article.objects.filter(user_id = user_id))
        cache.set(key, articles)

    return articles

在我的views.py中,

class ArticleViewSet(viewsets.ModelViewSet):

    ...

    def get_queryset(self, request, *args, **kwargs):
        return cache_user_articles(kwargs.get(user_id))

但是,这当然不会起作用,因为Django Rest Framework期望get_queryset的结果是QuerySet对象,而PUT请求它会调用' get&# 39;方法就可以了。有什么办法,我可以让它与通用的DRF视图一起使用。

2 个答案:

答案 0 :(得分:3)

由于Duck Typing,Python就像动态语言一样真正闪耀的地方。你可以轻松地写出像QuerySet那样嘎嘎作响的东西。

import mongoengine
from bson import ObjectId


class DuckTypedQuerySet(list):

    def __init__(self, data, document):
        if not hasattr(data, '__iter__') or isinstance(data, mongoengine.Document):
            raise TypeError("DuckTypedQuerySet requires iterable data")

        super(DuckTypedQuerySet, self).__init__(data)

        self._document = document

    @property
    def objects(self):
        return self

    def _query_match(self, instance, **kwargs):
        is_match = True

        for key, value in kwargs.items():
            attribute = getattr(instance, key, None)

            if isinstance(attribute, ObjectId) and not isinstance(value, ObjectId):
                attribute = str(attribute)

            if not attribute == value:
                is_match = False
                break

        return is_match


    def filter(self, **kwargs):
        data = filter(lambda instance: self._query_match(instance, **kwargs), self)

        return self.__class__(data, self._document)

    def get(self, **kwargs):
        results = self.filter(**kwargs)

        if len(results) > 1:
            raise self._document.MultipleObjectsReturned("{0} items returned, instead of 1".format(len(results)))

        if len(results) < 1:
            raise self._document.DoesNotExist("{0} matching query does not exist.".format(str(self._document)))

        return results[0]

    def first(self):
        return next(iter(self), None)

    def all(self):
        return self

    def count(self):
        return len(self)


def cache_user_articles(user_id):
    key = "articles_{0}".format(user_id)

    articles = cache.get(key)

    if articles is None:
        articles = DuckTypedQuerySet(list(Article.objects.filter(user_id = user_id)), document = Article)
        cache.set(key, articles)

    return articles

当然,这不是一个详尽的实施。您可能需要添加queryset中存在的其他方法。但我认为这些将用于简单的用例。现在,您可以使用Django Rest Framework的通用实现。

答案 1 :(得分:0)

这个怎么样? (我用类变量模拟了redis缓存)

+-------------+---------------------+---------------------+-----------------+-------------+---------------------+---------------------+-----------------+---------------------+---------------------+------------------+
| event1_name | event1_down_time    | event1_up_time      | event1_duration | event2_name | event2_down_time    | event2_up_time      | event1_duration | overlap_down_time   | overlap_up_time     | overlap_duration |
+-------------+---------------------+---------------------+-----------------+-------------+---------------------+---------------------+-----------------+---------------------+---------------------+------------------+
| e1          | 2015-01-01 00:00:03 | 2015-01-01 00:00:08 | 00:00:05        | e2          | 2015-01-01 00:00:05 | 2015-01-01 00:00:06 | 00:00:01        | 2015-01-01 00:00:05 | 2015-01-01 00:00:06 | 00:00:01         |
| e1          | 2015-01-01 00:00:03 | 2015-01-01 00:00:08 | 00:00:05        | e3          | 2015-01-01 00:00:02 | 2015-01-01 00:00:09 | 00:00:07        | 2015-01-01 00:00:03 | 2015-01-01 00:00:08 | 00:00:05         |
| e1          | 2015-01-01 00:00:03 | 2015-01-01 00:00:08 | 00:00:05        | e4          | 2015-01-01 00:00:01 | 2015-01-01 00:00:04 | 00:00:03        | 2015-01-01 00:00:03 | 2015-01-01 00:00:04 | 00:00:01         |
| e1          | 2015-01-01 00:00:03 | 2015-01-01 00:00:08 | 00:00:05        | e5          | 2015-01-01 00:00:07 | 2015-01-01 00:00:10 | 00:00:03        | 2015-01-01 00:00:07 | 2015-01-01 00:00:08 | 00:00:01         |
| e2          | 2015-01-01 00:00:05 | 2015-01-01 00:00:06 | 00:00:01        | e3          | 2015-01-01 00:00:02 | 2015-01-01 00:00:09 | 00:00:07        | 2015-01-01 00:00:05 | 2015-01-01 00:00:06 | 00:00:01         |
| e3          | 2015-01-01 00:00:02 | 2015-01-01 00:00:09 | 00:00:07        | e4          | 2015-01-01 00:00:01 | 2015-01-01 00:00:04 | 00:00:03        | 2015-01-01 00:00:02 | 2015-01-01 00:00:04 | 00:00:02         |
| e3          | 2015-01-01 00:00:02 | 2015-01-01 00:00:09 | 00:00:07        | e5          | 2015-01-01 00:00:07 | 2015-01-01 00:00:10 | 00:00:03        | 2015-01-01 00:00:07 | 2015-01-01 00:00:09 | 00:00:02         |
+-------------+---------------------+---------------------+-----------------+-------------+---------------------+---------------------+-----------------+---------------------+---------------------+------------------+
7 rows in set (0.00 sec)

然后在您的观点或您需要的任何地方:

您可以致电class CachedManager(models.Manager): cache = dict() def cached(self, user_id): cached = self.cache.get(user_id, []) if not cached: self.cache[user_id] = [article.pk for article in self.filter(user_id=user_id)] return self.cache[user_id] class Article(models.Model): objects = CachedManager() user_id = models.IntegerField() # Whatever fields your Article model has