Simon, thank you for your kind help.
working excerpt:
Base = declarative_base()
engine = create_engine(app.config.get('SQLALCHEMY_DATABASE_URI'))
metadata = MetaData(bind=engine)
Base.metadata = metadata
# load tables
metadata.reflect(autoload=True)
# load view, as standard reflection ignores views for whatever reason
web_view = Table("web_view", metadata, Column("NODE_ID", Integer,
primary_key=True), autoload=True)
# automap
Base = automap_base(metadata=metadata)
Base.prepare()
# set up relations
Nodes = Base.classes.nodes
Attributes = Base.classes.attribs
Entries = Base.classes.entries
WebView = Base.classes.web_view
print colored("# Mapped SQLAlchemy classes #", 'yellow')
print colored(','.join(Base.classes.keys()), 'green')
print colored("# ------------------------- #", 'yellow')
Session = sessionmaker(bind=engine)
session = Session()
On Thursday, February 14, 2019 at 10:59:19 AM UTC+1, Simon King wrote:
>
> There are a few different points to make here:
>
> 1. SQLAlchemy requires mapped classes to have a primary key defined.
> This is because an instance of the mapped class corresponds to a row
> in the database. In order to make changes to that row (UPDATE, DELETE
> etc.), SQLAlchemy needs to be able to target it uniquely, so it needs
> a set of values that uniquely identify a row. Normally that's the
> primary key, although you can use any combination of columns you like
> as long as they guarantee uniqueness.
>
> 2. View reflection doesn't pick up primary keys, so if you want to map
> a class to a view, you need to explicitly tell SQLAlchemy which
> columns you want to treat as primary keys.
>
> 3. If you want to alter the definition of a Table object inside a
> Metadata, you have to use the extend_existing flag. This is an
> "explicit vs implicit" design decision.
>
> Now that you've given your Table a primary key, and automap has mapped
> a class to it, I think you should be fine.
>
> Simon
>
> On Thu, Feb 14, 2019 at 8:28 AM <[email protected] <javascript:>>
> wrote:
> >
> > i fiddled around with this. here are some interessting perceptions.
> >
> > defining the primary_key manually yields this, which is totally
> irrational to me. the missing view that i am after is already defined
> somewhere? pffff...
> >
> > >>> my_view = Table("web_view", metadata, Column("NODE_ID", Integer,
> primary_key=True), autoload=True)
> > Traceback (most recent call last):
> > File "<stdin>", line 1, in <module>
> > File "/usr/lib64/python2.7/site-packages/sqlalchemy/sql/schema.py",
> line 439, in __new__
> > "existing Table object." % key)
> > sqlalchemy.exc.InvalidRequestError: Table 'web_view' is already defined
> for this MetaData instance. Specify 'extend_existing=True' to redefine
> options and columns on an existing Table object.
> >
> > so i did what it requires (extend_existing=True)...it seems it gets
> imported
> >
> > >>> my_view = Table("web_view", metadata, Column("NODE_ID", Integer,
> primary_key=True), autoload=True, extend_existing=True)
> > >>> my_view
> > Table('web_view',
> MetaData(bind=Engine(mysql://aix_reg:***@localhost/aix_registry)),
> Column('NODE_ID', Integer(), table=<web_view>, primary_key=True,
> nullable=False), Column('NODE', VARCHAR(length=256), table=<web_view>),
> Column('LOCATION', VARCHAR(length=256), table=<web_view>),
> Column('IS_CLUSTER', VARCHAR(length=256), table=<web_view>),
> Column('MAN_SYS_NAME', VARCHAR(length=256), table=<web_view>),
> Column('HAS_CICS', VARCHAR(length=256), table=<web_view>),
> Column('OS_LEVEL', VARCHAR(length=256), table=<web_view>),
> Column('HAS_COBOL', VARCHAR(length=256), table=<web_view>),
> Column('HAS_ORACLE', VARCHAR(length=256), table=<web_view>),
> Column('MEM_VALUE', VARCHAR(length=256), table=<web_view>),
> Column('HAS_TUXEDO', VARCHAR(length=256), table=<web_view>),
> Column('STORAGE_VALUE', VARCHAR(length=256), table=<web_view>),
> Column('IS_LPM', VARCHAR(length=256), table=<web_view>), Column('HAS_SAP',
> VARCHAR(length=256), table=<web_view>), Column('CPU_VALUE',
> VARCHAR(length=256), table=<web_view>), Column('CLUSTER_RGS',
> VARCHAR(length=256), table=<web_view>), Column('HA_LEVEL',
> VARCHAR(length=256), table=<web_view>), Column('CLUSTER_NODES',
> VARCHAR(length=256), table=<web_view>), Column('CLUSTER_NAME',
> VARCHAR(length=256), table=<web_view>), Column('CPU_POOL',
> VARCHAR(length=256), table=<web_view>), Column('AME_FACTOR',
> VARCHAR(length=256), table=<web_view>), Column('SYS_PROFILE',
> VARCHAR(length=256), table=<web_view>), Column('INFO_MAIL',
> VARCHAR(length=256), table=<web_view>), Column('HOSTNAME',
> VARCHAR(length=256), table=<web_view>), Column('OS_TYPE',
> VARCHAR(length=256), table=<web_view>), Column('Java6_64',
> VARCHAR(length=256), table=<web_view>), Column('Java7_64',
> VARCHAR(length=256), table=<web_view>), Column('Java8_64',
> VARCHAR(length=256), table=<web_view>), Column('Java5_64',
> VARCHAR(length=256), table=<web_view>), Column('Java8',
> VARCHAR(length=256), table=<web_view>), Column('Java5',
> VARCHAR(length=256), table=<web_view>), Column('Java14',
> VARCHAR(length=256), table=<web_view>), Column('Java7',
> VARCHAR(length=256), table=<web_view>), Column('Java71',
> VARCHAR(length=256), table=<web_view>), Column('Java71_64',
> VARCHAR(length=256), table=<web_view>), Column('Java14_64',
> VARCHAR(length=256), table=<web_view>), Column('Java6',
> VARCHAR(length=256), table=<web_view>), Column('UPTIME',
> VARCHAR(length=256), table=<web_view>), Column('IP', VARCHAR(length=256),
> table=<web_view>), Column('IP_LONG', VARCHAR(length=256),
> table=<web_view>), Column('CLUSTER_NODENAME', VARCHAR(length=256),
> table=<web_view>), Column('RG_SERVICE_IP_LONG', VARCHAR(length=256),
> table=<web_view>), Column('HAS_SNA', VARCHAR(length=256),
> table=<web_view>), schema=None)
> >
> > and the view apears as a class...
> >
> > >>> from sqlalchemy.ext.automap import automap_base
> > >>> Base = automap_base(metadata=metadata)
> > >>> Base.prepare()
> > >>> print(Base.classes.keys())
> > ['attribs', 'dyn_table', 'web_view', 'nodes', 'entries']
> >
> >
> > i have grave doubts that this is intended behaviour. not sure how to
> proceed from here.
> >
> > On Thursday, February 14, 2019 at 8:54:03 AM UTC+1, [email protected]
> wrote:
> >>
> >>
> >> maybe a "primary key" issue?
> >>
> >> from the docs:
> >>
> >> sually, it’s desired to have at least a primary key constraint when
> reflecting a view, if not foreign keys as well. View reflection doesn’t
> extrapolate these constraints.
> >>
> >> Use the “override” technique for this, specifying explicitly those
> columns which are part of the primary key or have foreign key constraints:
> >>
> >>
> >>
> >> my_view = Table("some_view", metadata,
> >> Column("view_id", Integer, primary_key=True),
> >> Column("related_thing", Integer,
> ForeignKey("othertable.thing_id")),
> >> autoload=True
> >> )
> >>
> >>
> >>
> >>
> >> On Thursday, February 14, 2019 at 8:48:40 AM UTC+1, [email protected]
> wrote:
> >>>
> >>> ok, so we are back to the central question. the output below
> indictates that only "tables" are reflected and "views" are ignored. i
> like to think i am not the first who wants to use mysql views with sqla?
> is this really such a difficult task?
> >>>
> >>>
> >>> >>> print(Base.classes.keys())
> >>> ['attribs', 'dyn_table', 'nodes', 'entries']
> >>>
> >>>
> >>>
> >>>
> >>>
> >>> On Wednesday, February 13, 2019 at 4:35:22 PM UTC+1, Simon King wrote:
> >>>>
> >>>> On Wed, Feb 13, 2019 at 3:22 PM Simon King <[email protected]>
> wrote:
> >>>> >
> >>>> > On Wed, Feb 13, 2019 at 3:13 PM <[email protected]> wrote:
> >>>> > >
> >>>> > > did not work out very well,...god, this stuff gives some good
> headache!
> >>>> > >
> >>>> > > >>> from sqlalchemy.ext.automap import automap_base
> >>>> > > >>> Base = automap_base(metadata=metadata)
> >>>> > > >>> Base.prepare()
> >>>> > > >>> WebView = Base.classes.web_view
> >>>> > > Traceback (most recent call last):
> >>>> > > File "<stdin>", line 1, in <module>
> >>>> > > File
> "/usr/lib64/python2.7/site-packages/sqlalchemy/util/_collections.py", line
> 212, in __getattr__
> >>>> > > raise AttributeError(key)
> >>>> > > AttributeError: web_view
> >>>> >
> >>>> >
> >>>> > What does "print(Base.classes)" report at this point?
> >>>>
> >>>> Sorry, that should probably have been "print(Base.classes.keys())"
> >>>>
> >>>> >
> >>>> >
> >>>> > >
> >>>> > > On Wednesday, February 13, 2019 at 4:01:25 PM UTC+1, Simon King
> wrote:
> >>>> > >>
> >>>> > >> It looks like you are mixing up Table objects (which are part of
> >>>> > >> SQLAlchemy Core) with mapped classes (part of SQLAlchemy ORM).
> >>>> > >>
> >>>> > >> The objects in metadata.tables are instances of the Table class,
> and
> >>>> > >> each correspond to a table or view in the database. The ORM
> allows you
> >>>> > >> to define classes which are mapped to those Tables, such that
> each
> >>>> > >> instance of the mapped class corresponds to a *row* in the
> table.
> >>>> > >> Flask-admin works with mapped classes (which it calls "models"),
> not
> >>>> > >> Table instances.
> >>>> > >>
> >>>> > >> Since you are reflecting the database anyway, the quickest way
> for you
> >>>> > >> to get up-and-running might be to use the Automap extension:
> >>>> > >>
> >>>> > >>
> https://docs.sqlalchemy.org/en/latest/orm/extensions/automap.html
> >>>> > >>
> >>>> > >> It doesn't look like "Base.prepare()" accepts an argument
> telling it
> >>>> > >> to reflect views, so you might actually need a combination of
> the two
> >>>> > >> approaches, something like this (untested):
> >>>> > >>
> >>>> > >> engine =
> create_engine(app.config.get('SQLALCHEMY_DATABASE_URI'))
> >>>> > >> metadata = MetaData(bind=engine)
> >>>> > >> metadata.reflect(views=True, autoload=True)
> >>>> > >>
> >>>> > >> Base = automap_base(metadata=metadata)
> >>>> > >> Base.prepare()
> >>>> > >>
> >>>> > >> Nodes = Base.classes.nodes
> >>>> > >> Attributes = Base.classes.attribs
> >>>> > >> Entries = Base.classes.entries
> >>>> > >> WebView = Base.classes.web_view
> >>>> > >>
> >>>> > >>
> >>>> > >> Hope that helps,
> >>>> > >>
> >>>> > >> Simon
> >>>> > >>
> >>>> > >> On Wed, Feb 13, 2019 at 2:19 PM <[email protected]> wrote:
> >>>> > >> >
> >>>> > >> > i already spent 3 days trying to map one stupid mysql view
> and i cant get it to work.
> >>>> > >> >
> >>>> > >> > in the python interpreter it works...
> >>>> > >> >
> >>>> > >> > Python 2.7.5 (default, May 31 2018, 09:45:54)
> >>>> > >> > [GCC 4.8.5 20150623 (Red Hat 4.8.5-28)] on linux2
> >>>> > >> > Type "help", "copyright", "credits" or "license" for more
> information.
> >>>> > >> > >>> SQLALCHEMY_DATABASE_URI =
> 'mysql://aix_reg:blalblabla@localhost/aix_registry'
> >>>> > >> > >>> from flask import Flask, request
> >>>> > >> > >>> from flask_sqlalchemy import SQLAlchemy
> >>>> > >> > >>> from sqlalchemy import String, Enum, create_engine,
> MetaData, Table, Column, Integer
> >>>> > >> > >>> from sqlalchemy.orm import sessionmaker
> >>>> > >> > >>> from sqlalchemy.ext.declarative import declarative_base
> >>>> > >> > >>> from flask_admin.contrib import sqla
> >>>> > >> > >>> from flask_admin import Admin, expose, BaseView
> >>>> > >> > >>> from flask_admin.contrib.sqla import ModelView
> >>>> > >> > >>> from flask_admin.model.template import
> EndpointLinkRowAction, LinkRowAction
> >>>> > >> > >>> Base = declarative_base()
> >>>> > >> > >>> engine = create_engine(SQLALCHEMY_DATABASE_URI)
> >>>> > >> > >>> metadata = MetaData(bind=engine)
> >>>> > >> > >>> Base.metadata = metadata
> >>>> > >> > >>> metadata.reflect(views=True, autoload=True)
> >>>> > >> > >>> WebView = metadata.tables['web_view']
> >>>> > >> > >>> WebView
> >>>> > >> > Table('web_view',
> MetaData(bind=Engine(mysql://aix_reg:***@localhost/aix_registry)),
> Column('NODE_ID', INTEGER(display_width=11), table=<web_view>,
> server_default=DefaultClause(<sqlalchemy.sql.elements.TextClause object at
> 0x3fff7aace890>, for_update=False)), Column('NODE', VARCHAR(length=256),
> table=<web_view>), Column('LOCATION', VARCHAR(length=256),
> table=<web_view>), Column('IS_CLUSTER', VARCHAR(length=256),
> table=<web_view>), Column('MAN_SYS_NAME', VARCHAR(length=256),
> table=<web_view>), Column('HAS_CICS', VARCHAR(length=256),
> table=<web_view>), Column('OS_LEVEL', VARCHAR(length=256),
> table=<web_view>), Column('HAS_COBOL', VARCHAR(length=256),
> table=<web_view>), Column('HAS_ORACLE', VARCHAR(length=256),
> table=<web_view>), Column('MEM_VALUE', VARCHAR(length=256),
> table=<web_view>), Column('HAS_TUXEDO', VARCHAR(length=256),
> table=<web_view>), Column('STORAGE_VALUE', VARCHAR(length=256),
> table=<web_view>), Column('IS_LPM', VARCHAR(length=256), table=<web_view>),
> Column('HAS_SAP', VARCHAR(length=256), table=<web_view>),
> Column('CPU_VALUE', VARCHAR(length=256), table=<web_view>),
> Column('CLUSTER_RGS', VARCHAR(length=256), table=<web_view>),
> Column('HA_LEVEL', VARCHAR(length=256), table=<web_view>),
> Column('CLUSTER_NODES', VARCHAR(length=256), table=<web_view>),
> Column('CLUSTER_NAME', VARCHAR(length=256), table=<web_view>),
> Column('CPU_POOL', VARCHAR(length=256), table=<web_view>),
> Column('AME_FACTOR', VARCHAR(length=256), table=<web_view>),
> Column('SYS_PROFILE', VARCHAR(length=256), table=<web_view>),
> Column('INFO_MAIL', VARCHAR(length=256), table=<web_view>),
> Column('HOSTNAME', VARCHAR(length=256), table=<web_view>),
> Column('OS_TYPE', VARCHAR(length=256), table=<web_view>),
> Column('Java6_64', VARCHAR(length=256), table=<web_view>),
> Column('Java7_64', VARCHAR(length=256), table=<web_view>),
> Column('Java8_64', VARCHAR(length=256), table=<web_view>),
> Column('Java5_64', VARCHAR(length=256), table=<web_view>), Column('Java8',
> VARCHAR(length=256), table=<web_view>), Column('Java5',
> VARCHAR(length=256), table=<web_view>), Column('Java14',
> VARCHAR(length=256), table=<web_view>), Column('Java7',
> VARCHAR(length=256), table=<web_view>), Column('Java71',
> VARCHAR(length=256), table=<web_view>), Column('Java71_64',
> VARCHAR(length=256), table=<web_view>), Column('Java14_64',
> VARCHAR(length=256), table=<web_view>), Column('Java6',
> VARCHAR(length=256), table=<web_view>), Column('UPTIME',
> VARCHAR(length=256), table=<web_view>), Column('IP', VARCHAR(length=256),
> table=<web_view>), Column('IP_LONG', VARCHAR(length=256),
> table=<web_view>), Column('CLUSTER_NODENAME', VARCHAR(length=256),
> table=<web_view>), Column('RG_SERVICE_IP_LONG', VARCHAR(length=256),
> table=<web_view>), Column('HAS_SNA', VARCHAR(length=256),
> table=<web_view>), schema=None)
> >>>> > >> >
> >>>> > >> >
> >>>> > >> >
> >>>> > >> > but in the flask app no way
> >>>> > >> >
> >>>> > >> > Base = declarative_base()
> >>>> > >> > engine =
> create_engine(app.config.get('SQLALCHEMY_DATABASE_URI'))
> >>>> > >> > metadata = MetaData(bind=engine)
> >>>> > >> > Base.metadata = metadata
> >>>> > >> > metadata.reflect(views=True, autoload=True)
> >>>> > >> >
> >>>> > >> > Nodes = metadata.tables['nodes']
> >>>> > >> > Attributes = metadata.tables['attribs']
> >>>> > >> > Entries = metadata.tables['entries']
> >>>> > >> > WebView = metadata.tables['web_view']
> >>>> > >> >
> >>>> > >> > session = sessionmaker(bind=engine)
> >>>> > >> >
> >>>> > >> >
> >>>> > >> > admin = Admin(app, name='AIX Registry',
> template_mode='bootstrap3')
> >>>> > >> > admin.add_view(AixAdmin(Nodes, session, 'Overview'))
> >>>> > >> >
> >>>> > >> > root@lpgaixmgmtlx01:/root/flask/aix_registry>python run.py
> >>>> > >> > Traceback (most recent call last):
> >>>> > >> > File "run.py", line 3, in <module>
> >>>> > >> > from app import app
> >>>> > >> > File "/root/flask/aix_registry/app/__init__.py", line 18, in
> <module>
> >>>> > >> > from app import views
> >>>> > >> > File "/root/flask/aix_registry/app/views.py", line 55, in
> <module>
> >>>> > >> > admin.add_view(AixAdmin(Nodes, session, 'Overview'))
> >>>> > >> > File
> "/usr/lib/python2.7/site-packages/flask_admin/contrib/sqla/view.py", line
> 329, in __init__
> >>>> > >> > menu_icon_value=menu_icon_value)
> >>>> > >> > File
> "/usr/lib/python2.7/site-packages/flask_admin/model/base.py", line 812, in
> __init__
> >>>> > >> > menu_icon_value=menu_icon_value)
> >>>> > >> > File "/usr/lib/python2.7/site-packages/flask_admin/base.py",
> line 192, in __init__
> >>>> > >> > self.endpoint = self._get_endpoint(endpoint)
> >>>> > >> > File
> "/usr/lib/python2.7/site-packages/flask_admin/model/base.py", line 825, in
> _get_endpoint
> >>>> > >> > return self.model.__name__.lower()
> >>>> > >> > AttributeError: 'Table' object has no attribute '__name__'
> >>>> > >> >
> >>>> > >> >
> >>>> > >> > so how can i do this highly frustrating task correctly?
> >>>> > >> >
> >>>> > >> > --
> >>>> > >> > SQLAlchemy -
> >>>> > >> > The Python SQL Toolkit and Object Relational Mapper
> >>>> > >> >
> >>>> > >> > http://www.sqlalchemy.org/
> >>>> > >> >
> >>>> > >> > To post example code, please provide an MCVE: Minimal,
> Complete, and Verifiable Example. See http://stackoverflow.com/help/mcve
> for a full description.
> >>>> > >> > ---
> >>>> > >> > You received this message because you are subscribed to the
> Google Groups "sqlalchemy" group.
> >>>> > >> > To unsubscribe from this group and stop receiving emails from
> it, send an email to [email protected].
> >>>> > >> > To post to this group, send email to [email protected].
>
> >>>> > >> > Visit this group at https://groups.google.com/group/sqlalchemy.
>
> >>>> > >> > For more options, visit https://groups.google.com/d/optout.
> >>>> > >
> >>>> > > --
> >>>> > > SQLAlchemy -
> >>>> > > The Python SQL Toolkit and Object Relational Mapper
> >>>> > >
> >>>> > > http://www.sqlalchemy.org/
> >>>> > >
> >>>> > > To post example code, please provide an MCVE: Minimal, Complete,
> and Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> >>>> > > ---
> >>>> > > You received this message because you are subscribed to the
> Google Groups "sqlalchemy" group.
> >>>> > > To unsubscribe from this group and stop receiving emails from it,
> send an email to [email protected].
> >>>> > > To post to this group, send email to [email protected].
> >>>> > > Visit this group at https://groups.google.com/group/sqlalchemy.
> >>>> > > For more options, visit https://groups.google.com/d/optout.
> >
> > --
> > SQLAlchemy -
> > The Python SQL Toolkit and Object Relational Mapper
> >
> > http://www.sqlalchemy.org/
> >
> > To post example code, please provide an MCVE: Minimal, Complete, and
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full
> description.
> > ---
> > You received this message because you are subscribed to the Google
> Groups "sqlalchemy" group.
> > To unsubscribe from this group and stop receiving emails from it, send
> an email to [email protected] <javascript:>.
> > To post to this group, send email to [email protected]
> <javascript:>.
> > Visit this group at https://groups.google.com/group/sqlalchemy.
> > For more options, visit https://groups.google.com/d/optout.
>
--
SQLAlchemy -
The Python SQL Toolkit and Object Relational Mapper
http://www.sqlalchemy.org/
To post example code, please provide an MCVE: Minimal, Complete, and Verifiable
Example. See http://stackoverflow.com/help/mcve for a full description.
---
You received this message because you are subscribed to the Google Groups
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.