[gen] Bugfixes and use of Ref.autoref fto avoid circular references within cross-class Ref fields.

This commit is contained in:
Gaetan Delannay 2014-04-22 19:37:36 +02:00
parent e1b6b1b951
commit ef21375410
4 changed files with 60 additions and 30 deletions

View file

@ -1129,6 +1129,36 @@ def autoref(klass, field):
class A:
attr1 = Ref(None)
autoref(A, A.attr1)
This function can also be used to avoid circular imports between 2
classes from 2 different packages. Imagine class P1 in package p1 has a
Ref to class P2 in package p2; and class P2 has another Ref to p1.P1
(which is not the back Ref of the previous one: it is another,
independent Ref).
In p1, you have
from p2 import P2
class P1:
ref1 = Ref(P2)
Then, if you write the following in p2, python will complain because of a
circular import:
from p1 import P1
class P2:
ref2 = Ref(P1)
The solution is to write this. In p1:
from p2 import P2
class P1:
ref1 = Ref(P2)
autoref(P1, P2.ref2)
And, in p2:
class P2:
ref2 = Ref(None)
'''
field.klass = klass
setattr(klass, field.back.attribute, field.back)

View file

@ -332,23 +332,21 @@ class Transition:
if msgPart: msg += msgPart
return msg
def trigger(self, transitionName, obj, wf, comment, doAction=True,
doNotify=True, doHistory=True, doSay=True, reindex=True,
noSecurity=False):
'''This method triggers this transition on p_obj. The transition is
supposed to be triggerable (call to self.isTriggerable must have been
performed before calling this method). If p_doAction is False, the
action that must normally be executed after the transition has been
triggered will not be executed. If p_doNotify is False, the
email notifications that must normally be launched after the
transition has been triggered will not be launched. If p_doHistory is
False, there will be no trace from this transition triggering in the
workflow history. If p_doSay is False, we consider the transition is
trigger programmatically, and no message is returned to the user.
If p_reindex is False, object reindexing will be performed by the
calling method.'''
# Security check
if not noSecurity and not self.isTriggerable(obj, wf):
def trigger(self, name, obj, wf, comment, doAction=True, doNotify=True,
doHistory=True, doSay=True, reindex=True, noSecurity=False):
'''This method triggers this transition (named p_name) on p_obj. If
p_doAction is False, the action that must normally be executed after
the transition has been triggered will not be executed. If p_doNotify
is False, the email notifications that must normally be launched
after the transition has been triggered will not be launched. If
p_doHistory is False, there will be no trace from this transition
triggering in the workflow history. If p_doSay is False, we consider
the transition is triggered programmatically, and no message is
returned to the user. If p_reindex is False, object reindexing will
be performed by the calling method.'''
# "Triggerability" and security checks.
if (name != '_init_') and \
not self.isTriggerable(obj, wf, noSecurity=noSecurity):
raise Exception('Transition "%s" can\'t be triggered.' % name)
# Create the workflow_history dict if it does not exist.
if not hasattr(obj.aq_base, 'workflow_history'):
@ -371,8 +369,8 @@ class Transition:
targetStateName = targetState.getName(wf)
break
# Create the event and add it in the object history
action = transitionName
if transitionName == '_init_': action = None
action = name
if name == '_init_': action = None
if not doHistory: comment = '_invisible_'
obj.addHistoryEvent(action, review_state=targetStateName,
comments=comment)
@ -384,9 +382,9 @@ class Transition:
if reindex and not obj.isTemporary(): obj.reindex()
# Send notifications if needed
if doNotify and self.notify and obj.getTool(True).mailEnabled:
sendNotification(obj.appy(), self, transitionName, wf)
sendNotification(obj.appy(), self, name, wf)
# Return a message to the user if needed
if not doSay or (transitionName == '_init_'): return
if not doSay or (name == '_init_'): return
if not msg: msg = obj.translate('object_saved')
obj.say(msg)

View file

@ -324,7 +324,6 @@ class ToolMixin(BaseMixin):
If p_refObject and p_refField are given, the query is limited to the
objects that are referenced from p_refObject through p_refField.'''
params = {'ClassName': className}
appyClass = self.getAppyClass(className, wrapper=True)
if not brainsOnly: params['batch'] = True

View file

@ -874,19 +874,22 @@ class AbstractWrapper(object):
replaced with normal chars.'''
return normalizeString(s, usage)
def search(self, klass, sortBy='', maxResults=None, noSecurity=False,
**fields):
def search(self, klass, sortBy='', sortOrder='asc', maxResults=None,
noSecurity=False, **fields):
'''Searches objects of p_klass. p_sortBy must be the name of an indexed
field (declared with indexed=True); every param in p_fields must
take the name of an indexed field and take a possible value of this
field. You can optionally specify a maximum number of results in
p_maxResults. If p_noSecurity is specified, you get all objects,
even if the logged user does not have the permission to view it.'''
field (declared with indexed=True); p_sortOrder can be "asc"
(ascending, the defaut) or "desc" (descending); every param in
p_fields must take the name of an indexed field and take a possible
value of this field. You can optionally specify a maximum number of
results in p_maxResults. If p_noSecurity is specified, you get all
objects, even if the logged user does not have the permission to
view it.'''
# Find the content type corresponding to p_klass
tool = self.tool.o
contentType = tool.getPortalType(klass)
# Create the Search object
search = Search('customSearch', sortBy=sortBy, **fields)
search = Search('customSearch', sortBy=sortBy, sortOrder=sortOrder,
**fields)
if not maxResults:
maxResults = 'NO_LIMIT'
# If I let maxResults=None, only a subset of the results will be