[Zope-dev] Re: Zope 2.7: OrderSupport for ObjectManager
Lennart Regebro
lennart@regebro.nu
Wed, 07 May 2003 11:40:48 +0200
I'm all confused now. What are the use cases for this order support again?
For me, ordered support is useful in two cases:
1. For people want to make menues or lists of document/objects, and want
them to be in a specific order. This requires ordering of the objects,
and to display that ordering.
2. When you make objects and services that rely on an ordered list of
subobjects. For example, say that you have a service of some kind that
uses several plugins to perform functions, and it has to call these
plugins in a particular order.
Is there anything I have missed?
When it comes to UI, you need to have a way of ordering and sorting the
objects, and of course displaying the cureent order. There are two ways
to do that as I can see it.
1. Use the currentZMI object listing, "main.dtml". That saves you from
having two user interfaces. It would be nice to have a sorting function
too, to sort on id, title and date, and this is alredy availiable in the
ZMI, and that means it doesn't have to be duplicated it.
2 Having two separate interfaces, one being the normal ZMI view, and the
second being a ZMI tab where you order and sort the objects. There are
benefits to this, for example it means that you can sort the ZMI object
list without changing the ordering. It also keeps the main.dtml exactly
as it is today, since the ordered support would instead add a separate
tab, and it would keep the main display less cluttered.
I do not as of today have an opinion on this last matter.
When it comes to API, I would expect an API with two methods. One to
move to a particular position, where 0 is first and -1 is last, as per
normal Python standard, and the other where you move relatively, where
negative numbers move you towards the front and positive towards the back.
As an example of this I give you my OrderedDictionary.py, which have:
def order(self, key, order)
and
def move(self, key, distance)
There would also be functions to move a set of keys one step forward,
one step backwards, first and last that are callable through the ZMI
with a list of ids, to make it easier to do web user interfaces.
I do have ONE opinion (wow!): I personally preferr the type of UI where
you check the boxes of the objects you want to move, and then click a
button to move all of them up/down/first/last as opposed to the UI that
has one up/down/first/last button per object.
Well, that's my take on all this. I'm sure I've missed some important
stuff, but as I said, I got all confused by the discussion. ;)
# (c) 2003 Nuxeo SARL <http://nuxeo.com>
# An Ordered, Persistent dictionary.
# Some code from David Benjamins odict
# $Id: OrderedDictionary.py,v 1.3 2003/02/21 17:14:30 regebro Exp $
from types import ListType, TupleType
from ZODB.PersistentMapping import PersistentMapping
from ZODB.PersistentList import PersistentList
class OrderedDictionary(PersistentMapping):
def __init__(self, dict = None):
if dict:
self._keys = PersistentList(dict.keys())
else:
self._keys = PersistentList()
PersistentMapping.__init__(self, dict)
def __delitem__(self, key):
PersistentMapping.__delitem__(self, key)
self._keys.remove(key)
def __setitem__(self, key, item):
PersistentMapping.__setitem__(self, key, item)
if key not in self._keys: self._keys.append(key)
def __cmp__(self, dict):
if isinstance(dict, OrderedDictionary):
return cmp(self.data, dict.data) and cmp(self._keys,
dict._keys)
else:
return 0
def clear(self):
PersistentMapping.clear(self)
self._keys = PersistentList()
def copy(self):
mcopy = OrderedDictionary()
mcopy.data = self.data.copy()
mcopy._keys = self._keys[:]
return mcopy
def items(self):
return zip(self._keys, self.values())
def keys(self):
return self._keys[:] # This returns a non-persistent copy of
self._keys,
# so you can manipulate the copy without
manipulating self._keys
def popitem(self):
try:
key = self._keys[-1]
except IndexError:
raise KeyError('dictionary is empty')
val = self[key]
del self[key]
return (key, val)
def index(self, key):
return self._keys.index(key)
def setdefault(self, key, failobj = None):
if key not in self._keys: self._keys.append(key)
return PersistentMapping.setdefault(self, key, failobj)
def update(self, dict):
for (key,val) in dict.items():
self.__setitem__(key,val)
def values(self):
return map(self.get, self._keys)
def order(self, key, order ):
"""Moves the specified item to the specifed position
0 is first, 1 is second, -1 is last, -1 second from last, and
so on.
"""
if order < 0:
order = len(self) + order # Negative numbers are counted
from behind
if order < 0:
order = 0 # It's still negative: move first
self._keys.remove(key)
if order >= len(self._keys):
self._keys.append(key)
else:
self._keys.insert(order, key)
def move(self, key, distance):
"""Moves the specified item a number of positions
Positive numbers move to higher index numbers, negavtive to
lower index numbers"""
oldpos = self.index(key)
newpos = oldpos + distance
if newpos < 0:
newpos = 0
self.order(key, newpos)