mirror of
https://gitlab.com/MoonTestUse1/AdministrationItDepartmens.git
synced 2025-08-14 00:25:46 +02:00
Проверка 09.02.2025
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
# ext/automap.py
|
||||
# Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
|
||||
# Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
|
||||
# <see AUTHORS file>
|
||||
#
|
||||
# This module is part of SQLAlchemy and is released under
|
||||
@@ -11,7 +11,7 @@ schema, typically though not necessarily one which is reflected.
|
||||
|
||||
It is hoped that the :class:`.AutomapBase` system provides a quick
|
||||
and modernized solution to the problem that the very famous
|
||||
`SQLSoup <https://sqlsoup.readthedocs.io/en/latest/>`_
|
||||
`SQLSoup <https://pypi.org/project/sqlsoup/>`_
|
||||
also tries to solve, that of generating a quick and rudimentary object
|
||||
model from an existing database on the fly. By addressing the issue strictly
|
||||
at the mapper configuration level, and integrating fully with existing
|
||||
@@ -64,7 +64,7 @@ asking it to reflect the schema and produce mappings::
|
||||
# collection-based relationships are by default named
|
||||
# "<classname>_collection"
|
||||
u1 = session.query(User).first()
|
||||
print (u1.address_collection)
|
||||
print(u1.address_collection)
|
||||
|
||||
Above, calling :meth:`.AutomapBase.prepare` while passing along the
|
||||
:paramref:`.AutomapBase.prepare.reflect` parameter indicates that the
|
||||
@@ -101,6 +101,7 @@ explicit table declaration::
|
||||
|
||||
from sqlalchemy import create_engine, MetaData, Table, Column, ForeignKey
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
engine = create_engine("sqlite:///mydatabase.db")
|
||||
|
||||
# produce our own MetaData object
|
||||
@@ -108,13 +109,15 @@ explicit table declaration::
|
||||
|
||||
# we can reflect it ourselves from a database, using options
|
||||
# such as 'only' to limit what tables we look at...
|
||||
metadata.reflect(engine, only=['user', 'address'])
|
||||
metadata.reflect(engine, only=["user", "address"])
|
||||
|
||||
# ... or just define our own Table objects with it (or combine both)
|
||||
Table('user_order', metadata,
|
||||
Column('id', Integer, primary_key=True),
|
||||
Column('user_id', ForeignKey('user.id'))
|
||||
)
|
||||
Table(
|
||||
"user_order",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True),
|
||||
Column("user_id", ForeignKey("user.id")),
|
||||
)
|
||||
|
||||
# we can then produce a set of mappings from this MetaData.
|
||||
Base = automap_base(metadata=metadata)
|
||||
@@ -123,8 +126,9 @@ explicit table declaration::
|
||||
Base.prepare()
|
||||
|
||||
# mapped classes are ready
|
||||
User, Address, Order = Base.classes.user, Base.classes.address,\
|
||||
Base.classes.user_order
|
||||
User = Base.classes.user
|
||||
Address = Base.classes.address
|
||||
Order = Base.classes.user_order
|
||||
|
||||
.. _automap_by_module:
|
||||
|
||||
@@ -177,18 +181,23 @@ the schema name ``default`` is used if no schema is present::
|
||||
|
||||
Base.metadata.create_all(e)
|
||||
|
||||
|
||||
def module_name_for_table(cls, tablename, table):
|
||||
if table.schema is not None:
|
||||
return f"mymodule.{table.schema}"
|
||||
else:
|
||||
return f"mymodule.default"
|
||||
|
||||
|
||||
Base = automap_base()
|
||||
|
||||
Base.prepare(e, modulename_for_table=module_name_for_table)
|
||||
Base.prepare(e, schema="test_schema", modulename_for_table=module_name_for_table)
|
||||
Base.prepare(e, schema="test_schema_2", modulename_for_table=module_name_for_table)
|
||||
|
||||
Base.prepare(
|
||||
e, schema="test_schema", modulename_for_table=module_name_for_table
|
||||
)
|
||||
Base.prepare(
|
||||
e, schema="test_schema_2", modulename_for_table=module_name_for_table
|
||||
)
|
||||
|
||||
The same named-classes are organized into a hierarchical collection available
|
||||
at :attr:`.AutomapBase.by_module`. This collection is traversed using the
|
||||
@@ -251,12 +260,13 @@ established based on the table name we use. If our schema contains tables
|
||||
# automap base
|
||||
Base = automap_base()
|
||||
|
||||
|
||||
# pre-declare User for the 'user' table
|
||||
class User(Base):
|
||||
__tablename__ = 'user'
|
||||
__tablename__ = "user"
|
||||
|
||||
# override schema elements like Columns
|
||||
user_name = Column('name', String)
|
||||
user_name = Column("name", String)
|
||||
|
||||
# override relationships too, if desired.
|
||||
# we must use the same name that automap would use for the
|
||||
@@ -264,6 +274,7 @@ established based on the table name we use. If our schema contains tables
|
||||
# generate for "address"
|
||||
address_collection = relationship("address", collection_class=set)
|
||||
|
||||
|
||||
# reflect
|
||||
engine = create_engine("sqlite:///mydatabase.db")
|
||||
Base.prepare(autoload_with=engine)
|
||||
@@ -274,11 +285,11 @@ established based on the table name we use. If our schema contains tables
|
||||
Address = Base.classes.address
|
||||
|
||||
u1 = session.query(User).first()
|
||||
print (u1.address_collection)
|
||||
print(u1.address_collection)
|
||||
|
||||
# the backref is still there:
|
||||
a1 = session.query(Address).first()
|
||||
print (a1.user)
|
||||
print(a1.user)
|
||||
|
||||
Above, one of the more intricate details is that we illustrated overriding
|
||||
one of the :func:`_orm.relationship` objects that automap would have created.
|
||||
@@ -305,35 +316,49 @@ scheme for class names and a "pluralizer" for collection names using the
|
||||
import re
|
||||
import inflect
|
||||
|
||||
|
||||
def camelize_classname(base, tablename, table):
|
||||
"Produce a 'camelized' class name, e.g. "
|
||||
"Produce a 'camelized' class name, e.g."
|
||||
"'words_and_underscores' -> 'WordsAndUnderscores'"
|
||||
|
||||
return str(tablename[0].upper() + \
|
||||
re.sub(r'_([a-z])', lambda m: m.group(1).upper(), tablename[1:]))
|
||||
return str(
|
||||
tablename[0].upper()
|
||||
+ re.sub(
|
||||
r"_([a-z])",
|
||||
lambda m: m.group(1).upper(),
|
||||
tablename[1:],
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
_pluralizer = inflect.engine()
|
||||
|
||||
|
||||
def pluralize_collection(base, local_cls, referred_cls, constraint):
|
||||
"Produce an 'uncamelized', 'pluralized' class name, e.g. "
|
||||
"Produce an 'uncamelized', 'pluralized' class name, e.g."
|
||||
"'SomeTerm' -> 'some_terms'"
|
||||
|
||||
referred_name = referred_cls.__name__
|
||||
uncamelized = re.sub(r'[A-Z]',
|
||||
lambda m: "_%s" % m.group(0).lower(),
|
||||
referred_name)[1:]
|
||||
uncamelized = re.sub(
|
||||
r"[A-Z]",
|
||||
lambda m: "_%s" % m.group(0).lower(),
|
||||
referred_name,
|
||||
)[1:]
|
||||
pluralized = _pluralizer.plural(uncamelized)
|
||||
return pluralized
|
||||
|
||||
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
|
||||
Base = automap_base()
|
||||
|
||||
engine = create_engine("sqlite:///mydatabase.db")
|
||||
|
||||
Base.prepare(autoload_with=engine,
|
||||
classname_for_table=camelize_classname,
|
||||
name_for_collection_relationship=pluralize_collection
|
||||
)
|
||||
Base.prepare(
|
||||
autoload_with=engine,
|
||||
classname_for_table=camelize_classname,
|
||||
name_for_collection_relationship=pluralize_collection,
|
||||
)
|
||||
|
||||
From the above mapping, we would now have classes ``User`` and ``Address``,
|
||||
where the collection from ``User`` to ``Address`` is called
|
||||
@@ -422,16 +447,21 @@ Below is an illustration of how to send
|
||||
options along to all one-to-many relationships::
|
||||
|
||||
from sqlalchemy.ext.automap import generate_relationship
|
||||
from sqlalchemy.orm import interfaces
|
||||
|
||||
def _gen_relationship(base, direction, return_fn,
|
||||
attrname, local_cls, referred_cls, **kw):
|
||||
|
||||
def _gen_relationship(
|
||||
base, direction, return_fn, attrname, local_cls, referred_cls, **kw
|
||||
):
|
||||
if direction is interfaces.ONETOMANY:
|
||||
kw['cascade'] = 'all, delete-orphan'
|
||||
kw['passive_deletes'] = True
|
||||
kw["cascade"] = "all, delete-orphan"
|
||||
kw["passive_deletes"] = True
|
||||
# make use of the built-in function to actually return
|
||||
# the result.
|
||||
return generate_relationship(base, direction, return_fn,
|
||||
attrname, local_cls, referred_cls, **kw)
|
||||
return generate_relationship(
|
||||
base, direction, return_fn, attrname, local_cls, referred_cls, **kw
|
||||
)
|
||||
|
||||
|
||||
from sqlalchemy.ext.automap import automap_base
|
||||
from sqlalchemy import create_engine
|
||||
@@ -440,8 +470,7 @@ options along to all one-to-many relationships::
|
||||
Base = automap_base()
|
||||
|
||||
engine = create_engine("sqlite:///mydatabase.db")
|
||||
Base.prepare(autoload_with=engine,
|
||||
generate_relationship=_gen_relationship)
|
||||
Base.prepare(autoload_with=engine, generate_relationship=_gen_relationship)
|
||||
|
||||
Many-to-Many relationships
|
||||
--------------------------
|
||||
@@ -482,18 +511,20 @@ two classes that are in an inheritance relationship. That is, with two
|
||||
classes given as follows::
|
||||
|
||||
class Employee(Base):
|
||||
__tablename__ = 'employee'
|
||||
__tablename__ = "employee"
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(String(50))
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'employee', 'polymorphic_on': type
|
||||
"polymorphic_identity": "employee",
|
||||
"polymorphic_on": type,
|
||||
}
|
||||
|
||||
|
||||
class Engineer(Employee):
|
||||
__tablename__ = 'engineer'
|
||||
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
|
||||
__tablename__ = "engineer"
|
||||
id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'engineer',
|
||||
"polymorphic_identity": "engineer",
|
||||
}
|
||||
|
||||
The foreign key from ``Engineer`` to ``Employee`` is used not for a
|
||||
@@ -508,25 +539,28 @@ we want as well as the ``inherit_condition``, as these are not things
|
||||
SQLAlchemy can guess::
|
||||
|
||||
class Employee(Base):
|
||||
__tablename__ = 'employee'
|
||||
__tablename__ = "employee"
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(String(50))
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'employee', 'polymorphic_on':type
|
||||
"polymorphic_identity": "employee",
|
||||
"polymorphic_on": type,
|
||||
}
|
||||
|
||||
class Engineer(Employee):
|
||||
__tablename__ = 'engineer'
|
||||
id = Column(Integer, ForeignKey('employee.id'), primary_key=True)
|
||||
favorite_employee_id = Column(Integer, ForeignKey('employee.id'))
|
||||
|
||||
favorite_employee = relationship(Employee,
|
||||
foreign_keys=favorite_employee_id)
|
||||
class Engineer(Employee):
|
||||
__tablename__ = "engineer"
|
||||
id = Column(Integer, ForeignKey("employee.id"), primary_key=True)
|
||||
favorite_employee_id = Column(Integer, ForeignKey("employee.id"))
|
||||
|
||||
favorite_employee = relationship(
|
||||
Employee, foreign_keys=favorite_employee_id
|
||||
)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity':'engineer',
|
||||
'inherit_condition': id == Employee.id
|
||||
"polymorphic_identity": "engineer",
|
||||
"inherit_condition": id == Employee.id,
|
||||
}
|
||||
|
||||
Handling Simple Naming Conflicts
|
||||
@@ -559,20 +593,24 @@ and will emit an error on mapping.
|
||||
|
||||
We can resolve this conflict by using an underscore as follows::
|
||||
|
||||
def name_for_scalar_relationship(base, local_cls, referred_cls, constraint):
|
||||
def name_for_scalar_relationship(
|
||||
base, local_cls, referred_cls, constraint
|
||||
):
|
||||
name = referred_cls.__name__.lower()
|
||||
local_table = local_cls.__table__
|
||||
if name in local_table.columns:
|
||||
newname = name + "_"
|
||||
warnings.warn(
|
||||
"Already detected name %s present. using %s" %
|
||||
(name, newname))
|
||||
"Already detected name %s present. using %s" % (name, newname)
|
||||
)
|
||||
return newname
|
||||
return name
|
||||
|
||||
|
||||
Base.prepare(autoload_with=engine,
|
||||
name_for_scalar_relationship=name_for_scalar_relationship)
|
||||
Base.prepare(
|
||||
autoload_with=engine,
|
||||
name_for_scalar_relationship=name_for_scalar_relationship,
|
||||
)
|
||||
|
||||
Alternatively, we can change the name on the column side. The columns
|
||||
that are mapped can be modified using the technique described at
|
||||
@@ -581,13 +619,14 @@ to a new name::
|
||||
|
||||
Base = automap_base()
|
||||
|
||||
|
||||
class TableB(Base):
|
||||
__tablename__ = 'table_b'
|
||||
_table_a = Column('table_a', ForeignKey('table_a.id'))
|
||||
__tablename__ = "table_b"
|
||||
_table_a = Column("table_a", ForeignKey("table_a.id"))
|
||||
|
||||
|
||||
Base.prepare(autoload_with=engine)
|
||||
|
||||
|
||||
Using Automap with Explicit Declarations
|
||||
========================================
|
||||
|
||||
@@ -603,26 +642,29 @@ defines table metadata::
|
||||
|
||||
Base = automap_base()
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = 'user'
|
||||
__tablename__ = "user"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
name = Column(String)
|
||||
|
||||
|
||||
class Address(Base):
|
||||
__tablename__ = 'address'
|
||||
__tablename__ = "address"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
email = Column(String)
|
||||
user_id = Column(ForeignKey('user.id'))
|
||||
user_id = Column(ForeignKey("user.id"))
|
||||
|
||||
|
||||
# produce relationships
|
||||
Base.prepare()
|
||||
|
||||
# mapping is complete, with "address_collection" and
|
||||
# "user" relationships
|
||||
a1 = Address(email='u1')
|
||||
a2 = Address(email='u2')
|
||||
a1 = Address(email="u1")
|
||||
a2 = Address(email="u2")
|
||||
u1 = User(address_collection=[a1, a2])
|
||||
assert a1.user is u1
|
||||
|
||||
@@ -651,7 +693,8 @@ be applied as::
|
||||
@event.listens_for(Base.metadata, "column_reflect")
|
||||
def column_reflect(inspector, table, column_info):
|
||||
# set column.key = "attr_<lower_case_name>"
|
||||
column_info['key'] = "attr_%s" % column_info['name'].lower()
|
||||
column_info["key"] = "attr_%s" % column_info["name"].lower()
|
||||
|
||||
|
||||
# run reflection
|
||||
Base.prepare(autoload_with=engine)
|
||||
@@ -1002,6 +1045,12 @@ class AutomapBase:
|
||||
|
||||
User, Address = Base.classes.User, Base.classes.Address
|
||||
|
||||
For class names that overlap with a method name of
|
||||
:class:`.util.Properties`, such as ``items()``, the getitem form
|
||||
is also supported::
|
||||
|
||||
Item = Base.classes["items"]
|
||||
|
||||
"""
|
||||
|
||||
by_module: ClassVar[ByModuleProperties]
|
||||
|
Reference in New Issue
Block a user