What am I doing wrong in my SQLAlchemy model and column property? - python

In my Flask + SQLAlchemy application I have - besider others - these two DB tables/models:
class Client(db.Model):
__tablename__ = "clients"
id = Column(Integer, primary_key=True)
client_name = Column(String, nullable=False, unique=True)
information = Column(String)
class ImageDataSet(db.Model):
__tablename__ = "image_data_sets"
id = Column(Integer, primary_key=True)
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
client = db.column_property(db.select([Client.client_name]).where(Client.id == client_id))
So in other words I want to have an attribute client in my model ImageDataSets, based on the client_id from the Client model/table. This works, however, when starting the application I get the following warning for my call to db.column_property:
SAWarning: implicitly coercing SELECT object to scalar subquery; please use the .scalar_subquery() method to produce a scalar subquery.
Any ideas what I am doing wrong here?

Thanks to Ilja Everilä's comment I solved this by changing the column property to the following:
client = db.column_property(db.select([Client.client_name]).where(Client.id == client_id).scalar_subquery())
Now the warning is gone.

Related

Querying a table from another table using SQLAlchemy

I am new to programming and learning about relational databases using SQLAlchemy, Python and Flask.
I want to know if it's possible and if so, how to get information referencing one table which is connected to multiple others. For example, I have the below table connected to another (using SQLAlchemy):
class Venue(db.Model):
__tablename__ = 'venue'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(25), unique=True)
location = db.relationship('Vlocation', backref='venue', cascade='all', lazy='dynamic')
def __rep__(self):
f'Venue: <{self.id}, {self.name}>'
class Vlocation(db.Model):
__tablename__ = 'vlocation'
id = db.Column(db.Integer, primary_key=True)
venue_id = db.Column(db.Integer, db.ForeignKey('venue.id', ondelete='CASCADE'), nullable=False)
address = db.Column(db.String(120))
city = db.Column(db.String(120))
state = db.Column(db.String(3))
Besides directly querying the class model Vlocation, like this: db.session.query(Vlocation.city, Vlocation.state).all(), is there a way to get this information by querying the class model Venue? I tried this: db.session.query(Venue.location.city, Venue.location.state).all(), but I got the following error: AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with Venue.location has an attribute 'city'. Is there a better way to do this?
Maybe you can try this
vlocations = []
for venue in Venue.query.all():
vlocations.extened(venue.location)
vlocations = list(set(vlocations))

How to set the default value of a column in sqlalchemy to the value of a column from a relationship?

I am setting up a Sqlalchemy mapper for a sqlite database. My User class has a non-nullable relationship with my Team class. The code I already have is as follows:
class Team(Base):
__tablename__ = 'teams'
team_id = Column(Integer, primary_key=True)
# Using Integer as holder for boolean
is_local = Column(Integer, default=0)
class User(Base):
__tablename__ = 'users'
user_id = Column(Integer, primary_key=True)
team_id = Column(Integer, ForeignKey(Team.team_id), default=1, nullable=False)
team = relationship('Team')
is_local = Column(Integer, default=0)
I would like to establish that the value of User.is_local is by default the value of Team.is_local for the User's linked team.
However, after the creation of the User, I would still like the ability to modify the user's is_local value without changing the values of the team or any other user on the team.
So if I were to execute
faraway = Team(is_local=1)
session.add(faraway)
session.commit()
u = User(team=faraway)
session.add(u)
session.commit()
print(bool(u.is_local))
The result should be True
So far, I have tried context-sensitive default functions as suggested by https://stackoverflow.com/a/36579924, but I have not been able to find the syntax allowing me to reference Team.is_local
Is there a simple way to do this?
The first suggestion from SuperShoot, using a sql expression as the default appears to work. Specifically,
is_local = Column(Integer, default=select([Team.is_local]).where(Team.team_id==team_id))
gives me the logic I require.

Flask / SQLAlchemy - Request tables with many-to-many relationships

I used Flask and SQLAlchemy to create an application based on a database. Here is the classes that I have defined:
models.py
class HasTag(db.Model):
tagged_document_id = db.Column(db.Integer, db.ForeignKey('Document.id'), primary_key=True)
document_tag_id = db.Column(db.Integer, db.ForeignKey('Tag.id'), primary_key=True)
class Document(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
title = db.Column(db.Text)
tag = db.relationship("Tag",
secondary=HasTag,
back_populates="tagged_document",
lazy="dynamic")
class Tag(db.Model):
id = db.Column(db.Integer, unique=True, nullable=False, primary_key=True, autoincrement=True)
label = db.Column(db.String, nullable=False)
tagged_document = db.relationship("Document",
secondary=HasTag,
back_populates="tag",
lazy="dynamic")
In the application, I have an advanced search form where it is possible to do a full text search through the different fields of the Document table.
routes.py
#app.route("/search")
def search():
keyword = request.args.get("keyword", None)
query = Document.query
if keyword:
query = Document.query.filter(or_(
Document.title.like("%{}%".format(keyword)),
...
))
The thing is, I'd like to be able to search the keyword given by the user also in the label of the tag. I tried something like:
if keyword:
query = Document.query.join(Tag).filter(or_(
Document.title.like("%{}%".format(keyword)),
...,
Tag.label.like("%{}%".format(keyword))
))
But I get this error: AttributeError: 'HasTag' object has no attribute 'foreign_keys'
Can you help me? Thanks!
I have a similar structure in one of my projects, and this is how I define relatioship:
leagues = db.relationship("League",
secondary=LeagueTeamAssociation.__tablename__,
back_populates="teams")
So, You need to provide table name to secondary parameter, either using above syntax (You'll need to add __tablename__ to your HasTag class) or using string "has_tag" (provided that this is the name of the table in the database).

SqlAlchemy issues with foreign keys

I am getting the error
Could not parse rfc1738 URL from string 'MACHINE_IE'
When I attempt to import the following
class MACHINE(declarative_base()):
__tablename__ = 'MACHINE'
MACHINE_UID = Column(Integer, primary_key=True)
MACHINE_IP = Column(String)
MACHINE_NAME = Column(String)
MACHINE_INFO = Column(String)
class IE(declarative_base()):
__tablename__ = 'IE'
IE_UID = Column(Integer, primary_key=True)
IE_VERSION = Column(String)
IE_MAJOR = Column(String)
class MACHINE_IE(declarative_base().metadata):
__tablename__ = 'MACHINE_IE'
IE_UID = Column(Integer, ForeignKey("IE.IE_UID"), primary_key=True)
MACHINE_UID = Column(Integer, ForeignKey("MACHINE.MACHINE_UID"))
EFFECTIVE_DATE = Column(DateTime)
If I remove
metadata
then I can import the module and perform a query on the "MACHINE" and "IE" tables and display the data from the rows. However when I query "MACHINE_IE" and then try to display some data from the query I get the following
sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'MACHINE_IE.IE_UID' could not find table 'IE' with which to generate a foreign key to target column 'IE_UID'
I am guessing I need "metadata" in there but the error seems to be complaining about the db connection string when I use it. However I am able to connect to the db w/o issue if "metadata" is removed.
Here is the connection data
connect_string = "oracle+cx_oracle://{0}:{1}#{2}:{3}/{4}".format(user, pw, server, port, sid)
Any assistance is appreciated.
You are using multiple instances of Base. You should be doing:
Base = declarative_base()
class MACHINE(Base):
...
class IE(Base):
...
...

SQLAlchemy column_property basics

I have two models:
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
class ReportPhoto(Base):
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
report_id = Column(Integer, ForeignKey(Report.id), nullable=False)
report = relationship(Report, uselist=False, backref=backref('report_photo', uselist=True))
And I would like to add column to Report model which indicates is there any records within ReportPhoto. I try to use column_property this way:
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
has_photo = column_property(
select(ReportPhoto.any())
)
but get an error NameError: name 'ReportPhoto' is not defined. How I can fix this issue?
something like that should work:
class ReportPhoto(Base):
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
report_id = Column(Integer, ForeignKey('report.id'), nullable=False)
class Report(Base):
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
report_photos = relationship(ReportPhoto, backref='report')
has_photo = column_property(
exists().where(ReportPhoto.report_id==id)
)
I will add to #Vladimir lliev's response with some clarification for anyone else that might not see how to do this.
Place the table that will have the 'foreign table referencing' column_property after that which it references. In this case, it means placing Report after ReportPhoto. This will solve your NameError, however, you would be left with a new error on your ReportPhoto foreign key reference. To solve this, place your foreign key table reference in quotes. You can read more by referencing the declarative documentation (e.g., declarative.py) and looking under "Configuring Relationships" --- specifically, read the portion on quoting your foreign references.
With your code, this would look like:
class ReportPhoto(Base):
# This now goes first
__tablename__ = 'report_photo'
id = Column(Integer, primary_key=True)
# Notice the quotations around Report references here
report_id = Column(Integer, ForeignKey("Report.id"), nullable=False)
# Notice the quotations around Report references here
report = relationship("Report",
uselist=False,
backref=backref("report_photo", uselist=True))
class Report(Base):
# This is now _after_ ReportPhoto
__tablename__ = 'report'
id = Column(Integer, primary_key=True)
# ReportPhoto now exists and we will not trip a NameError exception
has_photo = column_property(
select(ReportPhoto.any())
)

Categories