[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: class A:
attr1 = Ref(None) attr1 = Ref(None)
autoref(A, A.attr1) 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 field.klass = klass
setattr(klass, field.back.attribute, field.back) setattr(klass, field.back.attribute, field.back)

View file

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

View file

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