def get_obj_state(obj): if object_session(obj) is None and not has_identity(obj): return 'transient' elif object_session(obj) is not None and not has_identity(obj): return 'pending' elif object_session(obj) is None and has_identity(obj): return 'detached' elif object_session(obj) is not None and has_identity(obj): return 'persistent'
def delete_existing(self, obj, name): if has_identity(obj): sess = session(obj) items = getattr(obj, name, set([])) while items: item = items.pop() # only delete persistent objects if has_identity(item): sess.delete(item)
def get_object_state(self): if object_session(self) is None and not has_identity(self): return 'transient' elif object_session(self) is not None and not has_identity(self): return 'pending' elif object_session(self) is None and has_identity(self): return 'detached' elif object_session(self) is not None and has_identity(self): return 'persistent' raise Exception
def test_sql_session(self, rw_dir): """Test basic SQL handling""" session = PackageSession.new(url=self.temporary_sqlite_url(rw_dir)) assert len(list(session.query(SQLPackage))) == 0 now = datetime.now() # Setup a package pkg = SQLPackage(host='foo', root_path='path/foo', package_path='package_dir', managed_at=now, stable_since=now) assert len(pkg.transactions) == 0 session.add(pkg) session.commit() assert has_identity(pkg) assert session.to_sql_package(pkg) is pkg # Create a new package, for the fun of it dpkg = DummyPackage('hello/world', 'package') self.failUnlessRaises(ValueError, session.to_sql_package, dpkg) other_pkg = session.to_sql_package(dpkg, stable_since=10.0) assert other_pkg != pkg assert object_session(other_pkg) is session and not has_identity(other_pkg) # unmanage the package and verify it's not returned pkg.unmanage() self.failUnlessRaises(ValueError, session.to_sql_package, pkg) assert session.to_sql_package(pkg, managed_only=False) is pkg # Setup a simple transaction trans = SQLPackageTransaction(host='foo', type_name='testing', in_package=pkg, in_package_stable_since = pkg.stable_since, spooled_at=now) assert len(pkg.transactions) == 1 assert trans.in_package is pkg assert trans.out_package is None assert len(trans.files) == 0 session.add(trans) session.commit() # Add a transaction file file = SQLTransactionFile(transaction = trans, path='foo/bar', size=14, uid=2, gid=1, mode=0) assert len(trans.files) == 1 and trans.files[0] is file assert file.transaction is trans session.add(file) session.commit() # it will work indirectly as well trans.files.append(SQLTransactionFile(path='foo/bar2', size=14, uid=2, gid=1, mode=0)) session.commit()
def print_state(obj): from sqlalchemy.orm import object_session from sqlalchemy.orm.util import has_identity obj = obj if object_session(obj) is None and not has_identity(obj): print "transient:" if object_session(obj) is not None and not has_identity(obj): print "pending: " if object_session(obj) is None and has_identity(obj): print "# detached: " if object_session(obj) is not None and has_identity(obj): print "# persistent: " print type(obj)
def printstatus(obj): """ print an object's status regarding the sqla's session """ from sqlalchemy.orm import object_session from sqlalchemy.orm.util import has_identity if object_session(obj) is None and not has_identity(obj): print "Sqlalchemy status : transient" elif object_session(obj) is not None and not has_identity(obj): print "Sqlalchemy status : pending" elif object_session(obj) is None and has_identity(obj): print "Sqlalchemy status : detached" elif object_session(obj) is not None and has_identity(obj): print "Sqlalchemy status : persistent" else: print "Unknown Status"
def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, instance=None, nested=False, **kwargs): opts = self._meta exclude = list(opts.exclude) if opts.model is None: raise ValueError('ModelForm has no model class specified.') self.nested = nested if self.nested: exclude.extend(opts.exclude_on_nested) if instance is None: exclude.extend(opts.exclude_on_create) self.instance = opts.model() object_data = {} else: self.instance = instance object_data = model_to_dict(instance, opts.fields, exclude) if has_identity(instance): exclude.extend(opts.exclude_on_update) else: exclude.extend(opts.exclude_on_create) if initial is not None: object_data.update(initial) object_data.update(data) super(BaseSQLAModelForm, self).__init__(object_data, files, auto_id, prefix) for k in exclude: if k in self.fields: del self.fields[k]
def populate_obj(self, obj, name): if has_identity(obj): sess = session(obj) item = getattr(obj, name, None) if item: # only delete persistent objects if has_identity(item): sess.delete(item) elif item in sess: sess.expunge(item) setattr(obj, name, None) if self.data: setattr(obj, name, self.form.Meta.model()) FormField.populate_obj(self, obj, name)
def validate_login(self, _, value): assert not has_identity( self ), "user already in the database - cannot change login anymore!" if not User.login_regex.match(value) or value in self.blocked_logins: raise Exception("invalid unix-login!") return value
def refresh(self): """Refresh object and reconnect it with session.""" #: Detached if object_session(self) is None and has_identity(self): DatabaseManager.db().add(self) DatabaseManager.db().refresh(self) return self
def save(self, session=None, commit=True): """ Save the changes to the model. If the model has not been persisted then it adds the model to the declared session. Then it flushes the object session and optionally commits it. @param[in] session A specific session to use instead of the thread-local, scoped session. """ if has_identity(self): # Object has already been persisted to the database; grab its # session. session = object_session(self) else: # Ensure we have a database session. if session is None: session = __import__('alchemist.db').db.session # Object has not been persisted to the database. session.add(self) if commit: # Commit the session as requested. session.commit() else: # Just flush the session; do not commit. session.flush()
def update_from_SE(self, force=False, cache=timedelta(86400)): if ( has_identity(self) and self.user_id and not force and (datetime.utcnow() - self.updated) < cache ): return self user = requests.get( 'http://api.stackexchange.com/2.2/users/{}'.format(self.user_id), {'site': 'stackoverflow'} ).json()['items'][0] self.display_name = user['display_name'] self.reputation = user['reputation'] self.profile_image = user['profile_image'] self.user_type = user['user_type'] gold_badges = requests.get( 'http://api.stackexchange.com/2.2/users/{}/badges'.format(self.user_id), {'site': 'stackoverflow', 'max': 'gold', 'sort': 'rank'} ).json() self.gold_tag_badges = ' '.join( badge['name'] for badge in gold_badges['items'] if badge['badge_type'] == 'tag_based' ) self.updated = datetime.utcnow()
def serialize(self, fields=None, include=None, exclude=None): obj = {} if fields is None: fields = set(self.__class__.serializable) if include is not None: fields |= set(include) if exclude is not None: fields ^= set(exclude) for prop in fields: serializer_name = "serialize_" + prop if hasattr(self, serializer_name): obj[prop] = getattr(self, serializer_name)() else: is_transient = object_session(self) is None and not has_identity(self) if is_transient: # transiet instances are not tied to a session, # so we can't call getattr() because that can cause an attribute refresh, # which is a hard SQLAlchemy error v = self.__dict__.get(prop) else: v = getattr(self, prop) serializers = self.__serializable_args__["serializers"] opts = self.__serializable_args__["opts"] obj[prop] = serialize(v, serializers, opts) return obj
def __session(self): sess = object_session(self.instance) if sess is not None and self.autoflush and sess.autoflush and self.instance in sess: sess.flush() if not has_identity(self.instance): return None else: return sess
def cache_key(self): """ Instance property which returns the cache key for a single object """ self.validate_cache_mode('detail') if not has_identity(self): raise Exception('This instance has no identity') return self.build_cache_key('detail', **self.cache_data)
def clear(self, obj): """Delete this object from the session""" from sqlalchemy.orm.util import has_identity if obj not in self.session: # detached object; merge it with the one stored in session obj = self.session.merge(obj) if has_identity(obj): self.session.delete(obj)
def cache_detail_key(self, data=None): if not hasattr(self._meta, 'cache_detail_keys'): raise Exception('Class meta has no cache_detail_keys') if not has_identity(self): raise Exception('Cannot generate detail cache key for instance ' \ 'with no identity') data = data or instance_dict(self) raw_key, attrs = self._meta.cache_detail_keys[0] return self.format_key(raw_key % data)
def cache_list_version_key(self, data=None): if not self._meta.cache_list_keys: raise Exception('Class._meta has no cache_list_keys') if not has_identity(self): raise Exception('Cannot generate list cache key for instance ' \ 'with no identity') data = data or instance_dict(self) raw_key, attrs = self._meta.cache_list_keys[0] return raw_key % data
def make_asset_key(self, key, asset_type=None): """ Generates an asset key, which will be invalidated when the parent object is invalidated """ self.validate_cache_mode('asset') if not has_identity(self): raise Exception('This instance has no identity') return self.build_cache_key('asset', key, asset_type, **self.cache_data)
def _get_file_info(self, file_obj): try: file_obj = self.parser.parse(file_obj, Session) except Exception: if util.has_identity(file_obj): Session.delete(file_obj) else: Session.expunge(file_obj) log_traceback(level="info") return None return file_obj
def fetch(self, locale): session = sa.orm.object_session(self.obj) # If the object has no identity and its not in session or if the object # has _translations relationship loaded get the locale object from the # relationship. if '_translations' not in sa.inspect( self.obj).unloaded or (not session or not has_identity(self.obj)): return self.obj._translations.get(locale) return session.query(self.obj.__translatable__['class']).get( sa.inspect(self.obj).identity + (locale, ))
def set_password(self, value): is_new = has_identity(self) # Set password as a hash self._password = utils.generate_hash(value, hash='sha1') # (Re)generate API token and key after password is updated if is_new or not self.token: salt = (self.email or '') + self.salt + value self.token = utils.generate_random_hash(salt=salt, hash='sha256') if is_new or not self.key: salt = value + self.salt self.key = utils.generate_random_hash(salt=salt, hash='sha256')
def fetch(self, locale): session = sa.orm.object_session(self.obj) # If the object has no identity and its not in session or if the object # has _translations relationship loaded get the locale object from the # relationship. if '_translations' not in sa.inspect(self.obj).unloaded or ( not session or not has_identity(self.obj) ): return self.obj._translations.get(locale) return session.query(self.obj.__translatable__['class']).get( sa.inspect(self.obj).identity + (locale, ) )
def is_transient(self): """True if the allocation does not exist in the database, and is not about to be written to the database. If an allocation is transient it means that the given instance only exists in memory. See: http://www.sqlalchemy.org/docs/orm/session.html #quickie-intro-to-object-states http://stackoverflow.com/questions/3885601/ sqlalchemy-get-object-instance-state """ return object_session(self) is None and not has_identity(self)
def save(self, commit=False): """ Saves this ``form``'s cleaned_data into model instance ``self.instance``. If commit=True, then the changes to ``instance`` will be saved to the database. Returns ``instance``. """ if not has_identity(self.instance): fail_message = 'created' else: fail_message = 'changed' return save_instance(self, self.instance, self._meta.fields, fail_message, commit, self._meta.exclude)
def __init__(self, *args, **kwargs): instance = kwargs.pop('instance', None) nested = kwargs.pop('nested', False) super(BaseSQLAModelForm, self).__init__(*args, **kwargs) self.instance = instance if instance and has_identity(instance): excludes = self._meta.exclude_on_update[:] else: excludes = self._meta.exclude_on_create[:] if nested: excludes.extend(self._meta.exclude_on_nested) for k in excludes: if k in self.fields: del self.fields[k]
def __init__(self, *args, **kwargs): instance = kwargs.pop("instance", None) nested = kwargs.pop("nested", False) super(BaseSQLAModelForm, self).__init__(*args, **kwargs) self.instance = instance if instance and has_identity(instance): excludes = self._meta.exclude_on_update[:] else: excludes = self._meta.exclude_on_create[:] if nested: excludes.extend(self._meta.exclude_on_nested) for k in excludes: if k in self.fields: del self.fields[k]
def pyfa_abandon(obj): """ Remove object from database session. """ db_session = PyfaDataManager.session # If object isn't in session (transient or detached state), # do not do anything at all if db_session is None: return # Delete if persistent state if has_identity(obj): db_session.delete(obj) # Expunge if pending state else: db_session.expunge(obj)
def get_changes(self, ignore=[]): """ Returns a dict of pending changes to the current instance key: attribute name value: tuple in the form (old_value, new_value) """ insp = inspect(self) fields = self.get_checked_fields(ignore) changes = {} for field in fields: attr = getattr(insp.attrs, field) ins, eq, rm = attr.history if ins or rm: old_value = rm[0] if rm and has_identity(self) else None new_value = ins[0] if ins else None changes[field] = (old_value, new_value) return changes
def save(self, commit=False): """Save the changes to the model. If the model has not been persisted then it adds the model to the declared session. Then it flushes the object session and optionally commits it. """ if not has_identity(self): # Object has not been persisted to the database. session.add(self) if commit: # Commit the session as requested. session.commit() else: # Just flush the session; do not commit. session.flush()
def has_db_conflicts(self, instance, slug): """ Checks in the database for items with conflicting slugs """ session = object_session(instance) keys = self.comparison_keys['col_keys'][:] for rel_key, fk_keys in self.comparison_keys['rel_keys']: keys.extend(fk_keys) filters = {key: getattr(instance, key) for key in keys} filters[self.slug_key] = slug query = session.query(self.cls).filter_by(**filters) if has_identity(instance): # for existing objects, exclude the object from the search ident = instance.pk_as_query_filters(force=True) query = query.filter(not_(ident)) with session.no_autoflush: conflicts = query.count() return conflicts > 0
def is_active(self, value): """is_active property setter. If set to True - removes deactivated_at, deleted_at, activate_key and set activated_at to datetime now If set to False - set deactivated_at to now and activated_at to None """ # Allow to modify this only if object is in the persistent state to prevent "default values" errors/bugs. # http://stackoverflow.com/questions/3885601/sqlalchemy-get-object-instance-state if object_session(self) is not None and has_identity(self): if value: self.deactivated_at = None self.deleted_at = None self.activate_key = None self.activated_at = datetime.now() else: self.deactivated_at = datetime.now() self.activated_at = None else: raise AttributeError("User has to be in the persistent state - stored in the DB")
def validate_login(self, _, value): assert not has_identity( self ), "user already in the database - cannot change login anymore!" if not self.login_regex.match(value): raise IllegalLoginError( "Illegal login '{}': Logins must begin with a lower case " "letter and may be followed by lower case letters, digits or " "punctuation (dash, underscore and dot). Punctuation " "characters must be separated by at least on letter or digit.". format(value)) if value in self.blocked_logins: raise IllegalLoginError( "Illegal login '{}': This login is blocked and may not be used." .format(value)) if len(value) > self.login_character_limit: raise IllegalLoginError( "Illegal login '{}': Logins are limited to at most {} " "characters.".format(value, self.login_character_limit)) return value
def validate_login(self, _, value): assert not has_identity(self), "user already in the database - cannot change login anymore!" if not self.login_regex.match(value): raise IllegalLoginError( "Illegal login '{}': Logins must begin with a lower case " "letter and may be followed by lower case letters, digits or " "punctuation (dash, underscore and dot). Punctuation " "characters must be separated by at least on letter or digit." .format(value) ) if value in self.blocked_logins: raise IllegalLoginError( "Illegal login '{}': This login is blocked and may not be used." .format(value) ) if len(value) > self.login_character_limit: raise IllegalLoginError( "Illegal login '{}': Logins are limited to at most {} " "characters.".format(value, self.login_character_limit) ) return value
def is_active(self, value): """ Set user as active/inactive. :param bood value: True - removes deactivated_at, deleted_at, activate_key and set activated_at to datetime now False - set deactivated_at to now and activated_at to None """ # Allow to modify this only if object is in the persistent state to prevent "default values" errors/bugs. # http://stackoverflow.com/questions/3885601/sqlalchemy-get-object-instance-state if object_session(self) is not None and has_identity(self): if value: self.deactivated_at = None self.deleted_at = None self.activate_key = None self.activated_at = datetime.now() else: self.deactivated_at = datetime.now() self.activated_at = None else: raise AttributeError('User has to be in the persistent state - stored in the DB')
def get_cache_keys(self, child_updated=False): #print 'getting keys from', self cache_keys = set() version_keys = set() if not any(getattr(self._meta, k) for k in [ 'cache_detail_keys', 'cache_list_keys', 'cache_pointers', 'cache_cascades', 'cache_relations', ]): return cache_keys, version_keys session = Session.object_session(self) deleted = self.is_deleted or self in session.deleted data = instance_dict(self) cache = get_cache('objects') # get a list of all fields which changed changed_keys = [] for attr in self.__mapper__.iterate_properties: if not isinstance(attr, ColumnProperty) and \ attr.key not in self._meta.cache_relations: continue if attr.key in IGNORABLE_KEYS: continue ins, eq, rm = get_history(self, attr.key) if ins or rm: changed_keys.append(attr.key) self_updated = bool(changed_keys) or deleted if not self_updated and not child_updated: return (cache_keys, version_keys) if has_identity(self): # we only kill primary cache keys if the object exists # this key won't exist during CREATE for raw_key, attrs in self._meta.cache_detail_keys: if attrs and not any(key in changed_keys for key in attrs): # the fields which trigger this key were not changed continue cache_key = self.format_key(raw_key % data) cache_keys.add(cache_key) # collections will be altered by any action, so we always # kill these keys for raw_key, attrs in self._meta.cache_list_keys: if attrs and not any(key in changed_keys for key in attrs): # the fields which trigger this key were not changed continue cache_key = raw_key % data version_keys.add(cache_key) # pointer records contain only the id of the parent resource # if changed, we set the old key to False, and set the new key for raw_key, attrs, name in self._meta.cache_pointers: if attrs and not any(key in changed_keys for key in attrs): # the fields which trigger this key were not changed continue cache_key = raw_key % data c, idkey = identity_key(instance=self) if len(idkey) > 1: idkey = ','.join(str(i) for i in idkey) else: idkey = idkey[0] if not self.is_deleted: cache.set(cache_key, idkey) # if this is an existing object, we need to handle the old key if not has_identity(self): continue old_data = {} for attr in attrs: ins,eq,rm = get_history(self, attr) old_data[attr] = rm[0] if rm else eq[0] old_key = raw_key % old_data if old_key == cache_key and not self.is_deleted: continue old_idkey = cache.get(old_key) if old_idkey == idkey: # this object is the current owner of the key #print 'setting %s to False' % old_key, old_idkey, idkey cache.set(old_key, False) # cascade the cache kill operation to related objects, so parents # know if children have changed, in order to rebuild the cache for cascade in self._meta.cache_cascades: objs = getattr(self, cascade) if not objs: continue if not isinstance(objs, list): objs = [objs] for obj in objs: k1,k2 = obj.get_cache_keys(child_updated=True) cache_keys.update(k1) version_keys.update(k2) return (cache_keys, version_keys)
def delete(self): if has_identity(self): session = object_session(self) session.delete(self) session.commit()
def get_cache_keys(self, child_updated=False, force_expire_pointers=False, force=False): cache_alias = self._meta.cache_alias cache = self.get_cache() cache_keys = set() version_keys = set() if not self.is_cacheable: return (cache_keys, version_keys) orm = ORM.get() session = object_session(self) or orm.sessionmaker() deleted = self.is_deleted or self in session.deleted changes = self.get_changes(ignore=IGNORABLE_KEYS) self_updated = bool(changes) or deleted if not self_updated and not child_updated and not force: return (cache_keys, version_keys) changed_attrs = set(changes.keys()) data = self.cache_data old_data = {} if has_identity(self): for attr in self.cache_fields(): ins, eq, rm = get_history(self, attr) old_data[attr] = rm[0] if rm else eq[0] if 'detail' in self._meta.cache_modes: # we only kill primary cache keys if the object exists # this key won't exist during CREATE if has_identity(self): cache_key = self.cache_key cache_keys.add((cache_alias, cache_key)) if 'list' in self._meta.cache_modes: # collections will be altered by any action, so we always # kill these keys version_key = self.cache_list_version_key version_keys.add((cache_alias, version_key)) if self._meta.cache_partitions: # add the partition keys as well for pversion_key in self.cache_partition_version_keys: version_keys.add((cache_alias, pversion_key)) if changed_attrs.intersection(self._meta.cache_partitions): # if partition field values were changed, we need to # increment the version keys for the previous values for pversion_key in self.get_cache_partition_version_keys( **old_data): version_keys.add((cache_alias, pversion_key)) if 'asset' in self._meta.cache_modes: # models with sub-assets need to increment the version key # of the parent detail if has_identity(self): key = self.cache_detail_version_key if deleted: # delete the detail version key cache_keys.add((cache_alias, key)) else: # for updates, increment the version key version_keys.add((cache_alias, key)) # pointer records contain only the id of the parent resource # if changed, we set the old key to False, and set the new key for raw_key, attrs, name in self._meta.cache_pointers: if not changed_attrs.intersection( attrs) and not force_expire_pointers: # the fields which trigger this pointer were not changed continue cache_key = raw_key % data _, ident = identity_key(instance=self) if len(ident) > 1: ident = ','.join(map(str, ident)) else: ident = ident[0] if not self.is_deleted: cache.set(cache_key, ident) if force_expire_pointers: cache_keys.add((cache_alias, cache_key)) # if this is a new object, we're done if not has_identity(self): continue # if this is an existing object, we need to handle the old key old_data = {} for attr in attrs: ins, eq, rm = get_history(self, attr) old_data[attr] = rm[0] if rm else eq[0] old_key = raw_key % old_data if old_key == cache_key and not self.is_deleted: # the pointer key is unchanged, nothing to do here continue old_ident = cache.get(old_key) if old_ident and str(old_ident) == str(ident): # this object is the current owner of the key. we need to remove # the reference to this instance cache.set(old_key, False) # cascade the cache kill operation to related objects, so parents # know if children have changed, in order to rebuild the cache for cascade in self._meta.cache_cascades: objs = getattr(self, cascade) if not objs: # no related objects continue if not isinstance(objs, list): # *-to-one relation, force into a list objs = [objs] for obj in objs: child_keys = obj.get_cache_keys(child_updated=True) cache_keys.update(child_keys[0]) version_keys.update(child_keys[1]) return (cache_keys, version_keys)
def is_new(self): """Return True if the instance wasn't fetched from the database.""" return not has_identity(self)
def is_persisted(self): ''' Check if this instance has ever been persisted. Means it is either `detached` or `persistent`. ''' return has_identity(self)
def validate_login(self, _, value): assert not has_identity( self), "user already in the database - cannot change login anymore!" return super().validate_login(_, value)
def object_exists(self): return has_identity(self.object)