SqlAlchemy Query Two Classes With Join [duplicate] - python

This question already has answers here:
How do I return results from both tables in a SQLAlchemy JOIN?
(1 answer)
How to return all the columns with flask-sqlalchemy query join from two tables
(1 answer)
Closed 3 years ago.
I have two classes in a Flask app joined on Users.taxonomy_id = Taxonomy.id as follows:
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
# Data.
taxonomy_id = db.Column(db.Integer, db.ForeignKey('taxonomy.id',
onupdate='CASCADE', ondelete='CASCADE'),
index=True, nullable=False)
role = db.Column(db.Enum(*ROLE, name='role_types', native_enum=False),
index=True, nullable=False, server_default='member')
active = db.Column('is_active', db.Boolean(), nullable=False,
server_default='1')
username = db.Column(db.String(24), unique=True, index=True)
organisation = db.Column(db.String(255), index=True)
email = db.Column(db.String(255), index=True, nullable=False,
server_default='')
password = db.Column(db.String(128), nullable=False, server_default='')
def __init__(self, **kwargs):
super(User, self).__init__(**kwargs)
class Taxonomy(db.Model):
__tablename__ = 'taxonomy'
id = db.Column(db.Integer, primary_key=True)
# Relationships.
user = db.relationship(User, uselist=False, backref='taxonomy',
passive_deletes=True)
# Data.
taxonomy = db.Column(db.String(80), index=True, nullable=False)
title = db.Column(db.String(80), index=True, nullable=False)
description = db.Column(db.String(500), index=True, nullable=False,
server_default='')
parent = db.Column(db.Integer, nullable=False, default=0)
member_count = db.Column(db.Integer, nullable=False, default=0)
def __init__(self, **kwargs):
super(Taxonomy, self).__init__(**kwargs)
And both work fine for queries run on them independently such as:
if form.validate_on_submit():
current_user.username = request.form.get('username')
current_user.save()
Or:
t = Taxonomy.query.filter(Taxonomy.id == current_user.taxonomy_id).first()
But I'm having trouble getting them to work as a joined query. I've been playing around for a while with different approaches and can't find anything definitive online that solves my problem. I want to create a joined query based on the classes and return the result. The following works but only returns data from the Users class and table rather than both the users and taxonomy classes:
records = User.query.join(
Taxonomy, User.taxonomy_id == Taxonomy.id).filter(
User.id == 102).first()
Can anybody please help?

Related

how to filter associated data in flask and Flask-SQLAlchemy?

I have just started learning flask and Flask-SQLAlchemy and created following models:-
User:-
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, db.Sequence('users_id_seq'), primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False)
updated_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False, onupdate=helpers.get_utc_now)
def __repr__(self):
return '<User %r>' % self.username
Teacher:-
class Teacher(db.Model):
__tablename__ = "teachers"
id = db.Column(db.Integer, db.Sequence("teachers_id_seq"), primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"))
created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False)
updated_at = db.Column(
db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False, onupdate=helpers.get_utc_now
)
def __repr__(self):
return "<Teacher %r>" % self.id
Students:-
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, db.Sequence('students_id_seq'), primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False)
updated_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False, onupdate=helpers.get_utc_now)
def __repr__(self):
return '<Student %r>' % self.id
Assignment:-
class Assignment(db.Model):
__tablename__ = "assignments"
id = db.Column(db.Integer, db.Sequence("assignments_id_seq"), primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey(Student.id), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey(Teacher.id), nullable=True)
content = db.Column(db.Text)
grade = db.Column(BaseEnum(GradeEnum))
state = db.Column(BaseEnum(AssignmentStateEnum), default=AssignmentStateEnum.DRAFT, nullable=False)
created_at = db.Column(db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False)
updated_at = db.Column(
db.TIMESTAMP(timezone=True), default=helpers.get_utc_now, nullable=False, onupdate=helpers.get_utc_now
)
def __repr__(self):
return "<Assignment %r>" % self.id
Now in my application an student can create, edit and submit an assignment.
And when student submit an assignment, it will submitted to the particular teacher.
When student creates an assignment status field in assignment set to DRAFT, and when he submitted the assignment it will changes to SUBMITTED.
I am totally new to flask and Flask-SQLAlchemy and back-end stuff,and unable to figure out how to fetch assignments submitted to particular teacher.
I have written the following query as well but got wrong result**:-**
got empty data but there are data available in db.
#classmethod # is written is assignment class
def get_assignments_by_teacher(cls, teacher_id):
return cls.filter(cls.teacher_id == teacher_id).all()
from where i am calling this method:-
teacher_assignments = Assignment.get_assignments_by_teacher(p.teacher_id)
Thanks in advance.
Hope to here from you soon.
NOTE:- you can also suggest me better title for this post as well.
If you are trying to query all assignments for a certain teacher, you can achieve this by using sqlalchemy's query function with a filter.
Docs
teacher_assignments = Assignment.query.filter(Assignment.teacher_id == teacher_id).all()
You can also add something called a backref on your teacher model definition like this:
assignments = relationship("Assignment", backref="teacher")
Then, if you have your teacher object, you can access their assignments like this without writing any queries:
teacher.assignments
See here for more info on Backref

Sqlite3 relational database

I am trying to connect the id in the user class to the user_id in task class. I read the documentation from sqlalchemy and I have a hard time understanding the db.relationship part. I have also encountered a problem when updating the tables, when I ran db.create_all() in the terminal, these classes wasn't updated (it previously worked). Is this because the relational database I set up was wrong?
Here is my classes
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), nullable=False, unique=True)
password = db.Column(db.String(80), nullable=False)
class Task(db.Model):
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
description = db.Column(db.Text)
complete =db.Column(db.Boolean, default=False)
And here is the documentation example
class Category(db.Model, CRUDMixin):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50))
# Post table (in app/models/post.py)
class Post(db.Model, CRUDMixin):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(80))
body = db.Column(db.Text)
pub_date = db.Column(db.DateTime, default=datetime.utcnow)
# One to many relationship
category_id = db.Column(db.Integer, db.ForeignKey('category.id'))
category = db.relationship('Category', backref=db.backref('posts'))
Any answers or opinions will be greatly appreciated, thankyou.

How to delete all users with specified role

These are my User and Role models:
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
firstname = db.Column(db.String(100), nullable=False, server_default='')
lastname = db.Column(db.String(100), nullable=False, server_default='')
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
answers = db.relationship('Answer', backref="examinee", lazy=True)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')
roles = db.relationship('Role', secondary='user_roles', backref=db.backref('users', lazy='dynamic'))
class Role(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
name = db.Column(db.String(50), unique=True)
description = db.Column(db.String(255))
class UserRoles(db.Model):
id = db.Column(db.Integer(), primary_key=True, autoincrement=True)
user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('role.id', ondelete='CASCADE'))
UniqueConstraint('user_id', 'role_id', name='user_role_uniqueness')
How can I remove all users with specific role such as 'student'?
I have tried the following snippet which did not work:
#app.route('/removeAllStudentUsers', methods=['GET', 'POST'])
def remove_all_student_users():
default_role_name = "student"
default_role = Role.query.filter(Role.name == default_role_name).first()
User.query.filter().delete(and_(User.roles.contains(default_role), func.count(User.roles) == 1)) # this line causes the error
db.session.commit()
return redirect(url_for('users'))
The above code causes this error:
sqlalchemy.exc.ArgumentError: Valid strategies for session synchronization are 'evaluate', 'fetch', False
There are multiple issues with your attempt to achieve that, including the fact that you might need a group by in order to check the count of the roles.
I would implement this a following:
_ = (
User.query
.filter(User.roles.any(Role.name == default_role_name)) # 1
.filter(~User.roles.any(Role.name != default_role_name)) # 2
.delete(synchronize_session=False) # 3
)
where:
makes sure to return only those User who has the given role
makes sure to exclude the User who has also other roles (this is what you try to solve with func.count)
this is the solution for the error you get.

sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class

I am beginner trying to make a relational database in Flask project using SQLalchemy
This is the error that I am getting when I try to register a user:-
sqlalchemy.exc.InvalidRequestError: When initializing mapper mapped class Question->question, expression 'Answer' failed to locate a name ('Answer'). If this is a class name, consider adding this relationship() to the <class 'nothingness.models.Question'> class after both dependent classes have been defined.
DB relationships are:-
User (Many to Many) Table
User (1 to Many) Question
Question (1 to Many) Answer
Table (1 to Many) Question
Here are my codes
from datetime import datetime
from nothingness import db
members = db.Table(
"member",
db.Column("id", db.Integer, primary_key=True),
db.Column("table_id", db.Integer, db.ForeignKey("table.id")),
db.Column("user_id", db.Integer, db.ForeignKey("user.id")),
)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(25), unique=True, nullable=False)
name = db.Column(db.String(25), nullable=False)
email = db.Column(db.String(), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default="default.jpg")
password = db.Column(db.String(60), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
member = db.relationship("Table", secondary=members, backref=db.backref("members", lazy=True))
prashna = db.relationship("Question", backref="user", lazy=True)
def __repr__(self):
return f"User('{self.name}', '{self.username}', '{self.email}', '{self.image_file}')db.Model"
class Table(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(25), nullable=False)
key = db.Column(db.String(5), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
question = db.relationship("Question", backref="questions", lazy=True)
def __repr__(self):
return f"Table('{self.id}', '{self.name}', '{self.key}', {self.created_at})"
class Question(db.Model):
id = db.Column(db.Integer, primary_key=True)
question = db.Column(db.String(255), nullable=False)
asked_by = db.Column(db.Integer, db.ForeignKey("user.id"))
asked_to = db.Column(db.Integer, nullable=False)
answer = db.relationship("Answer", backref="question", lazy=True)
table = db.Column(db.Integer, db.ForeignKey("table.id"))
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
class Answer:
id = db.Column(db.Integer, primary_key=True)
points = db.Column(db.Integer)
answer = db.Column(db.String(255), nullable=False)
answered_by = db.Column(db.Integer, nullable=False)
table_id = db.Column(db.Integer, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
question = db.Column(db.Integer, db.ForeignKey("question.id"))
def __repr__(self):
return f"Answer('{self.points}', '{self.answer}', '{self.created_at}')"
This error occurred because I forgot to subclass Answer with db.Model

SqlAlchemy Error on Creating multiple foreign key to one table

I am new using sqlAlchemy and having problem creating new tables, specially when it comes around 2 foreign keys pointing to 1 table:
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want = db.relationship('Currency', backref='want', lazy='dynamic')
have = db.relationship('Currency', backref='have', lazy='dynamic')
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
have_id = db.Column(db.Integer, db.ForeignKey('currency_pairs.id'))
The error I am getting is:
sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper|CurrencyPairs|currency_pairs'. Original exception was: Could not determine join condition b
etween parent/child tables on relationship CurrencyPairs.want - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key refe
rence to the parent table
I have try different things but I get same result.
What am I doing wrong?
Thanks In advance.
I know this is an old question, but I had the same problem. I hope to help others with the answer.
This issue is addressed in the sqlalchemy documentation.
https://docs.sqlalchemy.org/en/13/orm/join_conditions.html#handling-multiple-join-paths
class Offers(db.Model):
__tablename__ = 'offers'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
contact_ign = db.Column(db.String(100))
conversion_rate = db.Column(db.Float)
stock = db.Column(db.Integer)
create_date = db.Column(db.DateTime(timezone=True), default=func.now())
currency_pair = db.relationship('CurrencyPairs', backref='pair', lazy='dynamic')
class CurrencyPairs(db.Model):
__tablename__ = 'currency_pairs'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
league = db.Column(db.String(100))
pair_id = db.Column(db.Integer, db.ForeignKey('offers.id'))
want_currency = relationship("Currency", foreign_keys='[Currency.want_id]', back_populates="want_currency_pairs")
have_currency = relationship("Currency", foreign_keys='[Currency.have_id]', back_populates="have_currency_pairs")
class Currency(db.Model):
__tablename__ = 'currency'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(100), nullable=False)
poe_trade = db.Column(db.Integer, nullable=False)
poe_official = db.Column(db.String(10), nullable=False)
tier = db.Column(db.Integer, nullable=False)
want_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.want_id]", back_populates="want_currency")
have_currency_pairs = relationship(CurrencyPairs, foreign_keys="[Currency.have_id]", back_populates="have_currency")
The way you wrote the code, sqlalchemy can't really understand which relationship to choose, because you have 2 of the same relationship. So you have to describe to sqlalchemy that there are 2 relationships to the same table.

Categories