SqlAlchemy issues with foreign keys - python

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):
...
...

Related

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

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.

How do I query resources in the nested collection in eve-sqlalchemy?

I am using Eve-SQLAlchemy==0.5.0
I would like to perform a nested query using Postman on my users such that I find all users that are within a specified organization.
Using SQL I would write my query such that:
select * from app_user
left join user_organization on user_organization.user_id = app_user.id
left join organization on organization.id = user_organization.organization_id
where organization.id = 2
I have a user model, an organization model, and a relational model linking the two user_organization.
from sqlalchemy import Column, DateTime, func, String, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class BaseModel(Base):
id = Column(Integer, primary_key=True, autoincrement=True)
__abstract__ = True
_created = Column(DateTime, default=func.now())
_updated = Column(DateTime, default=func.now(), onupdate=func.now())
_etag = Column(String(40))
class User(BaseModel):
__tablename__ = 'app_user'
organizations = relationship("Organization", secondary=UserOrganization.__tablename__)
class Organization(BaseModel):
__tablename__ = 'organization'
name = Column(String)
class UserOrganization(BaseModel):
__tablename__ = 'user_organization'
user_id = Column(Integer,
ForeignKey('app_user.id', ondelete='CASCADE'))
organization_id = Column(Integer,
ForeignKey('organization.id', ondelete='CASCADE'))
In my settings.py I have the resources registered:
# Resource Registration
DOMAIN = DomainConfig({
'organization': ResourceConfig(Organization),
'user': ResourceConfig(User)
}).render()
I have a series of postman collections setup, and using a GET request I can easily query any attribute... GET localhost:5000/user?where={"id":1}
I have tried (amongst many other things):
GET user?where={"organizations": {"organization_id" :2 }}
GET user?where={"organizations": 2}
It seems it's not possible at the moment due to a bug. I will try to fix it within the next week.
The code in https://github.com/pyeve/eve-sqlalchemy/blob/master/eve_sqlalchemy/parser.py#L73 is causing a GET ?where={"organizations": 2} to result in a SQL expression like user_id = 42 AND organization_id = 42 is generated. Which rarely makes any sense.

Error in foreign key constraint with SQLAlchemy

I am trying to implement very simple example table from an old course now in SQLAlchemy...
I have got this far but when I run the code...
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, Date, MetaData
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
engine = create_engine('mysql://x # amazonaws.com:3306/db', echo=True)
class Guest(Base):
__tablename__ = "guests"
guest_no = Column(String(4), primary_key=True)
g_name = Column(String(20))
g_address = Column(String(30))
booking = relationship("Booking", back_populates="guests")
class Hotel(Base):
__tablename__ = "hotels"
hotel_no = Column(String(4), primary_key=True)
h_name = Column(String(20))
h_address = Column(String(30))
room = relationship("Room", back_populates="hotels")
booking = relationship("Booking", back_populates="hotels")
class Room(Base):
__tablename__ = "rooms"
hotel_no = Column(String(4), ForeignKey('hotels.hotel_no'), primary_key=True)
room_no = Column(String(4), primary_key=True)
r_type = Column(String(1))
r_price = Column(Integer)
hotel = relationship("Hotel", back_populates="rooms")
booking = relationship("Booking", back_populates="rooms")
class Booking(Base):
__tablename__ = "bookings"
hotel_no = Column(String(4), ForeignKey('hotels.hotel_no'), primary_key=True)
guest_no = Column(String(4), ForeignKey('guests.guest_no'), primary_key=True)
date_form = Column(Date, primary_key=True)
date_to = Column(Date)
room_no = Column(String(4), ForeignKey('rooms.room_no'), primary_key=True)
hotel = relationship("Hotel", back_populates="bookings")
guest = relationship("Guest", back_populates="bookings")
room = relationship("Room", back_populates="bookings")
Base.metadata.create_all(engine)
it gives me an error about the room_no foreign key...
2017-09-11 16:16:03 2b8010c29700 Error in foreign key constraint of table db/bookings:
FOREIGN KEY(room_no) REFERENCES rooms (room_no)
):
Cannot find an index in the referenced table where the
referenced columns appear as the first columns, or column types
in the table and the referenced table do not match for constraint.
Note that the internal storage type of ENUM and SET changed in
tables created with >= InnoDB-4.1.12, and such columns in old tables
cannot be referenced by such columns in new tables.
See http://dev.mysql.com/doc/refman/5.6/en/innodb-foreign-key-constraints.html
for correct foreign key definition.
I looked around a bit and I made sure they were both the same type (they were) and were both primary keys (they previously were not) but the error persists.
Does anyone have insight into what is causing this?
Because rooms has a composite primary key: (hotel_no, room_no) you'll need to specify both columns in your foreign key relationship on the booking table:
__table_args__ = (
ForeignKeyConstraint(
['hotel_no', 'room_no'],
['rooms.hotel_no', 'rooms.room_no']
),
)

Flask SQLAlchemy Foreign Key Relationships

I'm having a lot of trouble getting my head around foreign keys and relationships in SQLAlchemy. I have two tables in my database. The first one is Request and the second one is Agent. Each Request contains one Agent and each Agent has one Request.
class Request(db.Model):
__tablename__ = 'request'
reference = db.Column(db.String(10), primary_key=True)
applicationdate = db.Column(db.DateTime)
agent = db.ForeignKey('request.agent'),
class Agent(db.Model):
__tablename__ = 'agent'
id = db.relationship('Agent', backref='request', \
lazy='select')
name = db.Column(db.String(80))
company = db.Column(db.String(80))
address = db.Column(db.String(180))
When I am running db.create_all() I get the following error
Could not initialize target column for ForeignKey 'request.agent' on table 'applicant': table 'request' has no column named 'agent'
Have a look at the SqlAlchemy documentation on OneToOne relationships. First you need to supply a Primary Key for each model. Then you need to define one Foreign Key which refers to the Primary Key of the other model. Now you can define a relationship with a backref that allows direct access to the related model.
class Request(db.Model):
__tablename__ = 'request'
id = db.Column(db.Integer, primary_key=True)
applicationdate = db.Column(db.DateTime)
class Agent(db.Model):
__tablename__ = 'agent'
id = db.Column(db.Integer, primary_key=True)
request_id = db.Column(db.Integer, db.ForeignKey('request.id'))
request = db.relationship("Request", backref=backref("request", uselist=False))
name = db.Column(db.String(80))
company = db.Column(db.String(80))
address = db.Column(db.String(180))
Now you can access your models like this:
request = Request.query.first()
print(request.agent.name)
agent = Agent.query.first()
print(agent.request.applicationdate)

How to build many-to-many relations using SQLAlchemy: a good example

I have read the SQLAlchemy documentation and tutorial about building many-to-many relation but I could not figure out how to do it properly when the association table contains more than the 2 foreign keys.
I have a table of items and every item has many details. Details can be the same on many items, so there is a many-to-many relation between items and details
I have the following:
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
My association table is (It's defined before the other 2 in the code):
class ItemDetail(Base):
__tablename__ = 'ItemDetail'
id = Column(Integer, primary_key=True)
itemId = Column(Integer, ForeignKey('Item.id'))
detailId = Column(Integer, ForeignKey('Detail.id'))
endDate = Column(Date)
In the documentation, it's said that I need to use the "association object". I could not figure out how to use it properly, since it's mixed declarative with mapper forms and the examples seem not to be complete. I added the line:
details = relation(ItemDetail)
as a member of Item class and the line:
itemDetail = relation('Detail')
as a member of the association table, as described in the documentation.
when I do item = session.query(Item).first(), the item.details is not a list of Detail objects, but a list of ItemDetail objects.
How can I get details properly in Item objects, i.e., item.details should be a list of Detail objects?
From the comments I see you've found the answer. But the SQLAlchemy documentation is quite overwhelming for a 'new user' and I was struggling with the same question. So for future reference:
ItemDetail = Table('ItemDetail',
Column('id', Integer, primary_key=True),
Column('itemId', Integer, ForeignKey('Item.id')),
Column('detailId', Integer, ForeignKey('Detail.id')),
Column('endDate', Date))
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
details = relationship('Detail', secondary=ItemDetail, backref='Item')
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
items = relationship('Item', secondary=ItemDetail, backref='Detail')
Like Miguel, I'm also using a Declarative approach for my junction table. However, I kept running into errors like
sqlalchemy.exc.ArgumentError: secondary argument <class 'main.ProjectUser'> passed to to relationship() User.projects must be a Table object or other FROM clause; can't send a mapped class directly as rows in 'secondary' are persisted independently of a class that is mapped to that same table.
With some fiddling, I was able to come up with the following. (Note my classes are different than OP's but the concept is the same.)
Example
Here's a full working example
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import declarative_base, relationship, Session
# Make the engine
engine = create_engine("sqlite+pysqlite:///:memory:", future=True, echo=False)
# Make the DeclarativeMeta
Base = declarative_base()
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String)
projects = relationship('Project', secondary='project_users', back_populates='users')
class Project(Base):
__tablename__ = "projects"
id = Column(Integer, primary_key=True)
name = Column(String)
users = relationship('User', secondary='project_users', back_populates='projects')
class ProjectUser(Base):
__tablename__ = "project_users"
id = Column(Integer, primary_key=True)
notes = Column(String, nullable=True)
user_id = Column(Integer, ForeignKey('users.id'))
project_id = Column(Integer, ForeignKey('projects.id'))
# Create the tables in the database
Base.metadata.create_all(engine)
# Test it
with Session(bind=engine) as session:
# add users
usr1 = User(name="bob")
session.add(usr1)
usr2 = User(name="alice")
session.add(usr2)
session.commit()
# add projects
prj1 = Project(name="Project 1")
session.add(prj1)
prj2 = Project(name="Project 2")
session.add(prj2)
session.commit()
# map users to projects
prj1.users = [usr1, usr2]
prj2.users = [usr2]
session.commit()
with Session(bind=engine) as session:
print(session.query(User).where(User.id == 1).one().projects)
print(session.query(Project).where(Project.id == 1).one().users)
Notes
reference the table name in the secondary argument like secondary='project_users' as opposed to secondary=ProjectUser
use back_populates instead of backref
I made a detailed writeup about this here.
Previous Answer worked for me, but I used a Class base approach for the table ItemDetail. This is the Sample code:
class ItemDetail(Base):
__tablename__ = 'ItemDetail'
id = Column(Integer, primary_key=True, index=True)
itemId = Column(Integer, ForeignKey('Item.id'))
detailId = Column(Integer, ForeignKey('Detail.id'))
endDate = Column(Date)
class Item(Base):
__tablename__ = 'Item'
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(Text)
details = relationship('Detail', secondary=ItemDetail.__table__, backref='Item')
class Detail(Base):
__tablename__ = 'Detail'
id = Column(Integer, primary_key=True)
name = Column(String)
value = Column(String)
items = relationship('Item', secondary=ItemDetail.__table__, backref='Detail')

Categories