📄 schema.py
字号:
# schema.py# Copyright (C) 2005, 2006, 2007, 2008 Michael Bayer mike_mp@zzzcomputing.com## This module is part of SQLAlchemy and is released under# the MIT License: http://www.opensource.org/licenses/mit-license.php"""The schema module provides the building blocks for database metadata.Each element within this module describes a database entitywhich can be created and dropped, or is otherwise part of such an entity.Examples include tables, columns, sequences, and indexes.All entities are subclasses of [sqlalchemy.schema#SchemaItem], and asdefined in this module they are intended to be agnostic of anyvendor-specific constructs.A collection of entities are grouped into a unit called [sqlalchemy.schema#MetaData].MetaData serves as a logical grouping of schema elements, and can alsobe associated with an actual database connection such that operationsinvolving the contained elements can contact the database as needed.Two of the elements here also build upon their "syntactic" counterparts,which are defined in [sqlalchemy.sql.expression#], specifically [sqlalchemy.schema#Table]and [sqlalchemy.schema#Column]. Since these objects are part of theSQL expression language, they are usable as components in SQL expressions."""import re, inspectfrom sqlalchemy import types, exceptions, util, databasesfrom sqlalchemy.sql import expression, visitorsURL = None__all__ = ['SchemaItem', 'Table', 'Column', 'ForeignKey', 'Sequence', 'Index', 'ForeignKeyConstraint', 'PrimaryKeyConstraint', 'CheckConstraint', 'UniqueConstraint', 'DefaultGenerator', 'Constraint', 'MetaData', 'ThreadLocalMetaData', 'SchemaVisitor', 'PassiveDefault', 'ColumnDefault', 'DDL']class SchemaItem(object): """Base class for items that define a database schema.""" __metaclass__ = expression._FigureVisitName def _init_items(self, *args): """Initialize the list of child items for this SchemaItem.""" for item in args: if item is not None: item._set_parent(self) def _set_parent(self, parent): """Associate with this SchemaItem's parent object.""" raise NotImplementedError() def get_children(self, **kwargs): """used to allow SchemaVisitor access""" return [] def __repr__(self): return "%s()" % self.__class__.__name__ def bind(self): """Return the connectable associated with this SchemaItem.""" m = self.metadata return m and m.bind or None bind = property(bind) def info(self): try: return self._info except AttributeError: self._info = {} return self._info info = property(info)def _get_table_key(name, schema): if schema is None: return name else: return schema + "." + nameclass _TableSingleton(expression._FigureVisitName): """A metaclass used by the ``Table`` object to provide singleton behavior.""" def __call__(self, name, metadata, *args, **kwargs): schema = kwargs.get('schema', None) useexisting = kwargs.pop('useexisting', False) mustexist = kwargs.pop('mustexist', False) key = _get_table_key(name, schema) try: table = metadata.tables[key] if not useexisting and table._cant_override(*args, **kwargs): raise exceptions.InvalidRequestError("Table '%s' is already defined for this MetaData instance. Specify 'useexisting=True' to redefine options and columns on an existing Table object." % key) else: table._init_existing(*args, **kwargs) return table except KeyError: if mustexist: raise exceptions.InvalidRequestError("Table '%s' not defined" % (key)) try: return type.__call__(self, name, metadata, *args, **kwargs) except: if key in metadata.tables: del metadata.tables[key] raiseclass Table(SchemaItem, expression.TableClause): """Represent a relational database table.""" __metaclass__ = _TableSingleton ddl_events = ('before-create', 'after-create', 'before-drop', 'after-drop') def __init__(self, name, metadata, *args, **kwargs): """Construct a Table. Table objects can be constructed directly. Arguments are: name The name of this table, exactly as it appears, or will appear, in the database. This property, along with the *schema*, indicates the *singleton identity* of this table. Further tables constructed with the same name/schema combination will return the same Table instance. \*args Should contain a listing of the Column objects for this table. \**kwargs kwargs include: schema The *schema name* for this table, which is required if the table resides in a schema other than the default selected schema for the engine's database connection. Defaults to ``None``. autoload Defaults to False: the Columns for this table should be reflected from the database. Usually there will be no Column objects in the constructor if this property is set. autoload_with if autoload==True, this is an optional Engine or Connection instance to be used for the table reflection. If ``None``, the underlying MetaData's bound connectable will be used. include_columns A list of strings indicating a subset of columns to be loaded via the ``autoload`` operation; table columns who aren't present in this list will not be represented on the resulting ``Table`` object. Defaults to ``None`` which indicates all columns should be reflected. info Defaults to {}: A space to store application specific data; this must be a dictionary. mustexist Defaults to False: indicates that this Table must already have been defined elsewhere in the application, else an exception is raised. useexisting Defaults to False: indicates that if this Table was already defined elsewhere in the application, disregard the rest of the constructor arguments. owner Defaults to None: optional owning user of this table. useful for databases such as Oracle to aid in table reflection. quote Defaults to False: indicates that the Table identifier must be properly escaped and quoted before being sent to the database. This flag overrides all other quoting behavior. quote_schema Defaults to False: indicates that the Namespace identifier must be properly escaped and quoted before being sent to the database. This flag overrides all other quoting behavior. """ super(Table, self).__init__(name) self.metadata = metadata self.schema = kwargs.pop('schema', None) self.owner = kwargs.pop('owner', None) self.indexes = util.Set() self.constraints = util.Set() self._columns = expression.ColumnCollection() self.primary_key = PrimaryKeyConstraint() self._foreign_keys = util.OrderedSet() self.ddl_listeners = util.defaultdict(list) self.kwargs = {} if self.schema is not None: self.fullname = "%s.%s" % (self.schema, self.name) else: self.fullname = self.name autoload = kwargs.pop('autoload', False) autoload_with = kwargs.pop('autoload_with', None) include_columns = kwargs.pop('include_columns', None) self._set_parent(metadata) # load column definitions from the database if 'autoload' is defined # we do it after the table is in the singleton dictionary to support # circular foreign keys if autoload: if autoload_with: autoload_with.reflecttable(self, include_columns=include_columns) else: _bind_or_error(metadata).reflecttable(self, include_columns=include_columns) # initialize all the column, etc. objects. done after # reflection to allow user-overrides self.__post_init(*args, **kwargs) def _init_existing(self, *args, **kwargs): autoload = kwargs.pop('autoload', False) autoload_with = kwargs.pop('autoload_with', None) schema = kwargs.pop('schema', None) if schema and schema != self.schema: raise exceptions.ArgumentError("Can't change schema of existing table from '%s' to '%s'", (self.schema, schema)) owner = kwargs.pop('owner', None) if owner: if not self.owner: self.owner = owner elif owner != self.owner: raise exceptions.ArgumentError("Can't change owner of existing table from '%s' to '%s'", (self.owner, owner)) include_columns = kwargs.pop('include_columns', None) if include_columns: for c in self.c: if c.name not in include_columns: self.c.remove(c) self.__post_init(*args, **kwargs) def _cant_override(self, *args, **kwargs): """return True if the given arguments cannot be sent to a table that already exists. the 'useexisting' flag overrides this. """ return bool(args) or bool(util.Set(kwargs).difference(['autoload', 'autoload_with', 'schema', 'owner'])) def __post_init(self, *args, **kwargs): self.quote = kwargs.pop('quote', False) self.quote_schema = kwargs.pop('quote_schema', False) if kwargs.get('info'): self._info = kwargs.pop('info') # validate remaining kwargs that they all specify DB prefixes if len([k for k in kwargs if not re.match(r'^(?:%s)_' % '|'.join(databases.__all__), k)]): raise TypeError("Invalid argument(s) for Table: %s" % repr(kwargs.keys())) self.kwargs.update(kwargs) self._init_items(*args) def key(self): return _get_table_key(self.name, self.schema) key = property(key) def _export_columns(self, columns=None): # override FromClause's collection initialization logic; Table implements it differently pass def _set_primary_key(self, pk): if getattr(self, '_primary_key', None) in self.constraints: self.constraints.remove(self._primary_key) self._primary_key = pk self.constraints.add(pk) def primary_key(self): return self._primary_key primary_key = property(primary_key, _set_primary_key) def __repr__(self): return "Table(%s)" % ', '.join( [repr(self.name)] + [repr(self.metadata)] + [repr(x) for x in self.columns] + ["%s=%s" % (k, repr(getattr(self, k))) for k in ['schema']]) def __str__(self): return _get_table_key(self.description, self.schema) def append_column(self, column): """Append a ``Column`` to this ``Table``.""" column._set_parent(self) def append_constraint(self, constraint): """Append a ``Constraint`` to this ``Table``.""" constraint._set_parent(self) def append_ddl_listener(self, event, listener): """Append a DDL event listener to this ``Table``. The ``listener`` callable will be triggered when this ``Table`` is created or dropped, either directly before or after the DDL is issued to the database. The listener may modify the Table, but may not abort the event itself. Arguments are: event One of ``Table.ddl_events``; e.g. 'before-create', 'after-create', 'before-drop' or 'after-drop'. listener A callable, invoked with three positional arguments: event The event currently being handled schema_item The ``Table`` object being created or dropped bind The ``Connection`` bueing used for DDL execution. Listeners are added to the Table's ``ddl_listeners`` attribute. """ if event not in self.ddl_events: raise LookupError(event) self.ddl_listeners[event].append(listener) def _set_parent(self, metadata): metadata.tables[_get_table_key(self.name, self.schema)] = self self.metadata = metadata
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -