📄 references.py
字号:
break return compare_columns(self.remote_key, local_variables) def get_where_for_local(self, other): """Generate a column comparison expression for reference properties. The returned expression may be used to find objects of the I{local} type referring to C{other}. It handles the following cases: Class.reference == obj Class.reference == obj.id Class.reference == (obj.id1, obj.id2) Where the right-hand side is the C{other} object given. """ try: obj_info = get_obj_info(other) except ClassInfoError: if type(other) is not tuple: remote_variables = (other,) else: remote_variables = other else: # Object may be security proxied or something, so # we get the real object here. # XXX UNTESTED! other = obj_info.get_obj() remote_variables = self.get_remote_variables(other) return compare_columns(self.local_key, remote_variables) def get_where_for_join(self): return compare_columns(self.local_key, self.remote_key) def get_local_variables(self, local): local_info = get_obj_info(local) return tuple(local_info.variables[column] for column in self._get_local_columns(local.__class__)) def get_remote_variables(self, remote): remote_info = get_obj_info(remote) return tuple(remote_info.variables[column] for column in self._get_remote_columns(remote.__class__)) def link(self, local, remote, setting=False): """Link objects to represent their relation. @param local: Object representing the I{local} side of the reference. @param remote: Object representing the I{remote} side of the reference, or the actual value to be set as the local key. @param setting: Pass true when the relationship is being newly created. """ local_info = get_obj_info(local) try: remote_info = get_obj_info(remote) except ClassInfoError: # Must be a plain key. Just set it. # XXX I guess this is broken if self.on_remote is True. local_variables = self.get_local_variables(local) if type(remote) is not tuple: remote = (remote,) assert len(remote) == len(local_variables) for variable, value in zip(local_variables, remote): variable.set(value) return local_store = Store.of(local) remote_store = Store.of(remote) if local_store is None: if remote_store is None: local_info.event.hook("added", self._add_all, local_info) remote_info.event.hook("added", self._add_all, local_info) else: remote_store.add(local) local_store = remote_store elif remote_store is None: local_store.add(remote) elif local_store is not remote_store: raise WrongStoreError("%r and %r cannot be linked because they " "are in different stores." % (local, remote)) # In cases below, we maintain a reference to the remote object # to make sure it won't get deallocated while the link is active. if self.many: relations = local_info.get(self) if relations is None: local_info[self] = {remote_info: remote} else: relations[remote_info] = remote else: old_remote = local_info.get(self) if old_remote is not None: self.unlink(local_info, get_obj_info(old_remote)) local_info[self] = remote if setting: local_vars = local_info.variables remote_vars = remote_info.variables pairs = zip(self._get_local_columns(local.__class__), self.remote_key) if self.on_remote: for local_column, remote_column in pairs: local_var = local_vars[local_column] if not local_var.is_defined(): track_changes = True else: remote_vars[remote_column].set(local_var.get()) if local_store is not None: local_store.add_flush_order(local, remote) local_info.event.hook("changed", self._track_local_changes, remote_info) local_info.event.hook("flushed", self._break_on_local_flushed, remote_info) #local_info.event.hook("removed", self._break_on_local_removed, # remote_info) else: for local_column, remote_column in pairs: remote_var = remote_vars[remote_column] if not remote_var.is_defined(): track_changes = True else: local_vars[local_column].set(remote_var.get()) if local_store is not None: local_store.add_flush_order(remote, local) remote_info.event.hook("changed", self._track_remote_changes, local_info) remote_info.event.hook("flushed", self._break_on_remote_flushed, local_info) #local_info.event.hook("removed", self._break_on_remote_removed, # local_info) local_info.event.hook("changed", self._break_on_local_diverged, remote_info) else: local_info.event.hook("changed", self._break_on_local_diverged, remote_info) remote_info.event.hook("changed", self._break_on_remote_diverged, local_info) def unlink(self, local_info, remote_info, setting=False): """Break the relation between the local and remote objects. @param setting: If true objects will be changed to persist breakage. """ unhook = False if self.many: relations = local_info.get(self) if relations is not None and remote_info in relations: relations.pop(remote_info, None) unhook = True elif local_info.pop(self, None) is not None: unhook = True if unhook: local_store = Store.of(local_info) local_info.event.unhook("changed", self._track_local_changes, remote_info) local_info.event.unhook("changed", self._break_on_local_diverged, remote_info) local_info.event.unhook("flushed", self._break_on_local_flushed, remote_info) remote_info.event.unhook("changed", self._track_remote_changes, local_info) remote_info.event.unhook("changed", self._break_on_remote_diverged, local_info) remote_info.event.unhook("flushed", self._break_on_remote_flushed, local_info) if local_store is None: if not self.many or not relations: local_info.event.unhook("added", self._add_all, local_info) remote_info.event.unhook("added", self._add_all, local_info) if local_store is not None: if self.on_remote: local_store.remove_flush_order(local_info, remote_info) else: local_store.remove_flush_order(remote_info, local_info) if setting: if self.on_remote: remote_vars = remote_info.variables for remote_column in self.remote_key: remote_vars[remote_column].set(None) else: local_vars = local_info.variables local_cols = self._get_local_columns(local_info.cls_info.cls) for local_column in local_cols: local_vars[local_column].set(None) def _track_local_changes(self, local_info, local_variable, old_value, new_value, fromdb, remote_info): """Deliver changes in local to remote. This hook ensures that the remote object will keep track of changes done in the local object, either manually or at flushing time. """ remote_column = self._get_remote_column(local_info.cls_info.cls, local_variable.column) if remote_column is not None: remote_info.variables[remote_column].set(new_value) def _track_remote_changes(self, remote_info, remote_variable, old_value, new_value, fromdb, local_info): """Deliver changes in remote to local. This hook ensures that the local object will keep track of changes done in the remote object, either manually or at flushing time. """ local_column = self._get_local_column(local_info.cls_info.cls, remote_variable.column) if local_column is not None: local_info.variables[local_column].set(new_value) def _break_on_local_diverged(self, local_info, local_variable, old_value, new_value, fromdb, remote_info): """Break the remote/local relationship on diverging changes. This hook ensures that if the local object has an attribute changed by hand in a way that diverges from the remote object, it stops tracking changes. """ remote_column = self._get_remote_column(local_info.cls_info.cls, local_variable.column) if remote_column is not None: variable = remote_info.variables[remote_column] if variable.get_lazy() is None and variable.get() != new_value: self.unlink(local_info, remote_info) def _break_on_remote_diverged(self, remote_info, remote_variable, old_value, new_value, fromdb, local_info): """Break the remote/local relationship on diverging changes. This hook ensures that if the remote object has an attribute changed by hand in a way that diverges from the local object, the relationship is undone. """ local_column = self._get_local_column(local_info.cls_info.cls, remote_variable.column) if local_column is not None: local_value = local_info.variables[local_column].get() if local_value != new_value: self.unlink(local_info, remote_info) def _break_on_local_flushed(self, local_info, remote_info): """Break the remote/local relationship on flush.""" self.unlink(local_info, remote_info) def _break_on_remote_flushed(self, remote_info, local_info): """Break the remote/local relationship on flush.""" self.unlink(local_info, remote_info) def _add_all(self, obj_info, local_info): store = Store.of(obj_info) store.add(local_info) local_info.event.unhook("added", self._add_all, local_info) def add(remote_info): remote_info.event.unhook("added", self._add_all, local_info) store.add(remote_info) if self.on_remote: store.add_flush_order(local_info, remote_info) else: store.add_flush_order(remote_info, local_info) if self.many: for remote_info in local_info[self]: add(remote_info) else: add(get_obj_info(local_info[self])) def _get_remote_columns(self, remote_cls): try: return self._remote_columns[remote_cls] except KeyError: columns = tuple(prop.__get__(None, remote_cls) for prop in self.remote_key) self._remote_columns[remote_cls] = columns return columns def _get_local_columns(self, local_cls): try: return self._local_columns[local_cls] except KeyError: columns = tuple(prop.__get__(None, local_cls) for prop in self.local_key) self._local_columns[local_cls] = columns return columns def _get_remote_column(self, local_cls, local_column): try: return self._l_to_r[local_cls].get(local_column) except KeyError: map = {} for local_prop, _remote_column in zip(self.local_key, self.remote_key): map[local_prop.__get__(None, local_cls)] = _remote_column return self._l_to_r.setdefault(local_cls, map).get(local_column) def _get_local_column(self, local_cls, remote_column): try: return self._r_to_l[local_cls].get(remote_column) except KeyError: map = {} for local_prop, _remote_column in zip(self.local_key, self.remote_key): map[_remote_column] = local_prop.__get__(None, local_cls) return self._r_to_l.setdefault(local_cls, map).get(remote_column)class PropertyResolver(object): """Transform strings and pure properties (non-columns) into columns.""" def __init__(self, reference, used_cls): self._reference = reference self._used_cls = used_cls self._registry = None self._namespace = None def resolve(self, properties): if not type(properties) is tuple: return (self.resolve_one(properties),) return tuple(self.resolve_one(property) for property in properties) def resolve_one(self, property): if type(property) is tuple: return self.resolve(property) elif isinstance(property, basestring): return self._resolve_string(property) elif not isinstance(property, Column): return _find_descriptor_obj(self._used_cls, property) return property def _resolve_string(self, property_path): if self._registry is None: try: registry = self._used_cls._storm_property_registry except AttributeError: raise RuntimeError("When using strings on references, " "classes involved must be subclasses " "of 'Storm'") cls = _find_descriptor_class(self._used_cls, self._reference) self._namespace = "%s.%s" % (cls.__module__, cls.__name__) return registry.get(property_path, self._namespace)def _find_descriptor_class(used_cls, descr): for cls in used_cls.__mro__: for attr, _descr in cls.__dict__.iteritems(): if _descr is descr: return cls raise RuntimeError("Reference used in an unknown class")def _find_descriptor_obj(used_cls, descr): for cls in used_cls.__mro__: for attr, _descr in cls.__dict__.iteritems(): if _descr is descr: return getattr(cls, attr) raise RuntimeError("Reference used in an unknown class")
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -