I'm trying to set up a relationship between two tables which allows me to reach obj1.obj2.name where obj1 is one table, and obj2 is another table. Relationship is one-to-one (one person to one geographical region)
# Table one (Person)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
region = db.Column(db.Integer, db.ForeignKey('region.id'))
# Table two (Region)
class Region(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
If I use Person.region (where Person is an object of Person class) I get the int of the primary key of the region of the user, but I would like to get the 'name' field associated with it.
I've figured out that this would work:
region = models.Region.query.filter_by(id=REGION_ID).first().name
but it's not applicable in my case since I need to access the 'name' field from a Flask template.
Any thoughts?
Here I basically use your model, but:
1) changed the name of the FK column
1) added a relationship (please read Relationship Configuration part of the documentation)
class Person(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100))
# #note: renamed the column, so that can use the name 'region' for
# relationship
region_id = db.Column(db.Integer, db.ForeignKey('region.id'))
# define relationship
region = db.relationship('Region', backref='people')
class Region(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
With this you are able to get the name of the region as below:
region_name = my_person.region.name # navigate a 'relationship' and get its 'name' attribute
In order to make sure that the region is loaded from the database at the same time as the person is, you can use joinedload option:
p = (db.session.query(Person)
.options(db.eagerload(Person.region))
.get(1)
)
print(p)
# below will not trigger any more SQL, because `p.region` is already loaded
print(p.region.name)
Related
im using an association_proxy like this:
study_participantions = association_proxy("quests", "study_participant",creator=lambda sp: sp.questionnaire)
I my Db there is:
A Patient table
A StudyParticipant table
A Questionnaire table
Patient and Questionnaire are a Many-To-One relationship
A Questionnaire can be Part of a StudyParticiapnt via a One-To-One relationship
StudyParticipant and Patient are not directly linked since StudyParticipant can be annonymous.
So via getter and setter i can query the Patient trough the questionnaire.
Since im working with an existing codebase I have to keep the patient inside the questionnaire
The StudyParticipant can be find via the proxy from the Patient. getting and setting works but if the Questionnaire is not part of a StudyParticiapnt the returned array contains None values is it possible to filter them out so i would get a clean array? For sure it should still be an
sqlalchemy.ext.associationproxy._AssociationList so appending and removing to it would still work.
Simplified Classes:
class Patient(Model):
__tablename__ = 'patient'
id = Column(Integer, primary_key=True)
study_participantions = association_proxy("quests", "study_participant",creator=lambda sp: sp.questionnaire)
class StudyParticipant(Model): #better name would be participation
__tablename__ = "study_participant"
id = Column(Integer, primary_key=True)
pseudonym = Column(String(40), nullable = True)
questionnaire = relationship("Questionnaire", backref="study_participant",uselist=False) # why go via the StudyQuestionnaire
class Questionnaire(Model, metaclass=QuestionnaireMeta):
__tablename__ = 'questionnaire'
id = Column(Integer, primary_key=True)
patient_id = Column(Integer(), ForeignKey('patient.id'), nullable=True)
patient = relationship('Patient', backref='quests',
primaryjoin=questionnaire_patient_join)
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.
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)
I have a problem with SQL Alchemy, while trying to think about an SQL schema I encountered the following problem.
My schema is based on 2 classes, Flight and Trip.
A Trip includes 2 fields: flights_to and flights_from.
Any of the fields is basically a list of flights, it could be made of one flight, or many flights (Connection flights).
class Trip(Base):
__tablename__ = "Trip"
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key = True)
flights_to = relationship("Flight", backref="Trip")
flights_from = relationship("Flight", backref="Trip")
class Flight(Base):
__tablename__ = "Flight"
__table_args__ = {'sqlite_autoincrement': True}
id = Column(Integer, primary_key = True)
arrival_airport = Column(String(20))
departure_airport = Column(String(20))
flight_number = Column(Integer)
trip_id = Column(Integer, ForeignKey('Trip.id'))
The problem happens when I create 2 fields in the same type:
sqlalchemy.exc.ArgumentError: Error creating backref 'Trip' on relationship 'Trip.flights_from': property of that name exists on mapper 'Mapper|Flight|Flight'
I have thought about using 2 inheriting classes of types FlightTo and FlightFrom and saving them at two different tables, but what if I want to use a FlightFrom as a FlightTo? will the flight be duplicated in 2 tables?
I would appreciate your help.
backref is used to define a new property on the other class you are using relationship with. So you can't have two property which have the same name
You should rename your backref for the flights_from to any other name than Trip.
It will work then.
For Example:
class Person(Model):
id = Column(Integer, primary_key=True)
name = Column(String)
address = relationship("Address",backref="address")
class Address(Model):
id = Column(Integer, primary_key=True)
house_no = Column(Integer)
person_id = Column(Integer, ForeignKey('person.id'))
So you can access the person name with house_no 100 by:
query_address = Address.query.filter_by(house_no=100).first()
person = query_address.address
This returns you the person object.
Thus if you have multiple such names , it will give you an error
I'm a little confused over the use of the two modules from SQLAlchemy. This is the code I have:
Base = declarative_base()
class Restaurant(Base):
__tablename__ = 'restaurant'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
class MenuItem(Base):
__tablename__ = 'menu_item'
name =Column(String(80), nullable = False)
id = Column(Integer, primary_key = True)
description = Column(String(250))
price = Column(String(8))
course = Column(String(250))
restaurant_id = Column(Integer,ForeignKey('restaurant.id'))
restaurant = relationship(Restaurant)
I understand that ForeignKey is used to define the foreign key relationship between the restaurant_id column of menu_item table and the id column of restaurant table. But why then is restaurant = relationship(Restaurant) used?
restaurant_id will refer to an id (the column value). restaurant will refer to a Restaurant instance that will be lazy loaded from the db on access (or eager loaded if you set up the right stuff earlier). If you set backref on the relationship you can also access a list of MenuItem objects from a Restaurant.