[gen] Added for Ref field, param 'insert' that defines at what position a newly tied object is inserted among referred objects.
This commit is contained in:
parent
477db8a21c
commit
98b748cfb1
108
fields/ref.py
108
fields/ref.py
|
@ -471,8 +471,8 @@ class Ref(Field):
|
||||||
|
|
||||||
def __init__(self, klass=None, attribute=None, validator=None,
|
def __init__(self, klass=None, attribute=None, validator=None,
|
||||||
multiplicity=(0,1), default=None, add=False, addConfirm=False,
|
multiplicity=(0,1), default=None, add=False, addConfirm=False,
|
||||||
delete=None, noForm=False, link=True, unlink=None, back=None,
|
delete=None, noForm=False, link=True, unlink=None, insert=None,
|
||||||
show=True, page='main', group=None, layouts=None,
|
back=None, show=True, page='main', group=None, layouts=None,
|
||||||
showHeaders=False, shownInfo=(), select=None, maxPerPage=30,
|
showHeaders=False, shownInfo=(), select=None, maxPerPage=30,
|
||||||
move=0, indexed=False, searchable=False,
|
move=0, indexed=False, searchable=False,
|
||||||
specificReadPermission=False, specificWritePermission=False,
|
specificReadPermission=False, specificWritePermission=False,
|
||||||
|
@ -486,7 +486,8 @@ class Ref(Field):
|
||||||
menuInfoMethod=None, menuUrlMethod=None):
|
menuInfoMethod=None, menuUrlMethod=None):
|
||||||
self.klass = klass
|
self.klass = klass
|
||||||
self.attribute = attribute
|
self.attribute = attribute
|
||||||
# May the user add new objects through this ref ?
|
# May the user add new objects through this ref ? "add" may also contain
|
||||||
|
# a method whose result must be a boolean value.
|
||||||
self.add = add
|
self.add = add
|
||||||
# When the user adds a new object, must a confirmation popup be shown?
|
# When the user adds a new object, must a confirmation popup be shown?
|
||||||
self.addConfirm = addConfirm
|
self.addConfirm = addConfirm
|
||||||
|
@ -510,6 +511,24 @@ class Ref(Field):
|
||||||
self.link = link
|
self.link = link
|
||||||
# May the user unlink existing objects?
|
# May the user unlink existing objects?
|
||||||
self.unlink = unlink
|
self.unlink = unlink
|
||||||
|
# When an object is inserted through this Ref field, at what position is
|
||||||
|
# it inserted? If "insert" is:
|
||||||
|
# None, it will be inserted at the end;
|
||||||
|
# "start", it will be inserted at the start of the tied objects;
|
||||||
|
# a method, (called with the object to insert as single arg), its return
|
||||||
|
# value (a number or a tuple of numbers) will be
|
||||||
|
# used to insert the object at the corresponding position
|
||||||
|
# (this method will also be applied to other objects to know
|
||||||
|
# where to insert the new one);
|
||||||
|
# a tuple, ('sort', method), the given method (called with the object
|
||||||
|
# to insert as single arg) will be used to sort tied objects
|
||||||
|
# and will be given as param "key" of the standard Python
|
||||||
|
# method "sort" applied on the list of tied objects.
|
||||||
|
# With value ('sort', method), a full sort is performed and may hardly
|
||||||
|
# reshake the tied objects; with value "method" alone, the tied
|
||||||
|
# object is inserted at some given place: tied objects are more
|
||||||
|
# maintained in the order of their insertion.
|
||||||
|
self.insert = insert
|
||||||
if unlink == None:
|
if unlink == None:
|
||||||
# By default, one may unlink objects via a Ref for which one can
|
# By default, one may unlink objects via a Ref for which one can
|
||||||
# link objects.
|
# link objects.
|
||||||
|
@ -863,47 +882,64 @@ class Ref(Field):
|
||||||
def linkObject(self, obj, value, back=False, noSecurity=True):
|
def linkObject(self, obj, value, back=False, noSecurity=True):
|
||||||
'''This method links p_value (which can be a list of objects) to p_obj
|
'''This method links p_value (which can be a list of objects) to p_obj
|
||||||
through this Ref field.'''
|
through this Ref field.'''
|
||||||
|
zobj = obj.o
|
||||||
# Security check
|
# Security check
|
||||||
if not noSecurity: obj.mayEdit(self.writePermission, raiseError=True)
|
if not noSecurity: zobj.mayEdit(self.writePermission, raiseError=True)
|
||||||
# p_value can be a list of objects
|
# p_value can be a list of objects
|
||||||
if type(value) in sutils.sequenceTypes:
|
if type(value) in sutils.sequenceTypes:
|
||||||
for v in value: self.linkObject(obj, v, back=back)
|
for v in value: self.linkObject(obj, v, back=back)
|
||||||
return
|
return
|
||||||
# Gets the list of referred objects (=list of uids), or create it.
|
# Gets the list of referred objects (=list of uids), or create it.
|
||||||
obj = obj.o
|
refs = getattr(zobj.aq_base, self.name, None)
|
||||||
refs = getattr(obj.aq_base, self.name, None)
|
|
||||||
if refs == None:
|
if refs == None:
|
||||||
refs = obj.getProductConfig().PersistentList()
|
refs = zobj.getProductConfig().PersistentList()
|
||||||
setattr(obj, self.name, refs)
|
setattr(zobj, self.name, refs)
|
||||||
# Insert p_value into it.
|
# Insert p_value into it.
|
||||||
uid = value.o.id
|
uid = value.o.id
|
||||||
if uid not in refs:
|
if uid in refs: return
|
||||||
# Where must we insert the object? At the start? At the end?
|
# Where must we insert the object?
|
||||||
if callable(self.add):
|
if not self.insert:
|
||||||
add = self.callMethod(obj, self.add)
|
refs.append(uid)
|
||||||
else:
|
elif self.insert == 'start':
|
||||||
add = self.add
|
refs.insert(0, uid)
|
||||||
if add == 'start':
|
elif callable(self.insert):
|
||||||
refs.insert(0, uid)
|
# It is a method. Use it on every tied object until we find where to
|
||||||
else:
|
# insert the new object.
|
||||||
refs.append(uid)
|
tool = zobj.getTool()
|
||||||
# Update the back reference
|
insertOrder = self.insert(obj, value)
|
||||||
if not back: self.back.linkObject(value, obj, back=True)
|
i = 0
|
||||||
|
inserted = False
|
||||||
|
while i < len(refs):
|
||||||
|
tied = tool.getObject(refs[i], appy=True)
|
||||||
|
if self.insert(obj, tied) > insertOrder:
|
||||||
|
refs.insert(i, uid)
|
||||||
|
inserted = True
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
if not inserted: refs.append(uid)
|
||||||
|
else:
|
||||||
|
# It is a tuple ('sort', method). Perform a full sort.
|
||||||
|
refs.append(uid)
|
||||||
|
tool = zobj.getTool()
|
||||||
|
refs.sort(key=lambda uid:self.insert[1](obj, \
|
||||||
|
tool.getObject(uid, appy=True)))
|
||||||
|
# Update the back reference
|
||||||
|
if not back: self.back.linkObject(value, obj, back=True)
|
||||||
|
|
||||||
def unlinkObject(self, obj, value, back=False, noSecurity=True):
|
def unlinkObject(self, obj, value, back=False, noSecurity=True):
|
||||||
'''This method unlinks p_value (which can be a list of objects) from
|
'''This method unlinks p_value (which can be a list of objects) from
|
||||||
p_obj through this Ref field.'''
|
p_obj through this Ref field.'''
|
||||||
|
zobj = obj.o
|
||||||
# Security check
|
# Security check
|
||||||
if not noSecurity: obj.mayEdit(self.writePermission, raiseError=True)
|
if not noSecurity: zobj.mayEdit(self.writePermission, raiseError=True)
|
||||||
# p_value can be a list of objects
|
# p_value can be a list of objects
|
||||||
if type(value) in sutils.sequenceTypes:
|
if type(value) in sutils.sequenceTypes:
|
||||||
for v in value: self.unlinkObject(obj, v, back=back)
|
for v in value: self.unlinkObject(obj, v, back=back)
|
||||||
return
|
return
|
||||||
obj = obj.o
|
refs = getattr(zobj.aq_base, self.name, None)
|
||||||
refs = getattr(obj.aq_base, self.name, None)
|
|
||||||
if not refs: return
|
if not refs: return
|
||||||
# Unlink p_value
|
# Unlink p_value
|
||||||
uid = value.o.UID()
|
uid = value.o.id
|
||||||
if uid in refs:
|
if uid in refs:
|
||||||
refs.remove(uid)
|
refs.remove(uid)
|
||||||
# Update the back reference
|
# Update the back reference
|
||||||
|
@ -919,19 +955,20 @@ class Ref(Field):
|
||||||
* a Appy object;
|
* a Appy object;
|
||||||
* a list of Appy or Zope objects.'''
|
* a list of Appy or Zope objects.'''
|
||||||
if not self.persist: return
|
if not self.persist: return
|
||||||
# Standardize p_value into a list of Zope objects
|
# Standardize p_value into a list of Appy objects
|
||||||
objects = value
|
objects = value
|
||||||
if not objects: objects = []
|
if not objects: objects = []
|
||||||
if type(objects) not in sutils.sequenceTypes: objects = [objects]
|
if type(objects) not in sutils.sequenceTypes: objects = [objects]
|
||||||
tool = obj.getTool()
|
tool = obj.getTool()
|
||||||
for i in range(len(objects)):
|
for i in range(len(objects)):
|
||||||
if isinstance(objects[i], basestring):
|
if isinstance(objects[i], basestring):
|
||||||
# We have a UID here
|
# We have an UID here
|
||||||
objects[i] = tool.getObject(objects[i])
|
objects[i] = tool.getObject(objects[i], appy=True)
|
||||||
else:
|
else:
|
||||||
# Be sure to have a Zope object
|
# Be sure to have an Appy object
|
||||||
objects[i] = objects[i].o
|
objects[i] = objects[i].appy()
|
||||||
uids = [o.UID() for o in objects]
|
uids = [o.o.id for o in objects]
|
||||||
|
appyObj = obj.appy()
|
||||||
# Unlink objects that are not referred anymore
|
# Unlink objects that are not referred anymore
|
||||||
refs = getattr(obj.aq_base, self.name, None)
|
refs = getattr(obj.aq_base, self.name, None)
|
||||||
if refs:
|
if refs:
|
||||||
|
@ -939,11 +976,11 @@ class Ref(Field):
|
||||||
while i >= 0:
|
while i >= 0:
|
||||||
if refs[i] not in uids:
|
if refs[i] not in uids:
|
||||||
# Object having this UID must unlink p_obj
|
# Object having this UID must unlink p_obj
|
||||||
self.back.unlinkObject(tool.getObject(refs[i]), obj)
|
tied = tool.getObject(refs[i], appy=True)
|
||||||
|
self.back.unlinkObject(tied, appyObj)
|
||||||
i -= 1
|
i -= 1
|
||||||
# Link new objects
|
# Link new objects
|
||||||
if objects:
|
if objects: self.linkObject(appyObj, objects)
|
||||||
self.linkObject(obj, objects)
|
|
||||||
|
|
||||||
def mayAdd(self, obj, checkMayEdit=True):
|
def mayAdd(self, obj, checkMayEdit=True):
|
||||||
'''May the user create a new referred object from p_obj via this Ref?
|
'''May the user create a new referred object from p_obj via this Ref?
|
||||||
|
@ -954,10 +991,7 @@ class Ref(Field):
|
||||||
# We can't (yet) do that on back references.
|
# We can't (yet) do that on back references.
|
||||||
if self.isBack: return gutils.No('is_back')
|
if self.isBack: return gutils.No('is_back')
|
||||||
# Check if this Ref is addable
|
# Check if this Ref is addable
|
||||||
if callable(self.add):
|
add = self.getAttribute(obj, 'add')
|
||||||
add = self.callMethod(obj, self.add)
|
|
||||||
else:
|
|
||||||
add = self.add
|
|
||||||
if not add: return gutils.No('no_add')
|
if not add: return gutils.No('no_add')
|
||||||
# Have we reached the maximum number of referred elements?
|
# Have we reached the maximum number of referred elements?
|
||||||
if self.multiplicity[1] != None:
|
if self.multiplicity[1] != None:
|
||||||
|
|
|
@ -135,7 +135,7 @@ class BaseMixin:
|
||||||
for field in self.getAllAppyTypes():
|
for field in self.getAllAppyTypes():
|
||||||
if field.type != 'Ref': continue
|
if field.type != 'Ref': continue
|
||||||
for obj in field.getValue(self):
|
for obj in field.getValue(self):
|
||||||
field.back.unlinkObject(obj, self, back=True)
|
field.back.unlinkObject(obj, appyObj, back=True)
|
||||||
# Uncatalog the object
|
# Uncatalog the object
|
||||||
self.reindex(unindex=True)
|
self.reindex(unindex=True)
|
||||||
# Delete the filesystem folder corresponding to this object
|
# Delete the filesystem folder corresponding to this object
|
||||||
|
|
|
@ -788,12 +788,12 @@ class AbstractWrapper(object):
|
||||||
def link(self, fieldName, obj):
|
def link(self, fieldName, obj):
|
||||||
'''This method links p_obj (which can be a list of objects) to this one
|
'''This method links p_obj (which can be a list of objects) to this one
|
||||||
through reference field p_fieldName.'''
|
through reference field p_fieldName.'''
|
||||||
return self.getField(fieldName).linkObject(self.o, obj)
|
return self.getField(fieldName).linkObject(self, obj)
|
||||||
|
|
||||||
def unlink(self, fieldName, obj):
|
def unlink(self, fieldName, obj):
|
||||||
'''This method unlinks p_obj (which can be a list of objects) from this
|
'''This method unlinks p_obj (which can be a list of objects) from this
|
||||||
one through reference field p_fieldName.'''
|
one through reference field p_fieldName.'''
|
||||||
return self.getField(fieldName).unlinkObject(self.o, obj)
|
return self.getField(fieldName).unlinkObject(self, obj)
|
||||||
|
|
||||||
def sort(self, fieldName, sortKey='title', reverse=False):
|
def sort(self, fieldName, sortKey='title', reverse=False):
|
||||||
'''Sorts referred elements linked to p_self via p_fieldName according
|
'''Sorts referred elements linked to p_self via p_fieldName according
|
||||||
|
@ -854,7 +854,7 @@ class AbstractWrapper(object):
|
||||||
setattr(appyObj, attrName, attrValue)
|
setattr(appyObj, attrName, attrValue)
|
||||||
if isField:
|
if isField:
|
||||||
# Link the object to this one
|
# Link the object to this one
|
||||||
appyType.linkObject(self.o, zopeObj)
|
appyType.linkObject(self, appyObj)
|
||||||
# Call custom initialization
|
# Call custom initialization
|
||||||
if externalData: param = externalData
|
if externalData: param = externalData
|
||||||
else: param = True
|
else: param = True
|
||||||
|
|
Loading…
Reference in a new issue