There are some models:
from sqlalchemy import create_engine, Table, Column, Integer, \
String, MetaData, join, ForeignKey, select
from sqlalchemy.orm import sessionmaker, column_property, relationship
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///memory')
s = sessionmaker(bind = engine)()
Base = declarative_base()
metadata = MetaData()
class BaseItem(Base):
__tablename__ = 'base_item'
id = Column(Integer, primary_key=True)
name = Column(Integer)
class Stats(Base):
__tablename__ = 'stats'
id = Column(Integer, primary_key=True)
base_item_id = Column(ForeignKey('base_item.id'))
key = Column(String(60))
value = Column(Integer)
class ItemProxy(Base):
__tablename__ = 'item_proxy'
id = Column(Integer, primary_key=True)
base_item_id = Column(ForeignKey('base_item.id'))
some = Column(Integer)
bonus_item_id = Column(ForeignKey('base_item.id'))
class Item(Base):
__table__ = BaseItem.__table__.join(ItemProxy.__table__, ItemProxy.__table__.c.base_item_id == BaseItem.__table__.c.id)
id = ItemProxy.__table__.c.id
base_id = column_property(BaseItem.__table__.c.id, ItemProxy.__table__.c.base_item_id)
stat = relationship('Stats')
bonus_stat = relationship('Stats', ???)
The model ItemProxy bonus_item_id field will not be filled at all times.
What you need to specify a relationship that would have made it the correct query?
Related
I am trying to use ORM with SQLAlchemy in Python. My current solution fails and throws an exception right in the moment the ORM is first used. I receive the following exception:
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Event->event, expression 'Tag' failed to locate a name ('Tag'). If this is a class name, consider adding this relationship() to the <class 'backend.source.database.event.Event'> class after both dependent classes have been defined.
My classes are defined like in the offical SQLAlchemy-Documentation (https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html#many-to-many), which is why I am kinda confused about that error.
association = Table('event_to_tag', declarative_base().metadata,
Column('event_id', Integer, ForeignKey('event.id'), primary_key=True),
Column('tag_id', Integer, ForeignKey('tag.id'), primary_key=True))
class Event(declarative_base()):
__tablename__ = "event"
id = Column(Integer, primary_key=True)
title = Column(String(255))
location = Column(String(255))
organizer_id = Column(Integer, ForeignKey(Organizer.id))
start = Column(DateTime)
end = Column(DateTime)
lang = Column(String(255))
costs = Column(DECIMAL)
registration = Column(TINYINT)
url = Column(String(255))
description = Column(Text)
tags = relationship("Tag", secondary=association, back_populates="events")
class Tag(declarative_base()):
__tablename__ = "tag"
id = Column(Integer, primary_key=True)
name = Column(String(255))
events = relationship("Event", secondary=association, back_populates="tags")
Thank you, greetings
I think you need to define a Base = declarative_base(), need to use in your models and associations.
from sqlalchemy import Column, Integer, ForeignKey, String, DECIMAL, Text, DateTime, Table, create_engine
from sqlalchemy.dialects.mssql import TINYINT
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import declarative_base, relationship, scoped_session, sessionmaker
Base = declarative_base()
association = Table('event_to_tag',
Base.metadata,
Column('event_id', Integer, ForeignKey('events.id'), primary_key=True),
Column('tag_id', Integer, ForeignKey('tags.id'), primary_key=True))
class Event(Base):
__tablename__ = "events"
id = Column(Integer, primary_key=True)
title = Column(String(255))
location = Column(String(255))
# organizer_id = Column(Integer, ForeignKey(Organizer.id))
start = Column(DateTime)
end = Column(DateTime)
lang = Column(String(255))
costs = Column(DECIMAL)
registration = Column(UUID)
url = Column(String(255))
description = Column(Text)
tags = relationship("Tag", secondary=association, back_populates="events")
class Tag(Base):
__tablename__ = "tags"
id = Column(Integer, primary_key=True)
name = Column(String(255))
events = relationship("Event", secondary=association, back_populates="tags")
class CreateEngine:
def __init__(self):
self.connection_string = "postgresql+psycopg2://<user_name>:<password>#127.0.0.1/<table_name>"
self.engine = create_engine(self.connection_string)
def create_table(self):
return Base.metadata.create_all(self.engine)
def create_session(self):
session_factory = sessionmaker(bind=self.engine)
Session = scoped_session(session_factory)
with Session() as session:
pass
if __name__ == "__main__":
CreateEngine().create_table()
Please could someone help me with this error? I have actually been really struggling to find solid, simple examples for SQLAlchemy. Whilst there are plenty of Model examples of there is not much examples of how to use these Models.
The Error:
sqlalchemy.exc.NoForeignKeysError:
Could not determine join condition between parent/child tables on relationship Species.sc_genus
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
The Code
from sqlalchemy import Integer, Column, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation
Base = declarative_base()
class Genus(Base):
__tablename__ = 'genus'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_sub_family = "sc_sub_family"
def __repr__(self):
return "<Genus(common_name='%s')>" % (self.scientific_name)
# Species is a child of Genus
class Species(Base):
__tablename__ = 'species'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_genus = relation("Genus", backref="species")
def __repr__(self):
return "<Species(common_name='%s')>" % (self.scientific_name)
def addSpecies(session):
species = Species()
species.common_name = "House Cat"
species.scientific_name = "Felis catus"
genus = Genus()
genus.scientific_name = "Felis"
session.add(genus)
species.sc_genus = genus
session.add(species)
session.commit()
if __name__ == "__main__":
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
## A bunch of stuff to make the connection to the database work.
engine = create_engine('sqlite:///foos.db', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
addSpecies(session)
I needed to specify a foreign key for the relationship.
class Genus(Base):
__tablename__ = 'genus'
id = Column(Integer, primary_key=True)
scientific_name = Column(String)
# sc_sub_family = "sc_sub_family"
def __repr__(self):
return "<Genus(common_name='%s')>" % (self.scientific_name)
# Species is a child of Genus
class Species(Base):
__tablename__ = 'species'
id = Column(Integer, primary_key=True)
common_name = Column(String)
scientific_name = Column(String)
sc_genus = relation("Genus", backref="species")
sc_genus_id = Column(Integer, ForeignKey('genus.id'))
def __repr__(self):
return "<Species(common_name='%s')>" % (self.scientific_name)
I'm trying to load a subset of columns from multiple joined tables in SQLAlchemy and can't figure out the magic syntax.
Here's a simple example of what I'm trying to do. It results in an error (below):
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, load_only
Base = declarative_base()
class Table1(Base):
__tablename__ = 'table1'
table1_id = Column(Integer, primary_key=True)
table1_val = Column(String)
r1 = relationship('Table2', backref = 'r2')
class Table2(Base):
__tablename__ = 'table2'
table2_id = Column(Integer, ForeignKey('table1.table1_id'), primary_key=True)
table2_val = Column(String)
from sqlalchemy.orm import sessionmaker
some_engine = create_engine('postgresql://scott:tiger#localhost/')
Session = sessionmaker(bind=some_engine)
session = Session()
query = session.query(Table1).join(Table2).options(load_only('table1_val','table2_val'))
ArgumentError: Can't find property named 'table2_val' on the mapped entity Mapper|Table1|table1 in this Query.
How do I pick and choose columns from these multiple tables?
Question was answered at [https://bitbucket.org/zzzeek/sqlalchemy/issues/3679/multiple-load_only-in-queryoptions-ignore]
Here is the corrected code:
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import joinedload, Load, relationship, load_only
Base = declarative_base()
class Table1(Base):
__tablename__ = 'table1'
global_id = Column(Integer, primary_key=True)
table1_val1 = Column(String)
table1_val2 = Column(String)
r1 = relationship('Table2', backref = 'r2')
class Table2(Base):
__tablename__ = 'table2'
global_id = Column(Integer, ForeignKey('table1.global_id'), primary_key=True)
table2_val1 = Column(String)
table2_val2 = Column(String)
from sqlalchemy.orm import sessionmaker
some_engine = create_engine('sqlite://')
Base.metadata.create_all(some_engine)
Session = sessionmaker(bind=some_engine)
session = Session()
session.add(Table1(table1_val1='1val1',table1_val2='1val2',r1=[Table2(table2_val1='2val1', table2_val2='2val2')]))
session.commit() # expires the attribute from the session
query = session.query(Table1).options(
# note that the 'load_only()' is applied to the 'joinedload' path, not
# put on its own Load() path
joinedload('r1',innerjoin=True).load_only('table2_val1'),
Load(Table1).load_only('table1_val1'))
foo = query.all()
assert 'table1_val2' not in foo[0].__dict__
assert 'table2_val2' not in foo[0].r1[0].__dict__
I have declared following models:
from sqlalchemy import (
Column,
Table,
Integer,
Date,
String,
ForeignKey,
)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship, backref
engine = create_engine('sqlite:///data.sqlite')
DBSession = sessionmaker(bind=engine)
Base = declarative_base()
lineup = Table('lineups', Base.metadata,
Column('match_id', Integer, ForeignKey('data.id')),
Column('player_id', Integer, ForeignKey('players.id')))
class Match(Base):
__tablename__ = 'data'
id = Column(Integer, primary_key=True)
date = Column(Date)
tournament = Column(String)
team1 = Column(String)
team2 = Column(String)
team1_lineup = relationship('Player', secondary=lineup)
team2_lineup = relationship('Player', secondary=lineup)
best_of = Column(Integer)
maps = relationship('Map')
score = Column(String)
class Map(Base):
__tablename__ = 'maps'
id = Column(Integer, primary_key=True)
match = Column(Integer, ForeignKey('data.id'))
name = Column(String)
score = Column(String)
class Player(Base):
__tablename__ = 'players'
id = Column(Integer, primary_key=True)
nickname = Column(String)
team = Column(String)
And I'm creating new Match object this way:
match = Match(...) # all kwargs except team1_lineup and team2_lineup
p1 = Player(id=1, nickname='p1', team='team')
p2 = Player(id=2, nickname='p2', team='team')
p3 = Player(id=3, nickname='p2', team='team')
match.team1_lineup.append(p1)
match.team2_lineup.append(p2)
match.team2_lineup.append(p3)
After commiting new object, I'm querying it.
>>> from hltv.models import Match, DBSession
>>> s = DBSession()
>>> m = s.query(Match).first()
>>> m.team1_lineup
[<hltv.models.Player object at 0x7f1a93009d10>, <hltv.models.Player object at 0x7f1a93009d90>, <hltv.models.Player object at 0x7f1a93009e10>]
>>> m.team2_lineup
[<hltv.models.Player object at 0x7f1a93009d10>, <hltv.models.Player object at 0x7f1a93009d90>, <hltv.models.Player object at 0x7f1a93009e10>]
The problem is m.team1_lineup and m.team2_lineup are the same. How can I solve this?
Additionally, how do I assign ID to every lineup (lineups with same players should have same ID)?
I've managed to solve my problem. I had to add IDs to lineup (which I've renamed to Team) and specify how to join tables by providing two different IDs for those teams. Another way way solution posted by André.
Here is the code:
from sqlalchemy import (
Column,
Table,
Integer,
Date,
String,
ForeignKey,
)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref
engine = create_engine('sqlite:///data.sqlite')
DBSession = sessionmaker(bind=engine)
Base = declarative_base()
class Match(Base):
__tablename__ = 'data'
id = Column(Integer, primary_key=True)
date = Column(Date)
tournament = Column(String)
best_of = Column(Integer)
score = Column(String)
maps = relationship('Map')
team1_id = Column(ForeignKey('team.id'))
team2_id = Column(ForeignKey('team.id'))
team1 = relationship('Team', primaryjoin='Match.team1_id == Team.id')
team2 = relationship('Team', primaryjoin='Match.team2_id == Team.id')
class Map(Base):
__tablename__ = 'map'
id = Column(Integer, primary_key=True)
match = Column(Integer, ForeignKey('data.id'))
name = Column(String)
score = Column(String)
lineup = Table('lineup',
Base.metadata,
Column('player_id', Integer, ForeignKey('player.id')),
Column('team_id', Integer, ForeignKey('team.id')))
class Player(Base):
__tablename__ = 'player'
id = Column(Integer, primary_key=True)
nickname = Column(String)
class Team(Base):
__tablename__ = 'team'
id = Column(Integer, primary_key=True)
name = Column(String)
players = relationship('Player', secondary=lineup, backref='teams')
I have some suggestions in relationships:
Considering that you have for each match 2(two) teams and 2(two) differents line up, when you use that same match id for two different lineups happens this query mistake to return the lineups;
I am not good in sports, but I think that you can do a better normalization on models. For example: Create a new model Team
My suggestion is change some models for Many to many, like that:
from sqlalchemy import (
Column,
Table,
Integer,
Date,
String,
ForeignKey,
)
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm import relationship, backref
engine = create_engine('sqlite:///data.sqlite')
DBSession = sessionmaker(bind=engine)
Base = declarative_base()
lineup = Table('lineups', Base.metadata,
Column('team_id', Integer, ForeignKey('teams.id')),
Column('player_id', Integer, ForeignKey('players.id')))
class Team(Base):
__tablename__ = 'teams'
id = Column(Integer, primary_key=True,autoincrement= True)
name = Column(String)
team_lineup = relationship('Player',secondary=lineup)
match_team = Table('match_teams', Base.metadata,
Column('match_id', Integer, ForeignKey('matches.id')),
Column('team_id', Integer, ForeignKey('teams.id')))
class Match(Base):
__tablename__ = 'matches'
id = Column(Integer, primary_key=True,autoincrement= True)
date = Column(Date)
tournament = Column(String)
team = relationship('Team',secondary=match_team)
best_of = Column(Integer)
maps = relationship('Map')
score = Column(String)
class Map(Base):
__tablename__ = 'maps'
id = Column(Integer, primary_key=True,autoincrement= True)
match = Column(Integer, ForeignKey('matches.id'))
name = Column(String)
score = Column(String)
class Player(Base):
__tablename__ = 'players'
id = Column(Integer, primary_key=True,autoincrement= True)
nickname = Column(String)
Creating objets...
s = DBSession()
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
t1 = Team(name="t1")
t2 = Team(name="t2")
p1 = Player()
p2 = Player()
p3 = Player()
s.add(p1)
s.add(p2)
s.add(p3)
t1.team_lineup.append(p1)
t2.team_lineup.append(p2)
t2.team_lineup.append(p3)
s.add(t1)
s.add(t2)
m = Match()
m.tournament="xpto"
m.team.append(t1)
m.team.append(t2)
s.add(m)
s.commit()
And after that, you can see that the lineups is like your expectation:
>>> m = s.query(Match).first()
>>> for t in m.team:
... print t.team_lineup
...
[<__main__.Player object at 0x10b57c890>]
[<__main__.Player object at 0x10b57c910>, <__main__.Player object at 0x10b57c990>]
In this case you can have "n" teams for each match. I don't know if is possible in the sport that you are managing.
I try to add a record to a joined-inheritance table.
For some reason I cannot fathom, the dependent table is not INSERTed into.
## Inheritance test case.
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import Session, relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Employee(Base):
__tablename__ = 'personnel'
__mapper_args__ = { 'polymorphic_on': 'etyp' }
id = Column(Integer, primary_key=True)
name = Column(String(50))
etyp = Column(String(10))
class Engineer(Employee):
__mapper_args__ = { 'polymorphic_identity':'eng' }
__tablename__ = "engineers"
id = Column(Integer, ForeignKey(Employee.id), index=True)
eng_data = Column(String(50))
engine = create_engine('sqlite:///', echo=True)
Base.metadata.create_all(engine)
session = Session(bind=engine)
e1 = Engineer(name="wally", eng_data="lazy guy")
session.add(e1)
session.commit()
# note that the Engineer table is not INSERTed into
e1 = session.query(Employee).filter_by(name="wally").one()
# the next line triggers an exception because it tries to lookup
# the Engineer row, which is missing
print e1.eng_data
This is Python 2.7, sqlalchemy 0.9.4 (Debian testing).
Making engineers.id a primary key will solve the problem:
class Engineer(Employee):
# ...
id = Column(Integer, ForeignKey(Employee.id), primary_key=True)