From: Federico Caselli Date: Wed, 17 Apr 2024 20:17:25 +0000 (+0200) Subject: Improve docs formatting on automap, fix missing import X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82803016b5fcbc3225af87a43768dbea2be87582;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Improve docs formatting on automap, fix missing import Fixes: #11273 Change-Id: I872dcc0c2cf5093034e1590533b2e0d26602df7f References: #11267 --- diff --git a/lib/sqlalchemy/ext/automap.py b/lib/sqlalchemy/ext/automap.py index 5cee3c9644..71c434d20d 100644 --- a/lib/sqlalchemy/ext/automap.py +++ b/lib/sqlalchemy/ext/automap.py @@ -64,7 +64,7 @@ asking it to reflect the schema and produce mappings:: # collection-based relationships are by default named # "_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,19 +181,20 @@ 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) - The same named-classes are organized into a hierarchical collection available at :attr:`.AutomapBase.by_module`. This collection is traversed using the dot-separated name of a particular package/module down into the desired @@ -251,12 +256,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 +270,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 +281,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 +312,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 +443,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 +466,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 +507,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 +535,26 @@ 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')) + __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) + 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 @@ -564,15 +592,15 @@ We can resolve this conflict by using an underscore as follows:: local_table = local_cls.__table__ if name in local_table.columns: newname = name + "_" - warnings.warn( - "Already detected name %s present. using %s" % - (name, newname)) + warnings.warn("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,12 +609,13 @@ 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) + Base.prepare(autoload_with=engine) Using Automap with Explicit Declarations ======================================== @@ -603,26 +632,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 +683,8 @@ be applied as:: @event.listens_for(Base.metadata, "column_reflect") def column_reflect(inspector, table, column_info): # set column.key = "attr_" - 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)