author | Aurelien Campeas <aurelien.campeas@logilab.fr> |
Tue, 15 Dec 2009 17:00:58 +0100 | |
branch | stable |
changeset 4122 | 3c3d2a102d4c |
parent 3961 | d1cbf77db999 |
child 3998 | 94cc7cad3d2d |
child 4212 | ab6573088b4a |
permissions | -rw-r--r-- |
0 | 1 |
"""Base class for entity objects manipulated in clients |
2 |
||
3 |
:organization: Logilab |
|
1977
606923dff11b
big bunch of copyright / docstring update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1904
diff
changeset
|
4 |
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
0 | 5 |
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
1977
606923dff11b
big bunch of copyright / docstring update
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
1904
diff
changeset
|
6 |
:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
0 | 7 |
""" |
8 |
__docformat__ = "restructuredtext en" |
|
9 |
||
1154 | 10 |
from warnings import warn |
11 |
||
0 | 12 |
from logilab.common import interface |
13 |
from logilab.common.compat import all |
|
14 |
from logilab.common.decorators import cached |
|
2613
5e19c2bb370e
R [all] logilab.common 0.44 provides only deprecated
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2421
diff
changeset
|
15 |
from logilab.common.deprecation import deprecated |
2312
af4d8f75c5db
use xml_escape
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2125
diff
changeset
|
16 |
from logilab.mtconverter import TransformData, TransformError, xml_escape |
631
99f5852f8604
major selector refactoring (mostly to avoid looking for select parameters on the target class), start accept / interface unification)
sylvain.thenault@logilab.fr
parents:
479
diff
changeset
|
17 |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
18 |
from rql import parse |
0 | 19 |
from rql.utils import rqlvar_maker |
20 |
||
3664
af7ca3597b8d
use typed_eid
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3626
diff
changeset
|
21 |
from cubicweb import Unauthorized, typed_eid |
0 | 22 |
from cubicweb.rset import ResultSet |
692
800592b8d39b
replace deprecated cubicweb.common.selectors by its new module path (cubicweb.selectors)
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
631
diff
changeset
|
23 |
from cubicweb.selectors import yes |
2656
a93ae0f6c0ad
R [base classes] only AppObject remaning, no more AppRsetObject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2650
diff
changeset
|
24 |
from cubicweb.appobject import AppObject |
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
25 |
from cubicweb.rqlrewrite import RQLRewriter |
709 | 26 |
from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype |
27 |
||
2421
08d42928fe36
does not make sense any more
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
2381
diff
changeset
|
28 |
from cubicweb.common.uilib import printable_value, soup2xhtml |
08d42928fe36
does not make sense any more
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
2381
diff
changeset
|
29 |
from cubicweb.common.mixins import MI_REL_TRIGGERS |
08d42928fe36
does not make sense any more
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
2381
diff
changeset
|
30 |
from cubicweb.common.mttransforms import ENGINE |
0 | 31 |
|
32 |
_marker = object() |
|
33 |
||
34 |
def greater_card(rschema, subjtypes, objtypes, index): |
|
35 |
for subjtype in subjtypes: |
|
36 |
for objtype in objtypes: |
|
37 |
card = rschema.rproperty(subjtype, objtype, 'cardinality')[index] |
|
38 |
if card in '+*': |
|
39 |
return card |
|
40 |
return '1' |
|
41 |
||
42 |
||
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
43 |
_MODE_TAGS = set(('link', 'create')) |
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
44 |
_CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata')) |
0 | 45 |
|
1154 | 46 |
try: |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
47 |
from cubicweb.web import formwidgets, uicfg |
0 | 48 |
|
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
49 |
def _dispatch_rtags(tags, rtype, role, stype, otype): |
1154 | 50 |
for tag in tags: |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
51 |
if tag in _MODE_TAGS: |
1739 | 52 |
uicfg.actionbox_appearsin_addmenu.tag_relation( |
53 |
(stype, rtype, otype, role), tag == 'create') |
|
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
54 |
elif tag in _CATEGORY_TAGS: |
1739 | 55 |
uicfg.autoform_section.tag_relation((stype, rtype, otype, role), |
56 |
tag) |
|
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
57 |
elif tag == 'inlineview': |
1739 | 58 |
uicfg.autoform_is_inlined.tag_relation((stype, rtype, otype, role), True) |
1154 | 59 |
else: |
60 |
raise ValueError(tag) |
|
1471
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
61 |
|
1154 | 62 |
except ImportError: |
1734
e8673144bc74
access to rwidgets through uicfg, removing buggy import breaking bw compat on the way
sylvain.thenault@logilab.fr
parents:
1721
diff
changeset
|
63 |
|
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
64 |
_dispatch_rtags = None |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
65 |
|
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
66 |
def _get_etype(bases, classdict): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
67 |
try: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
68 |
return classdict['id'] |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
69 |
except KeyError: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
70 |
for base in bases: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
71 |
etype = getattr(base, 'id', None) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
72 |
if etype and etype != 'Any': |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
73 |
return etype |
1474 | 74 |
|
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
75 |
def _get_defs(attr, name, bases, classdict): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
76 |
try: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
77 |
yield name, classdict.pop(attr) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
78 |
except KeyError: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
79 |
for base in bases: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
80 |
try: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
81 |
value = getattr(base, attr) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
82 |
delattr(base, attr) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
83 |
yield base.__name__, value |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
84 |
except AttributeError: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
85 |
continue |
1474 | 86 |
|
3245 | 87 |
|
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
88 |
class _metaentity(type): |
0 | 89 |
"""this metaclass sets the relation tags on the entity class |
90 |
and deals with the `widgets` attribute |
|
91 |
""" |
|
92 |
def __new__(mcs, name, bases, classdict): |
|
93 |
# collect baseclass' rtags |
|
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
94 |
etype = _get_etype(bases, classdict) |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
95 |
if etype and _dispatch_rtags is not None: |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
96 |
for name, rtags in _get_defs('__rtags__', name, bases, classdict): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
97 |
warn('%s: __rtags__ is deprecated' % name, DeprecationWarning) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
98 |
for relation, tags in rtags.iteritems(): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
99 |
# tags must become an iterable |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
100 |
if isinstance(tags, basestring): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
101 |
tags = (tags,) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
102 |
# relation must become a 3-uple (rtype, targettype, role) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
103 |
if isinstance(relation, basestring): |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
104 |
_dispatch_rtags(tags, relation, 'subject', etype, '*') |
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
105 |
_dispatch_rtags(tags, relation, 'object', '*', etype) |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
106 |
elif len(relation) == 1: # useful ? |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
107 |
_dispatch_rtags(tags, relation[0], 'subject', etype, '*') |
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
108 |
_dispatch_rtags(tags, relation[0], 'object', '*', etype) |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
109 |
elif len(relation) == 2: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
110 |
rtype, ttype = relation |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
111 |
ttype = bw_normalize_etype(ttype) # XXX bw compat |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
112 |
_dispatch_rtags(tags, rtype, 'subject', etype, ttype) |
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
113 |
_dispatch_rtags(tags, rtype, 'object', ttype, etype) |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
114 |
elif len(relation) == 3: |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
115 |
rtype, ttype, role = relation |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
116 |
ttype = bw_normalize_etype(ttype) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
117 |
if role == 'subject': |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
118 |
_dispatch_rtags(tags, rtype, 'subject', etype, ttype) |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
119 |
else: |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
120 |
_dispatch_rtags(tags, rtype, 'object', ttype, etype) |
1154 | 121 |
else: |
1177
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
122 |
raise ValueError('bad rtag definition (%r)' % (relation,)) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
123 |
for name, widgets in _get_defs('widgets', name, bases, classdict): |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
124 |
warn('%s: widgets is deprecated' % name, DeprecationWarning) |
7074698c6522
fix support for old __rtags__ and widgets
sylvain.thenault@logilab.fr
parents:
1154
diff
changeset
|
125 |
for rtype, wdgname in widgets.iteritems(): |
1804 | 126 |
if wdgname in ('URLWidget', 'EmbededURLWidget', 'RawDynamicComboBoxWidget'): |
1269 | 127 |
warn('%s widget is deprecated' % wdgname, DeprecationWarning) |
128 |
continue |
|
1313
9cff1eee0208
put class, not class name into rwidgets. New rfields rtags to specify a field for a relation
sylvain.thenault@logilab.fr
parents:
1269
diff
changeset
|
129 |
if wdgname == 'StringWidget': |
9cff1eee0208
put class, not class name into rwidgets. New rfields rtags to specify a field for a relation
sylvain.thenault@logilab.fr
parents:
1269
diff
changeset
|
130 |
wdgname = 'TextInput' |
9cff1eee0208
put class, not class name into rwidgets. New rfields rtags to specify a field for a relation
sylvain.thenault@logilab.fr
parents:
1269
diff
changeset
|
131 |
widget = getattr(formwidgets, wdgname) |
1582 | 132 |
assert hasattr(widget, 'render') |
1754
c9c7618a90de
autoform_widget superseeded by autoform_field_kwargs (api change addiction :-/)
sylvain.thenault@logilab.fr
parents:
1739
diff
changeset
|
133 |
uicfg.autoform_field_kwargs.tag_subject_of( |
c9c7618a90de
autoform_widget superseeded by autoform_field_kwargs (api change addiction :-/)
sylvain.thenault@logilab.fr
parents:
1739
diff
changeset
|
134 |
(etype, rtype, '*'), {'widget': widget}) |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
135 |
return super(_metaentity, mcs).__new__(mcs, name, bases, classdict) |
0 | 136 |
|
137 |
||
2656
a93ae0f6c0ad
R [base classes] only AppObject remaning, no more AppRsetObject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2650
diff
changeset
|
138 |
class Entity(AppObject, dict): |
0 | 139 |
"""an entity instance has e_schema automagically set on |
140 |
the class and instances has access to their issuing cursor. |
|
1474 | 141 |
|
0 | 142 |
A property is set for each attribute and relation on each entity's type |
143 |
class. Becare that among attributes, 'eid' is *NEITHER* stored in the |
|
144 |
dict containment (which acts as a cache for other attributes dynamically |
|
145 |
fetched) |
|
146 |
||
147 |
:type e_schema: `cubicweb.schema.EntitySchema` |
|
148 |
:ivar e_schema: the entity's schema |
|
149 |
||
150 |
:type rest_var: str |
|
151 |
:cvar rest_var: indicates which attribute should be used to build REST urls |
|
152 |
If None is specified, the first non-meta attribute will |
|
153 |
be used |
|
1474 | 154 |
|
0 | 155 |
:type skip_copy_for: list |
156 |
:cvar skip_copy_for: a list of relations that should be skipped when copying |
|
157 |
this kind of entity. Note that some relations such |
|
158 |
as composite relations or relations that have '?1' as object |
|
3626
017869a514c3
[doc] updated docstrings
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3552
diff
changeset
|
159 |
cardinality are always skipped. |
0 | 160 |
""" |
1498
2c6eec0b46b9
fix imports, cleanup, repair some ajax calls
sylvain.thenault@logilab.fr
parents:
1474
diff
changeset
|
161 |
__metaclass__ = _metaentity |
0 | 162 |
__registry__ = 'etypes' |
717 | 163 |
__select__ = yes() |
1264
fe2934a7df7f
cleanup, avoid spurious warning
sylvain.thenault@logilab.fr
parents:
1177
diff
changeset
|
164 |
|
1474 | 165 |
# class attributes that must be set in class definition |
0 | 166 |
id = None |
167 |
rest_attr = None |
|
1138
22f634977c95
make pylint happy, fix some bugs on the way
sylvain.thenault@logilab.fr
parents:
1132
diff
changeset
|
168 |
fetch_attrs = None |
2920
64322aa83a1d
start a new workflow engine
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2877
diff
changeset
|
169 |
skip_copy_for = ('in_state',) |
1264
fe2934a7df7f
cleanup, avoid spurious warning
sylvain.thenault@logilab.fr
parents:
1177
diff
changeset
|
170 |
# class attributes set automatically at registration time |
fe2934a7df7f
cleanup, avoid spurious warning
sylvain.thenault@logilab.fr
parents:
1177
diff
changeset
|
171 |
e_schema = None |
1474 | 172 |
|
0 | 173 |
MODE_TAGS = set(('link', 'create')) |
174 |
CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata')) |
|
175 |
@classmethod |
|
176 |
def __initialize__(cls): |
|
177 |
"""initialize a specific entity class by adding descriptors to access |
|
178 |
entity type's attributes and relations |
|
179 |
""" |
|
180 |
etype = cls.id |
|
181 |
assert etype != 'Any', etype |
|
182 |
cls.e_schema = eschema = cls.schema.eschema(etype) |
|
183 |
for rschema, _ in eschema.attribute_definitions(): |
|
184 |
if rschema.type == 'eid': |
|
185 |
continue |
|
186 |
setattr(cls, rschema.type, Attribute(rschema.type)) |
|
187 |
mixins = [] |
|
2941
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
188 |
for rschema, _, role in eschema.relation_definitions(): |
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
189 |
if (rschema, role) in MI_REL_TRIGGERS: |
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
190 |
mixin = MI_REL_TRIGGERS[(rschema, role)] |
0 | 191 |
if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ? |
192 |
mixins.append(mixin) |
|
193 |
for iface in getattr(mixin, '__implements__', ()): |
|
194 |
if not interface.implements(cls, iface): |
|
195 |
interface.extend(cls, iface) |
|
2941
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
196 |
if role == 'subject': |
0 | 197 |
setattr(cls, rschema.type, SubjectRelation(rschema)) |
198 |
else: |
|
199 |
attr = 'reverse_%s' % rschema.type |
|
200 |
setattr(cls, attr, ObjectRelation(rschema)) |
|
201 |
if mixins: |
|
3919
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
202 |
# see etype class instantation in cwvreg.ETypeRegistry.etype_class method: |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
203 |
# due to class dumping, cls is the generated top level class with actual |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
204 |
# user class as (only) parent. Since we want to be able to override mixins |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
205 |
# method from this user class, we have to take care to insert mixins after that |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
206 |
# class |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
207 |
# |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
208 |
# note that we don't plug mixins as user class parent since it causes pb |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
209 |
# with some cases of entity classes inheritance. |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
210 |
mixins.insert(0, cls.__bases__[0]) |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
211 |
mixins += cls.__bases__[1:] |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
212 |
cls.__bases__ = tuple(mixins) |
f91bd15f427c
fix nasty pluggable mixins bug, where mixins take precedence other user class
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3826
diff
changeset
|
213 |
cls.info('plugged %s mixins on %s', mixins, cls) |
1474 | 214 |
|
0 | 215 |
@classmethod |
216 |
def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X', |
|
217 |
settype=True, ordermethod='fetch_order'): |
|
218 |
"""return a rql to fetch all entities of the class type""" |
|
219 |
restrictions = restriction or [] |
|
220 |
if settype: |
|
221 |
restrictions.append('%s is %s' % (mainvar, cls.id)) |
|
222 |
if fetchattrs is None: |
|
223 |
fetchattrs = cls.fetch_attrs |
|
224 |
selection = [mainvar] |
|
225 |
orderby = [] |
|
226 |
# start from 26 to avoid possible conflicts with X |
|
227 |
varmaker = rqlvar_maker(index=26) |
|
228 |
cls._fetch_restrictions(mainvar, varmaker, fetchattrs, selection, |
|
229 |
orderby, restrictions, user, ordermethod) |
|
230 |
rql = 'Any %s' % ','.join(selection) |
|
231 |
if orderby: |
|
232 |
rql += ' ORDERBY %s' % ','.join(orderby) |
|
233 |
rql += ' WHERE %s' % ', '.join(restrictions) |
|
234 |
return rql |
|
1474 | 235 |
|
0 | 236 |
@classmethod |
237 |
def _fetch_restrictions(cls, mainvar, varmaker, fetchattrs, |
|
238 |
selection, orderby, restrictions, user, |
|
239 |
ordermethod='fetch_order', visited=None): |
|
240 |
eschema = cls.e_schema |
|
241 |
if visited is None: |
|
242 |
visited = set((eschema.type,)) |
|
243 |
elif eschema.type in visited: |
|
244 |
# avoid infinite recursion |
|
245 |
return |
|
246 |
else: |
|
247 |
visited.add(eschema.type) |
|
248 |
_fetchattrs = [] |
|
249 |
for attr in fetchattrs: |
|
250 |
try: |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3683
diff
changeset
|
251 |
rschema = eschema.subjrels[attr] |
0 | 252 |
except KeyError: |
253 |
cls.warning('skipping fetch_attr %s defined in %s (not found in schema)', |
|
254 |
attr, cls.id) |
|
255 |
continue |
|
256 |
if not user.matching_groups(rschema.get_groups('read')): |
|
257 |
continue |
|
258 |
var = varmaker.next() |
|
259 |
selection.append(var) |
|
260 |
restriction = '%s %s %s' % (mainvar, attr, var) |
|
261 |
restrictions.append(restriction) |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3683
diff
changeset
|
262 |
if not rschema.final: |
0 | 263 |
# XXX this does not handle several destination types |
264 |
desttype = rschema.objects(eschema.type)[0] |
|
265 |
card = rschema.rproperty(eschema, desttype, 'cardinality')[0] |
|
266 |
if card not in '?1': |
|
2928
edfdb69df6e8
oops, not self but cls here
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2877
diff
changeset
|
267 |
cls.warning('bad relation %s specified in fetch attrs for %s', |
edfdb69df6e8
oops, not self but cls here
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2877
diff
changeset
|
268 |
attr, cls) |
0 | 269 |
selection.pop() |
270 |
restrictions.pop() |
|
271 |
continue |
|
2871
83c5499e1436
[entity] fix fetch_rql w/ case where it's called while entity is not 'complete' (eg time where it's being added but have not yet all mandatory relations set)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2658
diff
changeset
|
272 |
# XXX we need outer join in case the relation is not mandatory |
83c5499e1436
[entity] fix fetch_rql w/ case where it's called while entity is not 'complete' (eg time where it's being added but have not yet all mandatory relations set)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2658
diff
changeset
|
273 |
# (card == '?') *or if the entity is being added*, since in |
83c5499e1436
[entity] fix fetch_rql w/ case where it's called while entity is not 'complete' (eg time where it's being added but have not yet all mandatory relations set)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2658
diff
changeset
|
274 |
# that case the relation may still be missing. As we miss this |
83c5499e1436
[entity] fix fetch_rql w/ case where it's called while entity is not 'complete' (eg time where it's being added but have not yet all mandatory relations set)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2658
diff
changeset
|
275 |
# later information here, systematically add it. |
83c5499e1436
[entity] fix fetch_rql w/ case where it's called while entity is not 'complete' (eg time where it's being added but have not yet all mandatory relations set)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2658
diff
changeset
|
276 |
restrictions[-1] += '?' |
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
277 |
destcls = cls.vreg['etypes'].etype_class(desttype) |
0 | 278 |
destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs, |
279 |
selection, orderby, restrictions, |
|
280 |
user, ordermethod, visited=visited) |
|
281 |
orderterm = getattr(cls, ordermethod)(attr, var) |
|
282 |
if orderterm: |
|
283 |
orderby.append(orderterm) |
|
284 |
return selection, orderby, restrictions |
|
285 |
||
1471
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
286 |
@classmethod |
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
287 |
@cached |
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
288 |
def parent_classes(cls): |
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
289 |
parents = [cls.vreg['etypes'].etype_class(e.type) |
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
290 |
for e in cls.e_schema.ancestors()] |
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
291 |
parents.append(cls.vreg['etypes'].etype_class('Any')) |
1471
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
292 |
return parents |
c889c3bcf5ec
new parent_classes method (cached)
sylvain.thenault@logilab.fr
parents:
1435
diff
changeset
|
293 |
|
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
294 |
@classmethod |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
295 |
@cached |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
296 |
def _rest_attr_info(cls): |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
297 |
mainattr, needcheck = 'eid', True |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
298 |
if cls.rest_attr: |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
299 |
mainattr = cls.rest_attr |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
300 |
needcheck = not cls.e_schema.has_unique_values(mainattr) |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
301 |
else: |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
302 |
for rschema in cls.e_schema.subject_relations(): |
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3683
diff
changeset
|
303 |
if rschema.final and rschema != 'eid' and cls.e_schema.has_unique_values(rschema): |
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
304 |
mainattr = str(rschema) |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
305 |
needcheck = False |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
306 |
break |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
307 |
if mainattr == 'eid': |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
308 |
needcheck = False |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
309 |
return mainattr, needcheck |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
310 |
|
1435
6cd6172718bb
allow to instantiate an entity without rset
sylvain.thenault@logilab.fr
parents:
1363
diff
changeset
|
311 |
def __init__(self, req, rset=None, row=None, col=0): |
2656
a93ae0f6c0ad
R [base classes] only AppObject remaning, no more AppRsetObject
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2650
diff
changeset
|
312 |
AppObject.__init__(self, req, rset, row, col) |
0 | 313 |
dict.__init__(self) |
314 |
self._related_cache = {} |
|
315 |
if rset is not None: |
|
316 |
self.eid = rset[row][col] |
|
317 |
else: |
|
318 |
self.eid = None |
|
319 |
self._is_saved = True |
|
1474 | 320 |
|
0 | 321 |
def __repr__(self): |
322 |
return '<Entity %s %s %s at %s>' % ( |
|
323 |
self.e_schema, self.eid, self.keys(), id(self)) |
|
324 |
||
325 |
def __nonzero__(self): |
|
326 |
return True |
|
327 |
||
328 |
def __hash__(self): |
|
329 |
return id(self) |
|
330 |
||
3047
ba86b1bdbcab
fix __cmp__ arguments
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3000
diff
changeset
|
331 |
def __cmp__(self, other): |
3000
4e76477949d5
B [entities] #343385 comparing entities should raise NotImplementedError
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2928
diff
changeset
|
332 |
raise NotImplementedError('comparison not implemented for %s' % self.__class__) |
4e76477949d5
B [entities] #343385 comparing entities should raise NotImplementedError
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
2928
diff
changeset
|
333 |
|
0 | 334 |
def pre_add_hook(self): |
335 |
"""hook called by the repository before doing anything to add the entity |
|
336 |
(before_add entity hooks have not been called yet). This give the |
|
337 |
occasion to do weird stuff such as autocast (File -> Image for instance). |
|
1474 | 338 |
|
0 | 339 |
This method must return the actual entity to be added. |
340 |
""" |
|
341 |
return self |
|
1474 | 342 |
|
0 | 343 |
def set_eid(self, eid): |
344 |
self.eid = self['eid'] = eid |
|
345 |
||
346 |
def has_eid(self): |
|
347 |
"""return True if the entity has an attributed eid (False |
|
348 |
meaning that the entity has to be created |
|
349 |
""" |
|
350 |
try: |
|
3664
af7ca3597b8d
use typed_eid
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3626
diff
changeset
|
351 |
typed_eid(self.eid) |
0 | 352 |
return True |
353 |
except (ValueError, TypeError): |
|
354 |
return False |
|
355 |
||
356 |
def is_saved(self): |
|
357 |
"""during entity creation, there is some time during which the entity |
|
358 |
has an eid attributed though it's not saved (eg during before_add_entity |
|
359 |
hooks). You can use this method to ensure the entity has an eid *and* is |
|
360 |
saved in its source. |
|
361 |
""" |
|
362 |
return self.has_eid() and self._is_saved |
|
1474 | 363 |
|
0 | 364 |
@cached |
365 |
def metainformation(self): |
|
366 |
res = dict(zip(('type', 'source', 'extid'), self.req.describe(self.eid))) |
|
367 |
res['source'] = self.req.source_defs()[res['source']] |
|
368 |
return res |
|
369 |
||
475
b32a5772ff06
should clear local perm cache if first attempt failed
sylvain.thenault@logilab.fr
parents:
413
diff
changeset
|
370 |
def clear_local_perm_cache(self, action): |
b32a5772ff06
should clear local perm cache if first attempt failed
sylvain.thenault@logilab.fr
parents:
413
diff
changeset
|
371 |
for rqlexpr in self.e_schema.get_rqlexprs(action): |
b32a5772ff06
should clear local perm cache if first attempt failed
sylvain.thenault@logilab.fr
parents:
413
diff
changeset
|
372 |
self.req.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None) |
b32a5772ff06
should clear local perm cache if first attempt failed
sylvain.thenault@logilab.fr
parents:
413
diff
changeset
|
373 |
|
0 | 374 |
def check_perm(self, action): |
375 |
self.e_schema.check_perm(self.req, action, self.eid) |
|
376 |
||
377 |
def has_perm(self, action): |
|
378 |
return self.e_schema.has_perm(self.req, action, self.eid) |
|
1474 | 379 |
|
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
380 |
def view(self, vid, __registry='views', **kwargs): |
0 | 381 |
"""shortcut to apply a view on this entity""" |
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
382 |
return self.vreg[__registry].render(vid, self.req, rset=self.rset, |
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
383 |
row=self.row, col=self.col, **kwargs) |
0 | 384 |
|
2059
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
385 |
def absolute_url(self, *args, **kwargs): |
0 | 386 |
"""return an absolute url to view this entity""" |
2059
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
387 |
# use *args since we don't want first argument to be "anonymous" to |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
388 |
# avoid potential clash with kwargs |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
389 |
if args: |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
390 |
assert len(args) == 1, 'only 0 or 1 non-named-argument expected' |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
391 |
method = args[0] |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
392 |
else: |
af33833d7571
absolute_url / build_url refactoring to avoid potential name clash
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2058
diff
changeset
|
393 |
method = None |
0 | 394 |
# in linksearch mode, we don't want external urls else selecting |
395 |
# the object for use in the relation is tricky |
|
396 |
# XXX search_state is web specific |
|
397 |
if getattr(self.req, 'search_state', ('normal',))[0] == 'normal': |
|
398 |
kwargs['base_url'] = self.metainformation()['source'].get('base-url') |
|
1904
e23536d29231
minor refactoring of absolute_url()
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
1842
diff
changeset
|
399 |
if method in (None, 'view'): |
1842
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
400 |
try: |
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
401 |
kwargs['_restpath'] = self.rest_path(kwargs.get('base_url')) |
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
402 |
except TypeError: |
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
403 |
warn('%s: rest_path() now take use_ext_eid argument, ' |
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
404 |
'please update' % self.id, DeprecationWarning) |
c7a22540d6f7
add some bw compat code for old rest_path prototype
sylvain.thenault@logilab.fr
parents:
1840
diff
changeset
|
405 |
kwargs['_restpath'] = self.rest_path() |
0 | 406 |
else: |
407 |
kwargs['rql'] = 'Any X WHERE X eid %s' % self.eid |
|
408 |
return self.build_url(method, **kwargs) |
|
409 |
||
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
410 |
def rest_path(self, use_ext_eid=False): |
0 | 411 |
"""returns a REST-like (relative) path for this entity""" |
412 |
mainattr, needcheck = self._rest_attr_info() |
|
413 |
etype = str(self.e_schema) |
|
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
414 |
path = etype.lower() |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
415 |
if mainattr != 'eid': |
0 | 416 |
value = getattr(self, mainattr) |
2125
19861294506f
https://www.logilab.net/cwo/ticket/343724
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2005
diff
changeset
|
417 |
if value is None or unicode(value) == u'': |
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
418 |
mainattr = 'eid' |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
419 |
path += '/eid' |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
420 |
elif needcheck: |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
421 |
# make sure url is not ambiguous |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
422 |
rql = 'Any COUNT(X) WHERE X is %s, X %s %%(value)s' % ( |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
423 |
etype, mainattr) |
1904
e23536d29231
minor refactoring of absolute_url()
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
1842
diff
changeset
|
424 |
nbresults = self.req.execute(rql, {'value' : value})[0][0] |
e23536d29231
minor refactoring of absolute_url()
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
1842
diff
changeset
|
425 |
if nbresults != 1: # ambiguity? |
e23536d29231
minor refactoring of absolute_url()
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
1842
diff
changeset
|
426 |
mainattr = 'eid' |
e23536d29231
minor refactoring of absolute_url()
Nicolas Chauvat <nicolas.chauvat@logilab.fr>
parents:
1842
diff
changeset
|
427 |
path += '/eid' |
0 | 428 |
if mainattr == 'eid': |
1840
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
429 |
if use_ext_eid: |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
430 |
value = self.metainformation()['extid'] |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
431 |
else: |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
432 |
value = self.eid |
f4b5c15d1147
test and fix #342997: local eid used for absolute_url of external entities
sylvain.thenault@logilab.fr
parents:
1804
diff
changeset
|
433 |
return '%s/%s' % (path, self.req.url_quote(value)) |
0 | 434 |
|
1360
13ae1121835e
rename attribute_metadata method to attr_metadata to save a few chars
sylvain.thenault@logilab.fr
parents:
1313
diff
changeset
|
435 |
def attr_metadata(self, attr, metadata): |
1101
0c067de38e46
unification of meta-attributes handling:
sylvain.thenault@logilab.fr
parents:
1098
diff
changeset
|
436 |
"""return a metadata for an attribute (None if unspecified)""" |
0c067de38e46
unification of meta-attributes handling:
sylvain.thenault@logilab.fr
parents:
1098
diff
changeset
|
437 |
value = getattr(self, '%s_%s' % (attr, metadata), None) |
0c067de38e46
unification of meta-attributes handling:
sylvain.thenault@logilab.fr
parents:
1098
diff
changeset
|
438 |
if value is None and metadata == 'encoding': |
0c067de38e46
unification of meta-attributes handling:
sylvain.thenault@logilab.fr
parents:
1098
diff
changeset
|
439 |
value = self.vreg.property_value('ui.encoding') |
0c067de38e46
unification of meta-attributes handling:
sylvain.thenault@logilab.fr
parents:
1098
diff
changeset
|
440 |
return value |
0 | 441 |
|
442 |
def printable_value(self, attr, value=_marker, attrtype=None, |
|
443 |
format='text/html', displaytime=True): |
|
444 |
"""return a displayable value (i.e. unicode string) which may contains |
|
445 |
html tags |
|
446 |
""" |
|
447 |
attr = str(attr) |
|
448 |
if value is _marker: |
|
449 |
value = getattr(self, attr) |
|
450 |
if isinstance(value, basestring): |
|
451 |
value = value.strip() |
|
452 |
if value is None or value == '': # don't use "not", 0 is an acceptable value |
|
453 |
return u'' |
|
454 |
if attrtype is None: |
|
455 |
attrtype = self.e_schema.destination(attr) |
|
456 |
props = self.e_schema.rproperties(attr) |
|
457 |
if attrtype == 'String': |
|
458 |
# internalinalized *and* formatted string such as schema |
|
459 |
# description... |
|
460 |
if props.get('internationalizable'): |
|
461 |
value = self.req._(value) |
|
1360
13ae1121835e
rename attribute_metadata method to attr_metadata to save a few chars
sylvain.thenault@logilab.fr
parents:
1313
diff
changeset
|
462 |
attrformat = self.attr_metadata(attr, 'format') |
0 | 463 |
if attrformat: |
464 |
return self.mtc_transform(value, attrformat, format, |
|
465 |
self.req.encoding) |
|
466 |
elif attrtype == 'Bytes': |
|
1360
13ae1121835e
rename attribute_metadata method to attr_metadata to save a few chars
sylvain.thenault@logilab.fr
parents:
1313
diff
changeset
|
467 |
attrformat = self.attr_metadata(attr, 'format') |
0 | 468 |
if attrformat: |
1360
13ae1121835e
rename attribute_metadata method to attr_metadata to save a few chars
sylvain.thenault@logilab.fr
parents:
1313
diff
changeset
|
469 |
encoding = self.attr_metadata(attr, 'encoding') |
0 | 470 |
return self.mtc_transform(value.getvalue(), attrformat, format, |
471 |
encoding) |
|
472 |
return u'' |
|
3212
07d11bacfefe
displaytime attribute should not have been removed from there
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3184
diff
changeset
|
473 |
value = printable_value(self.req, attrtype, value, props, |
07d11bacfefe
displaytime attribute should not have been removed from there
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3184
diff
changeset
|
474 |
displaytime=displaytime) |
0 | 475 |
if format == 'text/html': |
2312
af4d8f75c5db
use xml_escape
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2125
diff
changeset
|
476 |
value = xml_escape(value) |
0 | 477 |
return value |
478 |
||
479 |
def mtc_transform(self, data, format, target_format, encoding, |
|
480 |
_engine=ENGINE): |
|
481 |
trdata = TransformData(data, format, encoding, appobject=self) |
|
482 |
data = _engine.convert(trdata, target_format).decode() |
|
483 |
if format == 'text/html': |
|
1474 | 484 |
data = soup2xhtml(data, self.req.encoding) |
0 | 485 |
return data |
1474 | 486 |
|
0 | 487 |
# entity cloning ########################################################## |
488 |
||
489 |
def copy_relations(self, ceid): |
|
3626
017869a514c3
[doc] updated docstrings
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3552
diff
changeset
|
490 |
"""copy relations of the object with the given eid on this |
017869a514c3
[doc] updated docstrings
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3552
diff
changeset
|
491 |
object (this method is called on the newly created copy, and |
017869a514c3
[doc] updated docstrings
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3552
diff
changeset
|
492 |
ceid designates the original entity). |
0 | 493 |
|
494 |
By default meta and composite relations are skipped. |
|
495 |
Overrides this if you want another behaviour |
|
496 |
""" |
|
497 |
assert self.has_eid() |
|
498 |
execute = self.req.execute |
|
499 |
for rschema in self.e_schema.subject_relations(): |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3683
diff
changeset
|
500 |
if rschema.final or rschema.meta: |
0 | 501 |
continue |
502 |
# skip already defined relations |
|
503 |
if getattr(self, rschema.type): |
|
504 |
continue |
|
505 |
if rschema.type in self.skip_copy_for: |
|
506 |
continue |
|
507 |
# skip composite relation |
|
508 |
if self.e_schema.subjrproperty(rschema, 'composite'): |
|
509 |
continue |
|
510 |
# skip relation with card in ?1 else we either change the copied |
|
511 |
# object (inlined relation) or inserting some inconsistency |
|
512 |
if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1': |
|
513 |
continue |
|
514 |
rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % ( |
|
515 |
rschema.type, rschema.type) |
|
516 |
execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y')) |
|
517 |
self.clear_related_cache(rschema.type, 'subject') |
|
518 |
for rschema in self.e_schema.object_relations(): |
|
519 |
if rschema.meta: |
|
520 |
continue |
|
521 |
# skip already defined relations |
|
522 |
if getattr(self, 'reverse_%s' % rschema.type): |
|
523 |
continue |
|
524 |
# skip composite relation |
|
525 |
if self.e_schema.objrproperty(rschema, 'composite'): |
|
526 |
continue |
|
527 |
# skip relation with card in ?1 else we either change the copied |
|
528 |
# object (inlined relation) or inserting some inconsistency |
|
529 |
if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1': |
|
530 |
continue |
|
531 |
rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % ( |
|
532 |
rschema.type, rschema.type) |
|
533 |
execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y')) |
|
534 |
self.clear_related_cache(rschema.type, 'object') |
|
535 |
||
536 |
# data fetching methods ################################################### |
|
537 |
||
538 |
@cached |
|
539 |
def as_rset(self): |
|
540 |
"""returns a resultset containing `self` information""" |
|
541 |
rset = ResultSet([(self.eid,)], 'Any X WHERE X eid %(x)s', |
|
542 |
{'x': self.eid}, [(self.id,)]) |
|
543 |
return self.req.decorate_rset(rset) |
|
1474 | 544 |
|
0 | 545 |
def to_complete_relations(self): |
546 |
"""by default complete final relations to when calling .complete()""" |
|
547 |
for rschema in self.e_schema.subject_relations(): |
|
3689
deb13e88e037
follow yams 0.25 api changes to improve performance
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3683
diff
changeset
|
548 |
if rschema.final: |
0 | 549 |
continue |
550 |
if len(rschema.objects(self.e_schema)) > 1: |
|
551 |
# ambigous relations, the querier doesn't handle |
|
552 |
# outer join correctly in this case |
|
553 |
continue |
|
554 |
if rschema.inlined: |
|
555 |
matching_groups = self.req.user.matching_groups |
|
556 |
if matching_groups(rschema.get_groups('read')) and \ |
|
557 |
all(matching_groups(es.get_groups('read')) |
|
558 |
for es in rschema.objects(self.e_schema)): |
|
559 |
yield rschema, 'subject' |
|
1474 | 560 |
|
0 | 561 |
def to_complete_attributes(self, skip_bytes=True): |
562 |
for rschema, attrschema in self.e_schema.attribute_definitions(): |
|
563 |
# skip binary data by default |
|
564 |
if skip_bytes and attrschema.type == 'Bytes': |
|
565 |
continue |
|
566 |
attr = rschema.type |
|
567 |
if attr == 'eid': |
|
568 |
continue |
|
569 |
# password retreival is blocked at the repository server level |
|
570 |
if not self.req.user.matching_groups(rschema.get_groups('read')) \ |
|
571 |
or attrschema.type == 'Password': |
|
572 |
self[attr] = None |
|
573 |
continue |
|
574 |
yield attr |
|
1474 | 575 |
|
0 | 576 |
def complete(self, attributes=None, skip_bytes=True): |
577 |
"""complete this entity by adding missing attributes (i.e. query the |
|
578 |
repository to fill the entity) |
|
579 |
||
580 |
:type skip_bytes: bool |
|
581 |
:param skip_bytes: |
|
582 |
if true, attribute of type Bytes won't be considered |
|
583 |
""" |
|
584 |
assert self.has_eid() |
|
585 |
varmaker = rqlvar_maker() |
|
586 |
V = varmaker.next() |
|
587 |
rql = ['WHERE %s eid %%(x)s' % V] |
|
588 |
selected = [] |
|
589 |
for attr in (attributes or self.to_complete_attributes(skip_bytes)): |
|
590 |
# if attribute already in entity, nothing to do |
|
591 |
if self.has_key(attr): |
|
592 |
continue |
|
593 |
# case where attribute must be completed, but is not yet in entity |
|
594 |
var = varmaker.next() |
|
595 |
rql.append('%s %s %s' % (V, attr, var)) |
|
596 |
selected.append((attr, var)) |
|
597 |
# +1 since this doen't include the main variable |
|
598 |
lastattr = len(selected) + 1 |
|
599 |
if attributes is None: |
|
600 |
# fetch additional relations (restricted to 0..1 relations) |
|
601 |
for rschema, role in self.to_complete_relations(): |
|
602 |
rtype = rschema.type |
|
603 |
if self.relation_cached(rtype, role): |
|
604 |
continue |
|
605 |
var = varmaker.next() |
|
606 |
if role == 'subject': |
|
607 |
targettype = rschema.objects(self.e_schema)[0] |
|
608 |
card = rschema.rproperty(self.e_schema, targettype, |
|
609 |
'cardinality')[0] |
|
610 |
if card == '1': |
|
611 |
rql.append('%s %s %s' % (V, rtype, var)) |
|
612 |
else: # '?" |
|
613 |
rql.append('%s %s %s?' % (V, rtype, var)) |
|
614 |
else: |
|
615 |
targettype = rschema.subjects(self.e_schema)[1] |
|
616 |
card = rschema.rproperty(self.e_schema, targettype, |
|
617 |
'cardinality')[1] |
|
618 |
if card == '1': |
|
619 |
rql.append('%s %s %s' % (var, rtype, V)) |
|
620 |
else: # '?" |
|
621 |
rql.append('%s? %s %s' % (var, rtype, V)) |
|
622 |
assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype, |
|
623 |
role, card) |
|
624 |
selected.append(((rtype, role), var)) |
|
625 |
if selected: |
|
626 |
# select V, we need it as the left most selected variable |
|
627 |
# if some outer join are included to fetch inlined relations |
|
628 |
rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected), |
|
629 |
','.join(rql)) |
|
630 |
execute = getattr(self.req, 'unsafe_execute', self.req.execute) |
|
631 |
rset = execute(rql, {'x': self.eid}, 'x', build_descr=False)[0] |
|
632 |
# handle attributes |
|
633 |
for i in xrange(1, lastattr): |
|
634 |
self[str(selected[i-1][0])] = rset[i] |
|
635 |
# handle relations |
|
636 |
for i in xrange(lastattr, len(rset)): |
|
2941
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
637 |
rtype, role = selected[i-1][0] |
0 | 638 |
value = rset[i] |
639 |
if value is None: |
|
640 |
rrset = ResultSet([], rql, {'x': self.eid}) |
|
641 |
self.req.decorate_rset(rrset) |
|
642 |
else: |
|
643 |
rrset = self.req.eid_rset(value) |
|
2941
196493bd099c
[entity] use role instead of x
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2931
diff
changeset
|
644 |
self.set_related_cache(rtype, role, rrset) |
1474 | 645 |
|
0 | 646 |
def get_value(self, name): |
647 |
"""get value for the attribute relation <name>, query the repository |
|
648 |
to get the value if necessary. |
|
649 |
||
650 |
:type name: str |
|
651 |
:param name: name of the attribute to get |
|
652 |
""" |
|
653 |
try: |
|
654 |
value = self[name] |
|
655 |
except KeyError: |
|
656 |
if not self.is_saved(): |
|
657 |
return None |
|
658 |
rql = "Any A WHERE X eid %%(x)s, X %s A" % name |
|
2647
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
659 |
# XXX should we really use unsafe_execute here? I think so (syt), |
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
660 |
# see #344874 |
0 | 661 |
execute = getattr(self.req, 'unsafe_execute', self.req.execute) |
662 |
try: |
|
663 |
rset = execute(rql, {'x': self.eid}, 'x') |
|
664 |
except Unauthorized: |
|
665 |
self[name] = value = None |
|
666 |
else: |
|
667 |
assert rset.rowcount <= 1, (self, rql, rset.rowcount) |
|
668 |
try: |
|
669 |
self[name] = value = rset.rows[0][0] |
|
670 |
except IndexError: |
|
671 |
# probably a multisource error |
|
672 |
self.critical("can't get value for attribute %s of entity with eid %s", |
|
673 |
name, self.eid) |
|
674 |
if self.e_schema.destination(name) == 'String': |
|
2320 | 675 |
# XXX (syt) imo emtpy string is better |
0 | 676 |
self[name] = value = self.req._('unaccessible') |
677 |
else: |
|
678 |
self[name] = value = None |
|
679 |
return value |
|
680 |
||
681 |
def related(self, rtype, role='subject', limit=None, entities=False): |
|
682 |
"""returns a resultset of related entities |
|
1474 | 683 |
|
0 | 684 |
:param role: is the role played by 'self' in the relation ('subject' or 'object') |
685 |
:param limit: resultset's maximum size |
|
686 |
:param entities: if True, the entites are returned; if False, a result set is returned |
|
687 |
""" |
|
688 |
try: |
|
689 |
return self.related_cache(rtype, role, entities, limit) |
|
690 |
except KeyError: |
|
691 |
pass |
|
692 |
assert self.has_eid() |
|
693 |
rql = self.related_rql(rtype, role) |
|
2647
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
694 |
# XXX should we really use unsafe_execute here? I think so (syt), |
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
695 |
# see #344874 |
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
696 |
execute = getattr(self.req, 'unsafe_execute', self.req.execute) |
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
697 |
rset = execute(rql, {'x': self.eid}, 'x') |
0 | 698 |
self.set_related_cache(rtype, role, rset) |
699 |
return self.related(rtype, role, limit, entities) |
|
700 |
||
3130
2486163c4630
allow to specify targettypes in related_rql
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3047
diff
changeset
|
701 |
def related_rql(self, rtype, role='subject', targettypes=None): |
0 | 702 |
rschema = self.schema[rtype] |
703 |
if role == 'subject': |
|
3672
554a588ffaea
[entity] make related_rql honors its targettypes argument
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3671
diff
changeset
|
704 |
restriction = 'E eid %%(x)s, E %s X' % rtype |
3130
2486163c4630
allow to specify targettypes in related_rql
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3047
diff
changeset
|
705 |
if targettypes is None: |
2486163c4630
allow to specify targettypes in related_rql
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3047
diff
changeset
|
706 |
targettypes = rschema.objects(self.e_schema) |
3672
554a588ffaea
[entity] make related_rql honors its targettypes argument
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3671
diff
changeset
|
707 |
else: |
3683
2e4794c97cf4
fix related_rql when target_type specified
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3680
diff
changeset
|
708 |
restriction += ', X is IN (%s)' % ','.join(targettypes) |
0 | 709 |
card = greater_card(rschema, (self.e_schema,), targettypes, 0) |
710 |
else: |
|
3672
554a588ffaea
[entity] make related_rql honors its targettypes argument
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3671
diff
changeset
|
711 |
restriction = 'E eid %%(x)s, X %s E' % rtype |
3130
2486163c4630
allow to specify targettypes in related_rql
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3047
diff
changeset
|
712 |
if targettypes is None: |
2486163c4630
allow to specify targettypes in related_rql
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3047
diff
changeset
|
713 |
targettypes = rschema.subjects(self.e_schema) |
3672
554a588ffaea
[entity] make related_rql honors its targettypes argument
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3671
diff
changeset
|
714 |
else: |
3683
2e4794c97cf4
fix related_rql when target_type specified
Alexandre Fayolle <alexandre.fayolle@logilab.fr>
parents:
3680
diff
changeset
|
715 |
restriction += ', X is IN (%s)' % ','.join(targettypes) |
0 | 716 |
card = greater_card(rschema, targettypes, (self.e_schema,), 1) |
717 |
if len(targettypes) > 1: |
|
413
a7366dd3c33c
fix bug in entity.related_rql(): fetch_attr list / fetchorder weren't computed correctly
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
397
diff
changeset
|
718 |
fetchattrs_list = [] |
0 | 719 |
for ttype in targettypes: |
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
720 |
etypecls = self.vreg['etypes'].etype_class(ttype) |
413
a7366dd3c33c
fix bug in entity.related_rql(): fetch_attr list / fetchorder weren't computed correctly
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
397
diff
changeset
|
721 |
fetchattrs_list.append(set(etypecls.fetch_attrs)) |
a7366dd3c33c
fix bug in entity.related_rql(): fetch_attr list / fetchorder weren't computed correctly
Adrien Di Mascio <Adrien.DiMascio@logilab.fr>
parents:
397
diff
changeset
|
722 |
fetchattrs = reduce(set.intersection, fetchattrs_list) |
0 | 723 |
rql = etypecls.fetch_rql(self.req.user, [restriction], fetchattrs, |
724 |
settype=False) |
|
725 |
else: |
|
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
726 |
etypecls = self.vreg['etypes'].etype_class(targettypes[0]) |
0 | 727 |
rql = etypecls.fetch_rql(self.req.user, [restriction], settype=False) |
728 |
# optimisation: remove ORDERBY if cardinality is 1 or ? (though |
|
729 |
# greater_card return 1 for those both cases) |
|
730 |
if card == '1': |
|
731 |
if ' ORDERBY ' in rql: |
|
732 |
rql = '%s WHERE %s' % (rql.split(' ORDERBY ', 1)[0], |
|
733 |
rql.split(' WHERE ', 1)[1]) |
|
734 |
elif not ' ORDERBY ' in rql: |
|
735 |
args = tuple(rql.split(' WHERE ', 1)) |
|
736 |
rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % args |
|
737 |
return rql |
|
1474 | 738 |
|
0 | 739 |
# generic vocabulary methods ############################################## |
740 |
||
741 |
def unrelated_rql(self, rtype, targettype, role, ordermethod=None, |
|
742 |
vocabconstraints=True): |
|
743 |
"""build a rql to fetch `targettype` entities unrelated to this entity |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
744 |
using (rtype, role) relation. |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
745 |
|
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
746 |
Consider relation permissions so that returned entities may be actually |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
747 |
linked by `rtype`. |
0 | 748 |
""" |
749 |
ordermethod = ordermethod or 'fetch_unrelated_order' |
|
750 |
if isinstance(rtype, basestring): |
|
751 |
rtype = self.schema.rschema(rtype) |
|
752 |
if role == 'subject': |
|
753 |
evar, searchedvar = 'S', 'O' |
|
754 |
subjtype, objtype = self.e_schema, targettype |
|
755 |
else: |
|
756 |
searchedvar, evar = 'S', 'O' |
|
757 |
objtype, subjtype = self.e_schema, targettype |
|
758 |
if self.has_eid(): |
|
759 |
restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar] |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
760 |
args = {'x': self.eid} |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
761 |
if role == 'subject': |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
762 |
securitycheck_args = {'fromeid': self.eid} |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
763 |
else: |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
764 |
securitycheck_args = {'toeid': self.eid} |
0 | 765 |
else: |
766 |
restriction = [] |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
767 |
args = {} |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
768 |
securitycheck_args = {} |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
769 |
insertsecurity = (rtype.has_local_role('add') and not |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
770 |
rtype.has_perm(self.req, 'add', **securitycheck_args)) |
0 | 771 |
constraints = rtype.rproperty(subjtype, objtype, 'constraints') |
3961
d1cbf77db999
fix RQLUniqueConstraint behaviour by using a DISTINCT query and allowing
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3955
diff
changeset
|
772 |
# XXX consider constraint.mainvars to check if constraint apply |
0 | 773 |
if vocabconstraints: |
774 |
# RQLConstraint is a subclass for RQLVocabularyConstraint, so they |
|
775 |
# will be included as well |
|
776 |
restriction += [cstr.restriction for cstr in constraints |
|
777 |
if isinstance(cstr, RQLVocabularyConstraint)] |
|
778 |
else: |
|
779 |
restriction += [cstr.restriction for cstr in constraints |
|
780 |
if isinstance(cstr, RQLConstraint)] |
|
2650
18aec79ec3a3
R [vreg] important refactoring of the vregistry, moving behaviour to end dictionnary (and so leaving room for more flexibility ; keep bw compat ; update api usage in cw
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2647
diff
changeset
|
781 |
etypecls = self.vreg['etypes'].etype_class(targettype) |
0 | 782 |
rql = etypecls.fetch_rql(self.req.user, restriction, |
783 |
mainvar=searchedvar, ordermethod=ordermethod) |
|
784 |
# ensure we have an order defined |
|
785 |
if not ' ORDERBY ' in rql: |
|
786 |
before, after = rql.split(' WHERE ', 1) |
|
787 |
rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after) |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
788 |
if insertsecurity: |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
789 |
rqlexprs = rtype.get_rqlexprs('add') |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
790 |
rewriter = RQLRewriter(self.req) |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
791 |
rqlst = self.req.vreg.parse(self.req, rql, args) |
3826
0c0c051863cb
close #511810: bad rql generated when looking for vocabulary for a relation on an entity which doesn't exist (yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3746
diff
changeset
|
792 |
if not self.has_eid(): |
0c0c051863cb
close #511810: bad rql generated when looking for vocabulary for a relation on an entity which doesn't exist (yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3746
diff
changeset
|
793 |
existant = searchedvar |
0c0c051863cb
close #511810: bad rql generated when looking for vocabulary for a relation on an entity which doesn't exist (yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3746
diff
changeset
|
794 |
else: |
0c0c051863cb
close #511810: bad rql generated when looking for vocabulary for a relation on an entity which doesn't exist (yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3746
diff
changeset
|
795 |
existant = None # instead of 'SO', improve perfs |
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
796 |
for select in rqlst.children: |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
797 |
rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)], |
3826
0c0c051863cb
close #511810: bad rql generated when looking for vocabulary for a relation on an entity which doesn't exist (yet)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3746
diff
changeset
|
798 |
select.solutions, args, existant) |
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
799 |
rql = rqlst.as_string() |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
800 |
return rql, args |
1474 | 801 |
|
0 | 802 |
def unrelated(self, rtype, targettype, role='subject', limit=None, |
803 |
ordermethod=None): |
|
804 |
"""return a result set of target type objects that may be related |
|
805 |
by a given relation, with self as subject or object |
|
806 |
""" |
|
3241
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
807 |
try: |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
808 |
rql, args = self.unrelated_rql(rtype, targettype, role, ordermethod) |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
809 |
except Unauthorized: |
1a6f7a0e7dbd
unrelated_rql now considers relation's add perm
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3212
diff
changeset
|
810 |
return self.req.empty_rset() |
0 | 811 |
if limit is not None: |
812 |
before, after = rql.split(' WHERE ', 1) |
|
813 |
rql = '%s LIMIT %s WHERE %s' % (before, limit, after) |
|
3245 | 814 |
return self.req.execute(rql, args, tuple(args)) |
1474 | 815 |
|
0 | 816 |
# relations cache handling ################################################ |
1474 | 817 |
|
0 | 818 |
def relation_cached(self, rtype, role): |
819 |
"""return true if the given relation is already cached on the instance |
|
820 |
""" |
|
2647
b0a2e779845c
enable server side entity caching, 25% speedup on codenaf insertion. ALL CW TESTS OK
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2613
diff
changeset
|
821 |
return self._related_cache.get('%s_%s' % (rtype, role)) |
1474 | 822 |
|
0 | 823 |
def related_cache(self, rtype, role, entities=True, limit=None): |
824 |
"""return values for the given relation if it's cached on the instance, |
|
825 |
else raise `KeyError` |
|
826 |
""" |
|
827 |
res = self._related_cache['%s_%s' % (rtype, role)][entities] |
|
3680 | 828 |
if limit is not None and limit < len(res): |
0 | 829 |
if entities: |
830 |
res = res[:limit] |
|
831 |
else: |
|
832 |
res = res.limit(limit) |
|
833 |
return res |
|
1474 | 834 |
|
0 | 835 |
def set_related_cache(self, rtype, role, rset, col=0): |
836 |
"""set cached values for the given relation""" |
|
837 |
if rset: |
|
3552
8facb3324170
[entity] put cached related entities as tuple to be sure no one is modifying
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3546
diff
changeset
|
838 |
related = tuple(rset.entities(col)) |
0 | 839 |
rschema = self.schema.rschema(rtype) |
840 |
if role == 'subject': |
|
841 |
rcard = rschema.rproperty(self.e_schema, related[0].e_schema, |
|
842 |
'cardinality')[1] |
|
843 |
target = 'object' |
|
844 |
else: |
|
845 |
rcard = rschema.rproperty(related[0].e_schema, self.e_schema, |
|
846 |
'cardinality')[0] |
|
847 |
target = 'subject' |
|
848 |
if rcard in '?1': |
|
849 |
for rentity in related: |
|
3552
8facb3324170
[entity] put cached related entities as tuple to be sure no one is modifying
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3546
diff
changeset
|
850 |
rentity._related_cache['%s_%s' % (rtype, target)] = ( |
8facb3324170
[entity] put cached related entities as tuple to be sure no one is modifying
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3546
diff
changeset
|
851 |
self.as_rset(), (self,)) |
0 | 852 |
else: |
3552
8facb3324170
[entity] put cached related entities as tuple to be sure no one is modifying
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3546
diff
changeset
|
853 |
related = () |
0 | 854 |
self._related_cache['%s_%s' % (rtype, role)] = (rset, related) |
1474 | 855 |
|
0 | 856 |
def clear_related_cache(self, rtype=None, role=None): |
857 |
"""clear cached values for the given relation or the entire cache if |
|
858 |
no relation is given |
|
859 |
""" |
|
860 |
if rtype is None: |
|
861 |
self._related_cache = {} |
|
862 |
else: |
|
863 |
assert role |
|
864 |
self._related_cache.pop('%s_%s' % (rtype, role), None) |
|
1474 | 865 |
|
2942
77ebdbe93cf8
[entity] generic clear_all_caches methods, to use in test
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2941
diff
changeset
|
866 |
def clear_all_caches(self): |
3665
42f3a66cab51
fix clear_all_cache: ensure we don't remove eid from entity's dict
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3664
diff
changeset
|
867 |
haseid = 'eid' in self |
2942
77ebdbe93cf8
[entity] generic clear_all_caches methods, to use in test
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2941
diff
changeset
|
868 |
self.clear() |
77ebdbe93cf8
[entity] generic clear_all_caches methods, to use in test
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2941
diff
changeset
|
869 |
for rschema, _, role in self.e_schema.relation_definitions(): |
77ebdbe93cf8
[entity] generic clear_all_caches methods, to use in test
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2941
diff
changeset
|
870 |
self.clear_related_cache(rschema.type, role) |
3665
42f3a66cab51
fix clear_all_cache: ensure we don't remove eid from entity's dict
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3664
diff
changeset
|
871 |
# set eid if it was in, else we may get nasty error while editing this |
42f3a66cab51
fix clear_all_cache: ensure we don't remove eid from entity's dict
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3664
diff
changeset
|
872 |
# entity if it's bound to a repo session |
42f3a66cab51
fix clear_all_cache: ensure we don't remove eid from entity's dict
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3664
diff
changeset
|
873 |
if haseid: |
42f3a66cab51
fix clear_all_cache: ensure we don't remove eid from entity's dict
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3664
diff
changeset
|
874 |
self['eid'] = self.eid |
2942
77ebdbe93cf8
[entity] generic clear_all_caches methods, to use in test
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2941
diff
changeset
|
875 |
|
0 | 876 |
# raw edition utilities ################################################### |
1474 | 877 |
|
2877
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
878 |
def set_attributes(self, _cw_unsafe=False, **kwargs): |
0 | 879 |
assert kwargs |
880 |
relations = [] |
|
881 |
for key in kwargs: |
|
882 |
relations.append('X %s %%(%s)s' % (key, key)) |
|
397
cf577e26f924
don't introduce a spurious 'x' key in the entity-as dict
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
237
diff
changeset
|
883 |
# update current local object |
cf577e26f924
don't introduce a spurious 'x' key in the entity-as dict
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
237
diff
changeset
|
884 |
self.update(kwargs) |
cf577e26f924
don't introduce a spurious 'x' key in the entity-as dict
Aurelien Campeas <aurelien.campeas@logilab.fr>
parents:
237
diff
changeset
|
885 |
# and now update the database |
0 | 886 |
kwargs['x'] = self.eid |
2877
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
887 |
if _cw_unsafe: |
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
888 |
self.req.unsafe_execute( |
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
889 |
'SET %s WHERE X eid %%(x)s' % ','.join(relations), kwargs, 'x') |
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
890 |
else: |
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
891 |
self.req.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations), |
e469f3602858
new _cw_unsafe argument to Entity.set_attributes until something nicer is done (eg #344874)
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2875
diff
changeset
|
892 |
kwargs, 'x') |
1474 | 893 |
|
3671
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
894 |
def set_relations(self, _cw_unsafe=False, **kwargs): |
3955
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
895 |
"""add relations to the given object. To set a relation where this entity |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
896 |
is the object of the relation, use 'reverse_'<relation> as argument name. |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
897 |
|
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
898 |
Values may be an entity, a list of entity, or None (meaning that all |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
899 |
relations of the given type from or to this object should be deleted). |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
900 |
""" |
3671
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
901 |
if _cw_unsafe: |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
902 |
execute = self.req.unsafe_execute |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
903 |
else: |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
904 |
execute = self.req.execute |
3955
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
905 |
# XXX update cache |
3671
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
906 |
for attr, values in kwargs.iteritems(): |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
907 |
if attr.startswith('reverse_'): |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
908 |
restr = 'Y %s X' % attr[len('reverse_'):] |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
909 |
else: |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
910 |
restr = 'X %s Y' % attr |
3955
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
911 |
if values is None: |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
912 |
execute('DELETE %s WHERE X eid %%(x)s' % restr, |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
913 |
{'x': self.eid}, 'x') |
235a9fda6058
support x.set_relations(a_relation=None) to delete a_relation relations from this entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3919
diff
changeset
|
914 |
continue |
3746
74192424b2c8
accepts single values as create_entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3689
diff
changeset
|
915 |
if not isinstance(values, (tuple, list, set, frozenset)): |
74192424b2c8
accepts single values as create_entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3689
diff
changeset
|
916 |
values = (values,) |
3671
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
917 |
execute('SET %s WHERE X eid %%(x)s, Y eid IN (%s)' % ( |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
918 |
restr, ','.join(str(r.eid) for r in values)), |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
919 |
{'x': self.eid}, 'x') |
c765adac7f5c
new set_relations method on entity
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3665
diff
changeset
|
920 |
|
0 | 921 |
def delete(self): |
922 |
assert self.has_eid(), self.eid |
|
923 |
self.req.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema, |
|
924 |
{'x': self.eid}) |
|
1474 | 925 |
|
0 | 926 |
# server side utilities ################################################### |
1474 | 927 |
|
0 | 928 |
def set_defaults(self): |
929 |
"""set default values according to the schema""" |
|
930 |
self._default_set = set() |
|
931 |
for attr, value in self.e_schema.defaults(): |
|
932 |
if not self.has_key(attr): |
|
933 |
self[str(attr)] = value |
|
934 |
self._default_set.add(attr) |
|
935 |
||
936 |
def check(self, creation=False): |
|
937 |
"""check this entity against its schema. Only final relation |
|
938 |
are checked here, constraint on actual relations are checked in hooks |
|
939 |
""" |
|
940 |
# necessary since eid is handled specifically and yams require it to be |
|
941 |
# in the dictionary |
|
942 |
if self.req is None: |
|
943 |
_ = unicode |
|
944 |
else: |
|
945 |
_ = self.req._ |
|
946 |
self.e_schema.check(self, creation=creation, _=_) |
|
947 |
||
948 |
def fti_containers(self, _done=None): |
|
949 |
if _done is None: |
|
950 |
_done = set() |
|
951 |
_done.add(self.eid) |
|
952 |
containers = tuple(self.e_schema.fulltext_containers()) |
|
953 |
if containers: |
|
476
62968fa8845c
yield self if no ft container found
sylvain.thenault@logilab.fr
parents:
475
diff
changeset
|
954 |
yielded = False |
0 | 955 |
for rschema, target in containers: |
956 |
if target == 'object': |
|
957 |
targets = getattr(self, rschema.type) |
|
958 |
else: |
|
959 |
targets = getattr(self, 'reverse_%s' % rschema) |
|
960 |
for entity in targets: |
|
961 |
if entity.eid in _done: |
|
962 |
continue |
|
963 |
for container in entity.fti_containers(_done): |
|
964 |
yield container |
|
476
62968fa8845c
yield self if no ft container found
sylvain.thenault@logilab.fr
parents:
475
diff
changeset
|
965 |
yielded = True |
62968fa8845c
yield self if no ft container found
sylvain.thenault@logilab.fr
parents:
475
diff
changeset
|
966 |
if not yielded: |
62968fa8845c
yield self if no ft container found
sylvain.thenault@logilab.fr
parents:
475
diff
changeset
|
967 |
yield self |
0 | 968 |
else: |
969 |
yield self |
|
1474 | 970 |
|
0 | 971 |
def get_words(self): |
972 |
"""used by the full text indexer to get words to index |
|
973 |
||
974 |
this method should only be used on the repository side since it depends |
|
975 |
on the indexer package |
|
1474 | 976 |
|
0 | 977 |
:rtype: list |
978 |
:return: the list of indexable word of this entity |
|
979 |
""" |
|
980 |
from indexer.query_objects import tokenize |
|
3546
f0aecddf367e
fix bug when renaming a full text indexed attribute
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3316
diff
changeset
|
981 |
# take care to cases where we're modyfying the schema |
f0aecddf367e
fix bug when renaming a full text indexed attribute
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3316
diff
changeset
|
982 |
pending = self.req.transaction_data.setdefault('pendingrdefs', set()) |
0 | 983 |
words = [] |
984 |
for rschema in self.e_schema.indexable_attributes(): |
|
3546
f0aecddf367e
fix bug when renaming a full text indexed attribute
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3316
diff
changeset
|
985 |
if (self.e_schema, rschema) in pending: |
f0aecddf367e
fix bug when renaming a full text indexed attribute
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
3316
diff
changeset
|
986 |
continue |
0 | 987 |
try: |
988 |
value = self.printable_value(rschema, format='text/plain') |
|
1132 | 989 |
except TransformError: |
0 | 990 |
continue |
991 |
except: |
|
992 |
self.exception("can't add value of %s to text index for entity %s", |
|
993 |
rschema, self.eid) |
|
994 |
continue |
|
995 |
if value: |
|
996 |
words += tokenize(value) |
|
1474 | 997 |
|
0 | 998 |
for rschema, role in self.e_schema.fulltext_relations(): |
999 |
if role == 'subject': |
|
1000 |
for entity in getattr(self, rschema.type): |
|
1001 |
words += entity.get_words() |
|
1002 |
else: # if role == 'object': |
|
1003 |
for entity in getattr(self, 'reverse_%s' % rschema.type): |
|
1004 |
words += entity.get_words() |
|
1005 |
return words |
|
1006 |
||
3245 | 1007 |
@deprecated('[3.2] see new form api') |
1008 |
def vocabulary(self, rtype, role='subject', limit=None): |
|
1009 |
"""vocabulary functions must return a list of couples |
|
1010 |
(label, eid) that will typically be used to fill the |
|
1011 |
edition view's combobox. |
|
1012 |
||
1013 |
If `eid` is None in one of these couples, it should be |
|
1014 |
interpreted as a separator in case vocabulary results are grouped |
|
1015 |
""" |
|
1016 |
from logilab.common.testlib import mock_object |
|
1017 |
form = self.vreg.select('forms', 'edition', self.req, entity=self) |
|
1018 |
field = mock_object(name=rtype, role=role) |
|
1019 |
return form.form_field_vocabulary(field, limit) |
|
1020 |
||
0 | 1021 |
|
1022 |
# attribute and relation descriptors ########################################## |
|
1023 |
||
1024 |
class Attribute(object): |
|
1025 |
"""descriptor that controls schema attribute access""" |
|
1026 |
||
1027 |
def __init__(self, attrname): |
|
1028 |
assert attrname != 'eid' |
|
1029 |
self._attrname = attrname |
|
1030 |
||
1031 |
def __get__(self, eobj, eclass): |
|
1032 |
if eobj is None: |
|
1033 |
return self |
|
1034 |
return eobj.get_value(self._attrname) |
|
1035 |
||
1036 |
def __set__(self, eobj, value): |
|
1037 |
eobj[self._attrname] = value |
|
2875
b7399ef8b3e0
towards better edited attributes handling
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2872
diff
changeset
|
1038 |
if hasattr(eobj, 'edited_attributes'): |
b7399ef8b3e0
towards better edited attributes handling
Sylvain Thénault <sylvain.thenault@logilab.fr>
parents:
2872
diff
changeset
|
1039 |
eobj.edited_attributes.add(self._attrname) |
0 | 1040 |
|
1041 |
class Relation(object): |
|
1042 |
"""descriptor that controls schema relation access""" |
|
1043 |
_role = None # for pylint |
|
1044 |
||
1045 |
def __init__(self, rschema): |
|
1046 |
self._rschema = rschema |
|
1047 |
self._rtype = rschema.type |
|
1048 |
||
1049 |
def __get__(self, eobj, eclass): |
|
1050 |
if eobj is None: |
|
1051 |
raise AttributeError('%s cannot be only be accessed from instances' |
|
1052 |
% self._rtype) |
|
1053 |
return eobj.related(self._rtype, self._role, entities=True) |
|
1474 | 1054 |
|
0 | 1055 |
def __set__(self, eobj, value): |
1056 |
raise NotImplementedError |
|
1057 |
||
1058 |
||
1059 |
class SubjectRelation(Relation): |
|
1060 |
"""descriptor that controls schema relation access""" |
|
1061 |
_role = 'subject' |
|
1474 | 1062 |
|
0 | 1063 |
class ObjectRelation(Relation): |
1064 |
"""descriptor that controls schema relation access""" |
|
1065 |
_role = 'object' |
|
1066 |
||
1067 |
from logging import getLogger |
|
1068 |
from cubicweb import set_log_methods |
|
1069 |
set_log_methods(Entity, getLogger('cubicweb.entity')) |