如何在Django中创建对象队列?

6 投票
3 回答
3631 浏览
提问于 2025-04-15 15:37

我刚接触Django,正在尝试自己搭建一个博客。我想实现一个在Drupal中看到的功能,使用的是nodequeue模块。

我想做的是能够创建对象队列,比如说博客文章的队列。下面是我对这些队列运作方式的想法:

  • 每个队列的大小应该由用户自己定义。
  • 添加到队列中的对象的日期应该被记录下来。
  • 我希望能够定义每个队列中项目的顺序(不过我觉得这可能会很难)。
  • 如果队列满了,添加一个新项目时应该把队列中最旧的项目丢掉。

这样一个功能的一个例子是创建一个推荐文章队列。

我目前的知识水平还不足以让我找到正确的方法来实现这个功能。任何建议都会很感激。

提前谢谢你们!

3 个回答

0

你可以使用django-activity-stream这个工具。虽然它没有像Nodequeue那样的用户界面,但它可以用来创建不同的对象队列。

3

Alex的方法非常不错。我不想和他的专业水平相比,但为了让内容更完整,这里有另一种方法,使用了很棒的Queue.Queue类(额外好处:线程安全,不过根据你的描述,这对你来说可能没什么用)。这可能对你来说更容易理解,因为你对此有些担心:

myqueue.py

#!/usr/bin/python

# Renamed in Python 3.0
try: from Queue import Queue, Full, Empty
except: from queue import Queue, Full, Empty
from datetime import datetime

# Spec 1: Size of each queue should be user-defined.
#   - maxsize on __init__

# Spec 2: Date an object is added should be recorded.
#   - datetime.now() is first member of tuple, data is second

# Spec 3: I would like to be able to define the order of the items that
# belong to each queue.
#   - Order cannot be rearranged with this queue.

# Spec 4: If the queue is full, the addition of an extra item should discard
# the oldest item of the queue.
#   - implemented in put()

class MyQueue(Queue):
    "Wrapper around Queue that discards old items instead of blocking."
    def __init__(self, maxsize=10):
        assert type(maxsize) is int, "maxsize should be an integer"
        Queue.__init__(self, maxsize)

    def put(self, item):
        "Put an item into the queue, possibly discarding an old item."
        try:
            Queue.put(self, (datetime.now(), item), False)
        except Full:
            # If we're full, pop an item off and add on the end.
            Queue.get(self, False)
            Queue.put(self, (datetime.now(), item), False)

    def put_nowait(self, item):
        "Put an item into the queue, possibly discarding an old item."
        self.put(item)

    def get(self):
        "Get a tuple containing an item and the datetime it was entered."
        try:
            return Queue.get(self, False)
        except Empty:
            return None

    def get_nowait(self):
        "Get a tuple containing an item and the datetime it was entered."
        return self.get()


def main():
    "Simple test method, showing at least spec #4 working."
    queue = MyQueue(5)
    for i in range(1, 7):
        queue.put("Test item number %u" % i)

    while not queue.empty():
        time_and_data = queue.get()
        print "%s => %s" % time_and_data


if __name__ == "__main__":
    main()

预期输出

2009-11-02 23:18:37.518586 => Test item number 2
2009-11-02 23:18:37.518600 => Test item number 3
2009-11-02 23:18:37.518612 => Test item number 4
2009-11-02 23:18:37.518625 => Test item number 5
2009-11-02 23:18:37.518655 => Test item number 6
5

这里有一种方法:

import collections, datetime, itertools

class nodequeue(object):
  def __init__(self, N):
    self.data = collections.deque(N * [(None, None)])
  def add(self, anobj):
    self.data.popleft()
    self.data.push((anobj, datetime.datetime.now())
  def __iter__(self):
    it = iter(self.data)
    return it.dropwhile(lambda x: x[1] is None, self.data)

这个方法没有考虑“顺序”的要求,不过加上这个要求其实也不难,比如:

class nodequeueprio(object):
  def __init__(self, N):
    self.data = collections.deque(N * [(None, None, None)])
  def add(self, anobj, prio):
    self.data.popleft()
    self.data.push((anobj, datetime.datetime.now(), prio)
  def __iter__(self):
    it = iter(self.data)
    return sorted(it.dropwhile(lambda x: x[1] is None, self.data),
                  key=operator.itemgetter(2))

我觉得在队列里预先填充一些占位符 None 可以让代码更简单,因为 add 方法可以在添加新东西之前,先把最左边(最旧的或者是 None)的项目去掉——虽然 __iter__ 方法需要去掉这些占位符,但这也不是太麻烦。

撰写回答