SQLAlchemy declarative. Specify columns to select - python

Declarative base:
Base = declarative_base()
Base.query = Session.query_property()
The class:
class Cheat(Base):
__tablename__ = 'cheats'
id = Column(Integer, primary_key = True, autoincrement = True)
cheat = Column(Text)
name = Column(String(255), index = True)
_html = Column('html', Text)
_slug = Column('slug', String(255))
#hybrid_property
def html(self):
return self._html
#html.setter
def set_html(self, md):
from markdown import markdown
self._html = markdown(md)
#hybrid_property
def slug(self):
return self._slug
#slug.setter
def set_slug(self, name):
self._slug = slugify(name)
def __init__(self, name, cheat):
self.name = name
self.slug = name
self.cheat = cheat
self.html = cheat
def __repr__(self):
return "Cheat<%s>" % self.name
Now I can get everything from the cheats:
Cheat.query.all()
and SQLAlchemy will generate SQL statement similar to:
SELECT name, slug, cheat, html FROM cheats
but I want my SQL statement to be:
SELECT name, slug FROM cheats
so I need to specify which columns I want to retrieve, because I don't really need to pull heavy texts from the database over the network.
How do I do that?

define them as deferred, then they will only be fetched when the're accessed
from sqlalchemy.orm import deferred
class Cheat(Base):
__tablename__ = 'cheats'
id = Column(Integer, primary_key = True, autoincrement = True)
cheat = deferred(Column(Text))
name = Column(String(255), index = True)
_html = Column('html', Text)
_slug = deferred(Column('slug', String(255)))

for name, slug in session.query(Cheat.name, Cheat.slug):
print name, slug

Related

How to create a Union table inside the SQL database using SQLAlchemy?

I have the following structure :
import sqlalchemy as sa
import sqlalchemy.orm as orm
Base = orm.declarative_base()
class Equity(Base) :
__tablename__ = 'equities'
id = sa.Column(sa.String, primary_key=True)
name = sa.Column(sa.String, nullable=False)
currency = sa.Column(sa.String, nullable=False)
country = sa.Column(sa.String, nullable=False)
sector = sa.Column(sa.String, nullable=True)
def __repr__(self) :
return f"Equity('Ticker: {self.id}', 'Name: {self.name}')"
class Bond(Base) :
__tablename__ = 'bonds'
id = sa.Column(sa.String, primary_key=True)
name = sa.Column(sa.String, nullable=False)
country = sa.Column(sa.String, nullable=False)
currency = sa.Column(sa.String, nullable=False)
sector = sa.Column(sa.String, nullable=False)
def __repr__(self) :
return f"Bond('Ticker: {self.id}', 'Name: {self.name}')"
And I want to create a new permanent table inside the database that is the UNION of those two tables (using the columns ID and CURRENCY)
I know I can create that outside using this :
results = session.query(Equity.id, Equity.currency).union(session.query(Bond.id, Bond.currency))
But I want (if possible) to have a relationship table inside my Database that automatically updates when I change anything on either on the EQUITIES or BONDS table. Something like this :
class NewUnionTable(Base) :
<relationship><union>
Can someone help me create this, please?
Appreciate very much
From the Views recipe and your classes (simplified), here is a functioning version of the view of the union of your sets of columns.
Read through the recipe for more details, but simply it's creating the necessary compiler extensions and using them when appropriate via an event.
from sqlalchemy import Column, String, create_engine, event, inspect, select, table, union
from sqlalchemy.ext import compiler
from sqlalchemy.orm import Session, declarative_base
from sqlalchemy.schema import DDLElement
# boilerplate from https://github.com/sqlalchemy/sqlalchemy/wiki/Views
class CreateView(DDLElement):
def __init__(self, name, selectable):
self.name = name
self.selectable = selectable
class DropView(DDLElement):
def __init__(self, name):
self.name = name
#compiler.compiles(CreateView)
def _create_view(element, compiler, **kw):
return "CREATE VIEW %s AS %s" % (
element.name,
compiler.sql_compiler.process(element.selectable, literal_binds=True),
)
#compiler.compiles(DropView)
def _drop_view(element, compiler, **kw):
return "DROP VIEW %s" % (element.name)
def view_exists(ddl, target, connection, **kw):
return ddl.name in inspect(connection).get_view_names()
def view_doesnt_exist(ddl, target, connection, **kw):
return not view_exists(ddl, target, connection, **kw)
def view(name, metadata, selectable):
t = table(name)
t._columns._populate_separate_keys(
col._make_proxy(t) for col in selectable.selected_columns
)
event.listen(
metadata,
"after_create",
CreateView(name, selectable).execute_if(callable_=view_doesnt_exist),
)
event.listen(
metadata,
"before_drop",
DropView(name).execute_if(callable_=view_exists),
)
return t
# demo
Base = declarative_base()
class Equity(Base):
__tablename__ = "equities"
id = Column(String, primary_key=True)
currency = Column(String, nullable=False)
class Bond(Base):
__tablename__ = "bonds"
id = Column(String, primary_key=True)
currency = Column(String, nullable=False)
common_view = view(
"bonds_equities_union_view",
Base.metadata,
union(
select(Equity.id.label("id"), Equity.currency.label("currency")),
select(Bond.id.label("id"), Bond.currency.label("currency")),
),
)
engine = create_engine("sqlite://", echo=True, future=True)
Base.metadata.create_all(engine)
with Session(engine) as session:
session.add_all(
[
Equity(id="AAA", currency="EUR"),
Equity(id="AAB", currency="USD"),
Bond(id="AAA", currency="EUR"),
Equity(id="EEF", currency="GBP"),
]
)
session.commit()
with Session(engine) as session:
results = session.execute(select(common_view)).all()
print(results) # [('AAA', 'EUR'), ('AAB', 'USD'), ('EEF', 'GBP')]
NB. this is a union, so it's only distinct values. Notice four instances inserted but only three in the view. This deduplication (sort + filter) is slow on large datasets, if you do not care use a union_all which allows duplicates.

SqlAlchemy: handling table columns that have a dollar sign in their name

In SqlAlchemy you declare a variable for every table column. But I have to read from a table that has a column named customer$partner$naziv and you can't declare a variable with a name like that in Python. Is there a way to get around this issue?
Here is what I have (obviously not working):
class RfidSif(Base):
""""""
__tablename__ = 'rfid_sif'
id = Column(Integer, primary_key=True)
customer = Column(Integer)
customer$partner$naziv = Column(String)
rfid_id = Column(String)
rfid_name = Column(String)
rfid_group_name = Column(String)
rfid_comment = Column(String)
rfid_startdate = Column(Date)
rfid_enddate = Column(Date)
activity = Column(SmallInteger)
user_headless = Column(Integer)
#----------------------------------------------------------------------
def __init__(self, id, customer, customer$partner$naziv, rfid_id, rfid_name, rfid_group_name, rfid_comment, rfid_startdate, rfid_enddate, activity, user_headless):
""""""
self.id = id
self.customer = customer
self.customer$partner$naziv = customer$partner$naziv
self.rfid_id = rfid_id
self.rfid_name = rfid_name
self.rfid_group_name = rfid_group_name
self.rfid_comment = rfid_comment
self.rfid_startdate = rfid_startdate
self.rfid_enddate = rfid_enddate
self.activity = activity
self.user_headless = user_headless
#----------------------------------------------------------------------
def __repr__(self):
""""""
return "<RfidSif - '%s': '%s' - '%s'>" % (self.id, self.rfid_id, self.name)
Simply specify the column name as the first argument and use a different attribute name:
customer_partner_naziv = Column('customer$partner$naziv', String)
Also, you do not need your own __init__ - the default constructor accepts keyword arguments for everything (and you shouldn't set id manually anyway, it's usually a serial/autoincrement column)

Flask-admin, editing relationship giving me object representation of Foreign Key object

I have a flask project, and I am getting started learning the flask-admin module.
SqlAlchemy schema for the required tables.
import datetime
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import backref, relationship
Base = declarative_base()
class Workgroup(Base):
__tablename__ = 'workgroups'
id = sqlalchemy.Column(sqlalchemy.Integer,
primary_key=True,
autoincrement=True
)
name = sqlalchemy.Column(sqlalchemy.String(16))
shorthand = sqlalchemy.Column(sqlalchemy.String(4))
def __unicode__(self):
return self.name
class Drive(Base):
"""
A drive in an edit station.
"""
__tablename__ = 'drives'
id = sqlalchemy.Column(sqlalchemy.Integer,
primary_key=True,
autoincrement=True
)
name = sqlalchemy.Column(sqlalchemy.String(64))
computer_id = sqlalchemy.Column(sqlalchemy.Integer,
sqlalchemy.ForeignKey(Computer.id)
)
computer = relationship('Computer', backref='drives')
is_active = sqlalchemy.Column(sqlalchemy.Boolean)
free_space = sqlalchemy.Column(sqlalchemy.BigInteger)
used_space = sqlalchemy.Column(sqlalchemy.BigInteger)
total_space = sqlalchemy.Column(sqlalchemy.BigInteger)
percentage_full = sqlalchemy.Column(sqlalchemy.Float)
boot_time = sqlalchemy.Column(sqlalchemy.DateTime)
last_changed_workgroup = sqlalchemy.Column(sqlalchemy.DateTime)
last_checked_in = sqlalchemy.Column(sqlalchemy.DateTime)
last_notified = sqlalchemy.Column(sqlalchemy.DateTime)
image_version = sqlalchemy.Column(sqlalchemy.String(64))
image_date = sqlalchemy.Column(sqlalchemy.DateTime)
current_workgroup_id = sqlalchemy.Column(sqlalchemy.Integer,
sqlalchemy.ForeignKey(Workgroup.id)
)
workgroup = relationship('Workgroup', backref='drives')
Admin Test
class DriveAdmin(sqla.ModelView):
column_display_pk = True
column_hide_backrefs = False
column_display_all_relations = True
form_columns = [ 'computer_id', 'workgroup.name', ]
column_list = ('computer.name', 'name', 'workgroup', 'computer.short_description', 'computer.notes',
'computer.station_type.description', 'computer.room.name')
class WorkgroupAdmin(sqla.ModelView):
column_display_pk = True # optional, but I like to see the IDs in the list
column_hide_backrefs = False
column_list = ('id', 'name', 'shorthand')
# Create admin
admin = admin.Admin(app, name='Example: SQLAlchemy2', template_mode='bootstrap3')
admin.add_view(WorkgroupAdmin(schema.Workgroup, db))
admin.add_view(DriveAdmin(schema.Drive, db))
replacing form columns for 'workgroup' with 'workgroup.name' gives me an invalid model property name, even though I have successfully used schema.workgroup.name elsewhere in code.
The resulting admin form looks like this.
How do I go about getting the workgroup.name value to appear as opposed to the object representation?
Thanks for reading!
You need to get the workgroup class to return its name via the repr function. That way it will show in the field.
class Workgroup(Base):
__tablename__ = 'workgroups'
id = sqlalchemy.Column(sqlalchemy.Integer,
primary_key=True,
autoincrement=True
)
name = sqlalchemy.Column(sqlalchemy.String(16))
shorthand = sqlalchemy.Column(sqlalchemy.String(4))
def __unicode__(self):
return self.name
def __repr__(self):
return '<Workgroup %r>' % (self.name)

One-to-many Flask | SQLAlchemy

I am trying to create a one-to-many relationship using Flask and SQLAlchemy.
I want the one-to-many relationship to be as so:
"For any single movie, there can be multiple characters"
Here it what I have so far, but it is saving in my DB as one-to-one right now. (One movie to one character, saving multiple times in DB for multiple characters)
class Movie(db.Model):
__tablename__ = "movies"
id = db.Column('movies_id', db.Integer, primary_key=True)
movie_type = db.Column('movie_type', db.Text())
def __init__(self, movie_type):
self.movie_type = movie_type
def __repr__(self):
return '<Movie %r>' % self.id
class Character(db.Model):
__tablename__ = "characters"
id = db.Column('character_id', db.Integer, primary_key=True)
character_description = db.Column('character_description', db.Text())
movie_id = db.Column(db.Integer, db.ForeignKey('movies.movie_id'))
movie = db.relationship('Movie', backref='characters', lazy='dynamic')
def __init__(self, character_description, movie):
self.character_description = character_description
self.movie = movie
def __repr__(self):
return '<Character %r>' % self.id
I am saving into the DB like this:
movie = models.movie(movie_type)
character = models.Character(character_description, movie)
db.session.add(movie)
db.session.add(character)
db.session.commit()
The end goal is to be able to look up what movie a character is in. If you could also help me out with that query, that would be great!
Thanks ahead of time.
Well, I think you miss the characters relations in the movie + the insert was not totaly right.
There is also little details that you have to be carefull. Why id of movie is movieS_id and id of character is character_id ?
Also, the name of the column is the same as the name of the variable if not specified.
For example you can do that:
character_description = db.Column(db.Text())
Anyway, without changing this details, you can try this:
class Movie(db.Model):
__tablename__ = "movies"
id = db.Column('movies_id', db.Integer, primary_key=True)
movie_type = db.Column('movie_type', db.Text())
characters = db.relationship("Character", backref="movie", lazy='dynamic')
def __init__(self, movie_type):
self.movie_type = movie_type
def __repr__(self):
return '<Movie %r>' % self.id
class Character(db.Model):
__tablename__ = "characters"
id = db.Column('character_id', db.Integer, primary_key=True)
character_description = db.Column('character_description', db.Text())
movie_id = db.Column(db.Integer, db.ForeignKey('movies.movies_id'))
movie = db.relationship('Movie')
def __init__(self, character_description, movie):
self.character_description = character_description
self.movie = movie
def __repr__(self):
return '<Character %r>' % self.id
Inserting
c = Character(character_description='c')
c2 = Character(character_description='c2')
m = Movie(movie_type ='action')
# link characters to movie
m.characters.append(c)
m.characters.append(c2)
# or
m.characters.extend([c,c2])
db.session.add(m)
# add characters
db.session.add(c)
db.session.add(c2)
# or
db.session.add_all([c,c2])
# commit
db.session.commit()

How to store and search list in SQLAlchemy?

I need to write two classes like this:
class Item(Base, DBBase):
__tablename__ = 'items'
id = Column(Integer, primary_key = True)
name = Column(String)
description = Column(String)
price = Column(Float, default = 0)
on_sell = Column(Boolean, default = False)
img = Column(String)
attributes = relationship('ItemAttribute')
def __init__(self, name, description):
self.name = name
self.description = description
class ItemAttribute(Base, DBBase):
__tablename__ = 'itemattributes'
id = Column(Integer, primary_key = True)
name = Column(String, nullable = False)
value = Column(String, nullable = False)
item_id = Column(Integer, ForeignKey('items.id'))
item = relationship('Item')
def __init__(self, name, value):
self.name = name
self.value = value
One item can own several attributes, and I need to:
1. insert some methods on class Item to easily do CURD(insertion, deletion, update and query) attributes for it. I need to search a attribute of a item and return it's corresponding value.
2. have the ability to search items by attributes. For example, some items have the attributes of 'Feature' = 'True'. I need to get all items which have this attribute.
Thanks for help. :-)
If you add backref onto your ItemAttribute relationship:
item_id = Column(Integer, ForeignKey('items.id', onupdate='CASCADE', ondelete='CASCADE'))
item = relationship(Items, backref='attributes')
This will create and Item.attributes[] array which contains the ItemAttribute's. You might also add the onupdate and ondelete if you're using mysql.
Then when you query, you can do this:
rs = mySession.query(Items)
firstItem = rs.first()
for attribute in firstItem.attributes:
print attribute
When querying you can filter by joining the backref:
rs = mySession.query(Items).join(Items.attributes).filter(ItemAttribute.name=='somethingSpecial')
Further, if it's a one to one relationship (but it's not in this case), you could skip the list by specifing uselist=False:
item = relationship(ITEM, backref='attribute', uselist=False)

Categories