sqlalchemy query(join) to retrieve sum of votes for a post - python

I have 3 models
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True, nullable=False)
content = Column(String, nullable=False)
published = Column(Boolean)
owner_id = Column(Integer, ForeignKey("users.id"))
owner = relationship("User")
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True, index=True)
email = Column(String)
password = Column(String)
class Vote(Base):
__tablename__ = "votes"
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
post_id = Column(Integer, ForeignKey("posts.id"), primary_key=True)
I would like to generate a query that retrieves one or all posts and automatically sum up all of the votes for said post. I know how to do this in sql but not sure how to accomplish this using SQLAlchemy.
The sql query would look like this:
SELECT posts.*, count(votes.post_id) as votes from posts left join votes on (posts.id = votes.post_id) where posts.id = 1 group by posts.id;
Also is there a way to make it so i don't have to create a custom query and instead have the post model automatically populate the sum of all votes with a reference or something like that?

Related

SQLAlchemy Association Proxy distinct right side

Given the models below, how can I ensure that the Users returned by the Document.users association proxy are distinct?
For example, the DocumentUser association table can have the follow data
document_id
user_id
role_id
Document_1
User_1
Role_1
Document_1
User_1
Role_2
Document_2
User_2
Role_1
To demo, the models are laid out as:
class Document(db.Model):
__tablename__ = 'documents'
id = db.Column(db.String, primary_key=True)
title = db.Column(db.String)
document_user_associations = db.relationship(
'DocumentUser',
lazy='select',
back_populates='document'
)
users = association_proxy('document_user_associations', 'user')
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.String, primary_key=True)
full_name = db.Column(db.String)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.String, primary_key=True)
name = db.Column(db.String)
class DocumentUser(db.Model):
__tablename__ = 'document_users'
document_id = db.Column(db.String, db.ForeignKey('documents.id') primary_key=True)
user_id = db.Column(db.String, db.ForeignKey('users.id'), primary_key=True)
role_id = db.Column(db.String, db.ForeignKey('roles.id'), primary_key=True)
document = db.relationship('Document', back_populates='document_user_associations')
user = db.relationship('User')
role = db.relationship('Role')
If I were to do the following, assuming the data in the above table is persisted,
document_1 = db.session.query(Document).get(1)
print(document_1.users)
I would hope to see [<User User_1>] but instead I get [<User User_1>, <User User_1>] due to the association object. Is it possible using the association_proxy to return distinct Users given the above model?
EDIT:
In effect, it would be ideal if the association_proxy, or the association table relationship it relies on, to be able to group by the user, providing a list of the roles.

Use Flask-SqlAlchemy to query relationship database

I'm trying to use Flask-SQLAlchemy to query out database for the user profile page
So far I don't have a solution for this problem, only able to query all the User data by using users.query.all()
Each user has their own role_id, department_id, researchfield_id.
How can i query out all the Role, Department, ResearchField data that has relationship with User through ID?
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
research_id = db.Column(db.Integer, db.ForeignKey('researchfields.id'))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
user = db.relationship('User', backref='department',
lazy='dynamic')
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
users = db.relationship('User', backref='role',
lazy='dynamic')
class ResearchField(db.Model):
__tablename__ = "researchfields"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(60), index=True)
parent_id = db.Column(db.Integer, db.ForeignKey("researchfields.id") , nullable=True)
users = db.relationship('User', backref='researchfield', lazy='dynamic')
If I understand correctly, what you're seeking for is a way to filter out users based on a specific model. Because in your example, the other way around is redundant - every user has only one department, so no need to filter out departments for that user. In order to achieve that, I would use the backref method provided by SQLAlchemy from the User model.
Here's an example consisting of two of the models:
from sqlalchemy.orm import backref
class User(UserMixin, db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
password_hash = db.Column(db.String(128))
is_admin = db.Column(db.Boolean, default=False)
department_id = db.Column(db.Integer, db.ForeignKey('departments.id'))
department = db.relationship("Department", backref=backref("users", lazy="dynamic"))
class Department(db.Model):
__tablename__ = "departments"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(sqlalchemy.types.NVARCHAR(100), unique=True)
Now you can use:
department = Department.query.filter_by(id=1).first()
print(department.users.filter_by(is_admin=True).all()) # get all admins with that department
Every user has only one department, so you could just get the user's department by:
user = User.query.filter_by(id=1).first()
print(user.department) # prints Department object

How can I attach generic `Contact` to multiple different tables?

I'm trying to create a set of tables which can all have a Contactassigned to them.
class Contact(Base):
__tablename__ = "contact"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the contact.")
phone = Column(String, index=True, doc="Phone number of the contact.")
Contacts can be linked to from various other tables, and one record can have more than one contact in different fields.
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the group.")
second_contact = Column(Integer, ForeignKey("contact.id"), doc="Second contact details for the group.")
class Vendor(Base):
__tablename__ = "vendor"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the vendor.")
contact = Column(Integer, ForeignKey("contact.id"), doc="Main contact details for the vendor.")
This setup seems to work, but in flask-admin, no contact fields show up when creating a new item for either BusinessGroup or Vendor.
How can I make this design work? Or should I be modelling this kind of relationship in a different way entirely?
I ended up subclassing the Contact table so that I could have:
class MainContact(Contact):
__tablename__ = "main_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="main_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class SecondContact(Contact):
__tablename__ = "second_contact"
id = Column(Integer, ForeignKey("contact.id"), primary_key=True)
business_group = relationship("BusinessGroup", back_populates="second_contact")
business_group_id = Column(Integer, ForeignKey("business_group.id"), nullable=False)
...
class BusinessGroup(Base):
__tablename__ = "business_group"
id = Column(Integer, primary_key=True)
name = Column(String, index=True, nullable=False, doc="Name of the group.")
main_contact = relationship(
"MainContact", back_populates="business_group", uselist=False,
doc="Main contact details for the business_group."
)
second_contact = relationship(
"SecondContact", back_populates="business_group", uselist=False,
doc="Second contact details for the business_group."
)
As well as requiring subclassing so that we can have two different contacts referred to from the same model, the other important part was adding the foreign key relationships, so that the contacts show up in the Flask Admin panel.

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.

How to inserting data into interrelated relationship tables using SQLAlchemy (not flask_sqlalchemy or flask-merge)?

I am new to SQLAlchemy, I am trying to build a practice project using SQLAlchemy. I have created the database containing tables with the following relationship. Now my questions are :
How to INSERT data into the tables as they are interdependent?
Does this form a loop in database design?
Is the looping database design, a bad practice? How to resolve this if its a bad practice?
Department.manager_ssn ==> Employee.SSN
and
Employee.department_id ==> Department.deptid
database relationship diagram
and following is the current version of code creating this exact database.
# Department table
class Departments(Base):
__tablename__ = "Departments"
# Attricutes
Dname= Column(String(15), nullable=False)
Dnumber= Column(Integer, primary_key=True)
Mgr_SSN= Column(Integer, ForeignKey('Employees.ssn'), nullable=False)
employees = relationship("Employees")
# Employee table
class Employees(Base):
__tablename__ = "Employees"
# Attributes
Fname = Column(String(30), nullable=False)
Minit = Column(String(15), nullable=False)
Lname = Column(String(15), nullable=False)
SSN = Column(Integer, primary_key=True)
Bdate = Column(Date, nullable=False)
Address = Column(String(15), nullable=False)
Sex = Column(String(1), default='F', nullable=False)
Salary = Column(Integer, nullable=False)
Dno = Column(Integer, ForeignKey('Departments.Dnumber'), nullable=False)
departments = relationship("Departments")
Please provide the solution in SQLAlchemy only and not in flask-sqlalchemy or flask-migrate and I am using Python 3.6.
You can avoid such circular reference design altogether by
Declaring the foreign key constraint on just one side of the relationship
Use a boolean flag to denote if the employee is a manager
class Department(Base):
__tablename__ = 'departments'
department_id = Column(Integer, primary_key=True)
employees = relationship('Employee', lazy='dynamic', back_populates='department')
class Employee(Base):
__tablename__ = 'employees'
employee_id = Column(Integer, primary_key=True)
is_manager = Column(Boolean, nullable=False, default=False)
department_id = Column(Integer, ForeignKey('departments.department_id'), nullable=False)
department = relationship('Department', back_populates='employees')
You can find the manager of the department using
department = session.query(Department).get(..)
department.employees.filter(Employee.is_manager == True).one_or_none()

Categories