How can I query a (PostgreSQL) View with SQLAlchemy - sqlalchemy

Usually I connect/query like this
class MyTable(Base):
__tablename__ = 'mytable'
__table_args__ = {'schema': 'tables'}
id = Column(Integer, primary_key=True)
salutation = Column(VARCHAR(1))
def __init__(self, salutation):
self.salutation = salutation
def load():
""""""
Session = sessionmaker(bind=engine)
session = Session()
for i in session.query(MyTable).order_by(MyTable.id):
entries.append(i.__dict__)
session.close()
But How I query an (PostgreSQL) DB View, without any keys?

Related

Many-to-One relationships in SQLAlchemy

I'm having trouble with Many-to-One relationships between my SQLAlchemy models. The relationship between ChangeOrder (many) and Contract (one) is fine, but the one between LineItem (many) and ChangeOrder (one) isn't working.
I've tried both approaches suggested in the basic relationships docs and both fail for the LineItem to ChangeOrder relationship.
# using back_populates
class Contract(Base):
__tablename__ = "contract"
id = Column(Integer, primary_key=True)
change_orders = relationship("ChangeOrder", back_populates="contract")
class ChangeOrder(Base):
__tablename__ = "changeorder"
id = Column(Integer, primary_key=True)
line_items = relationship("LineItem", back_populates="change_order")
contract_id = Column(Integer, ForeignKey("contract.id"))
contract = relationship("Contract", back_populates="change_orders")
class LineItem(Base):
__tablename__ = "lineitem"
id = Column(Integer, primary_key=True)
change_order_id = Column(Integer, ForeignKey("changeorder.id"))
change_order = relationship("ChangeOrder", back_populates="line_items")
def test_insert_change_order(db_session, item):
c = Contract()
db_session.add(c)
db_session.commit()
co = ChangeOrder(contract_id=c.id)
db_session.add(co)
db_session.commit()
row = db_session.query(Contract).get(c.id)
assert len(row.change_orders) == 1 # this Many-to-One works
li = LineItem(change_order_id=co.id)
db_session.add(li)
db_session.commit()
row = db_session.query(ChangeOrder).get(co.id)
assert len(row.line_items) == 1 # this Many-to-One does not
I also tried the backref approach, but it has the same problem.
# using backref
class LineItem(Base):
__tablename__ = "lineitem"
id = Column(Integer, primary_key=True)
change_order_id = Column(Integer, ForeignKey("changeorder.id"))
change_order = relationship("ChangeOrder", backref="line_items")
class ChangeOrder(Base):
__tablename__ = "changeorder"
id = Column(Integer, primary_key=True)
contract_id = Column(Integer, ForeignKey("contract.id"))
contract = relationship("Contract", backref="change_orders")
class Contract(Base):
__tablename__ = "contract"
id = Column(Integer, primary_key=True)
conftest.py
import pytest
from flask_sqlalchemy import SQLAlchemy
from frontend.app import app
#pytest.fixture
def testapp():
db = SQLAlchemy()
app.config["SQLALCHEMY_ECHO"] = True
with app.app_context():
db.create_all()
yield app
db.session.remove()
db.drop_all()
#pytest.fixture(scope="session")
def database():
db = SQLAlchemy()
with app.app_context():
db.create_all()
yield db
#pytest.fixture(scope="session")
def _db(database):
return database

Sqlalchemy eager loading of parent all properties in joined table inheritance

I have the following problem:
I have a hierachy of classes with joined table inheritance:
class AdGroupModel(Base, AdwordsRequestMixin):
__tablename__ = 'ad_groups'
db_id = Column(BigInteger, primary_key=True)
created_at = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now())
# ----RELATIONS-----
# campaign MANY-to-ONE
campaign_db_id = Column(BigInteger,
ForeignKey('campaigns.db_id', ondelete='CASCADE'),
nullable = True,
)
# # ads ONE-to-MANY
ads = relationship("AdModel",
backref="ad_group",
lazy="subquery",
passive_deletes=True,
single_parent=True,
cascade="all, delete, delete-orphan")
# # # keywords ONE-to-MANY
criteria = relationship("AdGroupCriterionModel",
backref="ad_group",
lazy="subquery",
passive_deletes=True,
single_parent=True,
cascade="all, delete, delete-orphan")
# Joined Table Inheritance
type = Column(Unicode(50))
__mapper_args__ = {
'polymorphic_identity': 'ad_group',
'polymorphic_on': type
}
class AdGroupCriterionModel(Base, AdGroupDependenceMixin):
__tablename__ = 'ad_group_criterion'
db_id = Column(BigInteger, primary_key=True)
destination_url = Column(Unicode, nullable=True)
status = Column(Enum("PAUSED", "ACTIVE", "DELETED",
name='criterion_status'), default="ACTIVE")
# ----RELATIONS---
# ad_group ONE-to-MANY
ad_group_db_id = Column(BigInteger, ForeignKey('ad_groups.db_id',
ondelete='CASCADE'), nullable=True)
# Joined Table Inheritance
criterion_sub_type = Column(Unicode(50))
__mapper_args__ = {
'polymorphic_on': criterion_sub_type
}
class AdGroupKeywordModel(AdGroupCriterionModel):
__tablename__ = 'ad_group_keyword'
__mapper_args__ = {'polymorphic_identity': 'Keyword'}
db_id = Column(Integer, ForeignKey('ad_group_criterion.db_id'), primary_key=True)
text = Column(Unicode, nullable=False)
class AdGroupDependenceMixin(object):
_aggad_id = Column(BigInteger, nullable=True)
_agname = Column(Unicode, nullable=True)
#hybrid_property
def ad_group_GAD_id(self):
if self.ad_group is None:
res = self._aggad_id
else:
res = self.ad_group.GAD_id
return res
#ad_group_GAD_id.setter
def ad_group_GAD_id(self, value):
self._aggad_id = value
if value is not None:
self.ad_group = None
#ad_group_GAD_id.expression
def ad_group_GAD_id(cls):
what = case([( cls._aggad_id != None, cls._aggad_id)], else_=AdGroupModel.GAD_id)
return what.label('adgroupgadid_expression')
#hybrid_property
def ad_group_name(self):
if self.ad_group is None:
return self._agname
else:
return self.ad_group.name
#ad_group_name.setter
def ad_group_name(self, value):
self._agname = value
if value is not None:
self.campaign = None
#ad_group_name.expression
def ad_group_name(cls):
what = case([( cls._agname != None, cls._agname)], else_=AdGroupModel.name)
return what.label('adgroupname_expression')
And I load the Keywords objects from the database with the following query:
all_objects1 = self.database.session.query(AdGroupKeywordModel).join(AdGroupModel)\
.options(subqueryload('ad_group'))\
.filter(AdGroupModel.GAD_id!=None)\
.limit(self.options.limit).all()
which returns obejcts of type AdGroupKeywordModel.
Unfortunately every time I try to access the properties of the AdGroupKeywordModel which are in the parent table (AdGroupCriterionModel) a query of this type is emitted:
sqlalchemy.engine.base.Engine
SELECT ad_group_criterion.destination_url AS ad_group_criterion_destination_url, ad_group_criterion.status AS ad_group_criterion_status, ad_group_criterion.ad_group_db_id AS ad_group_criterion_ad_group_db_id, ad_group_criterion.criterion_sub_type AS ad_group_criterion_criterion_sub_type, ad_group_keyword.text AS ad_group_keyword_text
FROM ad_group_criterion JOIN ad_group_keyword ON ad_group_criterion.db_id = ad_group_keyword.db_id
which is strongly compromising the performace.
What I would like to have is that all the attributes for the class AdGroupKeywordModel which are related to the parent (and other classes defined in the relationship) to be loaded with the initial query and be cached for further use. So that when I access them I do not get any overhead from further sqlstatements.
It seems that eager loading is only defined for relationships but not for hierarchies. Is it possible to have this behaviour in sqlalchemy for hierarchies as well?
Thanks
What I see is: only AdGroupModel has a relationship with a lazy= definition (which is the keyword which defines eager loading for relationships), and the query only has a subqueryload('ad_group').
The only point, in which ad_group or AdGroupModel touch with AdGroupKeywordModel is in AdGroupModel.criteria, which has as backref AdGroupCriterionModel.ad_group. I'm not familiar with the subqueryload syntax, but If I would want to eager-load AdGroupCriterionModel.ad_group, I'd define criteria like this:
criteria = relationship(
"AdGroupCriterionModel", backref=backref("ad_group", lazy="subquery"),
lazy="subquery", passive_deletes=True, single_parent=True,
cascade="all, delete, delete-orphan")
The key would be in defining the right lazy also for the backref.

Creating a self-referencing M2M relationship in SQLAlchemy (+Flask)

While trying to learn Flask, I am building a simple Twitter clone. This would include the ability for a User to follow other Users. I am trying to set up a relational database through SQLAlchemy to allow this.
I figured I would need a self-referencing many-to-many relationship on the User. Following from the SQLAlchemy documentation I arrived at:
#imports omitted
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///twitclone.db'
db = SQLAlchemy(app)
Base = declarative_base()
user_to_user = Table("user_to_user", Base.metadata,
Column("follower_id", Integer, ForeignKey("user.id"), primary_key=True),
Column("followed_id", Integer, ForeignKey("user.id"), primary_key=True)
)
class User(db.Model):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String, unique=False)
handle = Column(String, unique=True)
password = Column(String, unique=False)
children = relationship("tweet")
following = relationship("user",
secondary=user_to_user,
primaryjoin=id==user_to_user.c.follower_id,
secondaryjoin=id==user_to_user.c.followed_id,
backref="followed_by"
)
#Tweet class goes here
db.create_all()
if __name__ == "__main__":
app.run()
Running this code results in the database being created without any error messages. However, the whole part (table) connecting a user to a user is simply omitted. This is the definition of the User table:
CREATE TABLE user (
id INTEGER NOT NULL,
name VARCHAR,
handle VARCHAR,
password VARCHAR,
PRIMARY KEY (id),
UNIQUE (handle)
)
Why does SQLAlchemy not create the self-referential relationship for the User?
note: I am new to both Flask and SQLAlchemy and could be missing something obvious here.
Ok, it seems I mixed up two different styles of using SQLAlchemy with Flask: the declarative extension of SQLAlchemy and flask-sqlalchemy extension. Both are similar in capabilities with the difference being that the flask extension has some goodies like session handling. This is how I rewrote my code to strictly make use of flask-sqlalchemy.
from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///kwek.db'
db = SQLAlchemy(app)
#Table to handle the self-referencing many-to-many relationship for the User class:
#First column holds the user who follows, the second the user who is being followed.
user_to_user = db.Table('user_to_user',
db.Column("follower_id", db.Integer, db.ForeignKey("user.id"), primary_key=True),
db.Column("followed_id", db.Integer, db.ForeignKey("user.id"), primary_key=True)
)
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=False)
handle = db.Column(db.String(16), unique=True)
password = db.Column(db.String, unique=False)
kweks = db.relationship("Kwek", lazy="dynamic")
following = db.relationship("User",
secondary=user_to_user,
primaryjoin=id==user_to_user.c.follower_id,
secondaryjoin=id==user_to_user.c.followed_id,
backref="followed_by"
)
def __repr__(self):
return '<User %r>' % self.name
class Kwek(db.Model):
__tablename__ = 'kwek'
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(140), unique=False)
post_date = db.Column(db.DateTime, default=datetime.now())
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Kwek %r>' % self.content
if __name__ == "__main__":
app.run()

Accessing extra column in many-to-many relationship with simplified association objects

I'm trying to work with the example in the SQLAlchemy docs: Simplifying Association Objects
What I am struggling with understanding is how I can access the special_key. Ultimately I'd like to be able to do something like this:
for user in users
for keyword in user.keywords
keyword.special_key
Here is the code from the example:
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
# association proxy of "user_keywords" collection
# to "keyword" attribute
keywords = association_proxy('user_keywords', 'keyword')
def __init__(self, name):
self.name = name
class UserKeyword(Base):
__tablename__ = 'user_keyword'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key=True)
special_key = Column(String(50))
# bidirectional attribute/collection of "user"/"user_keywords"
user = relationship(User,
backref=backref("user_keywords",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
keyword = relationship("Keyword")
def __init__(self, keyword=None, user=None, special_key=None):
self.user = user
self.keyword = keyword
self.special_key = special_key
class Keyword(Base):
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
keyword = Column('keyword', String(64))
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
Am I on the right track in following this pattern here?
My goal is essentially many-to-many with an extra column containing a boolean value.
This should work:
for user in users:
for keyword in user.user_keywords:
print keyword.special_key

how to add default related record with sqlaclhemy (e.g. user belongs to group)

I have two models: AuthUser and AuthGroup, they are linked via a many2many relationship. By default I have at least 3 user "states":
unlogged -> no group
logged in -> users group
admin -> admin group
I would like to be sure that whenever a new user is added to the database, it is added to the users group too. Is there a way to get this functionality in the model defintion?
here are my table definitions
Base = declarative_base()
user_group_table = Table('auth_user_groups', Base.metadata,
Column('user_id', types.Integer(), \
ForeignKey('auth_users.id', onupdate='CASCADE', ondelete='CASCADE')),
Column('group_id', types.Integer(), \
ForeignKey('auth_groups.id', onupdate='CASCADE', ondelete='CASCADE'))
)
class AuthGroup(Base):
__tablename__ = 'auth_groups'
__table_args__ = {"sqlite_autoincrement": True}
id = Column(types.Integer(), primary_key=True)
name = Column(Unicode(80), unique=True, nullable=False)
created = Column(types.DateTime(), default=functions.now())
users = relation('AuthUser', secondary=user_group_table, \
backref='auth_groups')
def __repr__(self):
return u'%s' % self.name
def __unicode__(self):
return self.name
class AuthUser(Base):
__tablename__ = 'auth_users'
__table_args__ = {"sqlite_autoincrement": True}
id = Column(types.Integer(), primary_key=True)
login = Column(Unicode(80), default=u'', index=True)
username = Column(Unicode(80), default=u'', index=True)
_password = Column('password', Unicode(80), default=u'', index=True)
email = Column(Unicode(80), default=u'', index=True)
groups = relation('AuthGroup', secondary=user_group_table, \
backref='auth_users')
def _set_password(self, password):
self._password = bcrypt.hashpw(password, bcrypt.gensalt())
def _get_password(self):
return self._password
password = synonym('_password', descriptor=property(_get_password, \
_set_password))
#classmethod
def get_by_id(cls, id):
return DBSession.query(cls).filter(cls.id==id).first()
#classmethod
def get_by_login(cls, login):
return DBSession.query(cls).filter(cls.login==login).first()
#classmethod
def get_by_username(cls, username):
return DBSession.query(cls).filter(cls.username==username).first()
#classmethod
def get_by_email(cls, email):
return DBSession.query(cls).filter(cls.email==email).first()
#classmethod
def check_password(cls, **kwargs):
if kwargs.has_key('id'):
user = cls.get_by_id(kwargs['id'])
if kwargs.has_key('username'):
user = cls.get_by_username(kwargs['username'])
if not user:
return False
if bcrypt.hashpw(kwargs['password'], user.password) == user.password:
return True
else:
return False
usually a class constructor validates expected data upon new object creation (an INSERT in the ORM, short of any trickery to bypass the usual mechanics, always corresponds to a new instance of a mapped class created via constructor):
class AuthUser(Base):
def __init__(self, **kw):
self.groups = kw.pop('groups', None)
if not self.groups:
raise ValueError("at least one group is required!")
super(AuthUser, self).__init__(**kw)