SQLAlchemy generating query in loop with contains - python

I'm building a web app in Flask that involves some SQLAlchemy. I have a function "get_team()" that is driving me bonkers.
def get_team(playerlist)
cond = and_(*[Team.users.contains(p) for p in playerlist ])
q = Team.query.filter(cond)
...
I keep getting the error: "AttributeError: 'unicode' object has no attribute '_sa_instance_state'". Here's what the models look like:
class Team(db.Model):
__tablename__ = 'teams'
id = db.Column(db.Integer, primary_key=True)
users = association_proxy("user_associations", "user", creator=lambda u: Teaming(user=u))
def __repr__(self):
s = ""
for u in self.users:
s = s + u.username + ", "
return "Team {tid}: {users_string}".format(tid = self.id, users_string=s)
class Teaming(db.Model):
__tablename__ = 'teaming'
team_id = db.Column(db.Integer, db.ForeignKey('teams.id'), primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
user = db.relationship("User", backref="team_associations")
team = db.relationship("Team", backref="user_associations")
def __repr__(self):
return "".format(uid = self.user_id, tid = self.team_id)
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)
teams = association_proxy("team_associations", "team", creator = lambda t: Teaming(team=t))
Any direction would be greatly appreciated!

Most likely cause is that playerlist is not a list of User instances, but some strings (maybe names). contains works only on model instances. If you would like to work with other attributes, then use any.
def get_team_of_players(playerlist):
cond = and_(*[Team.users.contains(p) for p in playerlist])
q = Team.query.filter(cond)
return q
p1, p2 = db.session.query(User).get(1), db.session.query(User).get(2)
q = get_team_of_players([p1, p2]).all()
def get_team_of_usernames(usernames):
cond = and_(*[Team.users.any(User.username == p) for p in usernames])
q = Team.query.filter(cond)
return q
p1, p2 = 'user1', 'user2'
q = get_team_of_usernames([p1, p2]).all()

Related

Undefined attribute error for accessing relation

I have defined two model with relation between them as below:
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), nullable=False, unique=False)
db.relationship('User', backref='role', lazy='dynamic')
def __init__(self, name):
self.name = name
def __repr__(self):
return f'<Role id={self.id} name={self.name}>'
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(40), unique=True)
password = db.Column(db.String(40), unique=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __init__(self, username, password, role_id):
self.username = username
self.password = password
self.role_id = role_id
def __repr__(self):
return f'<User id={self.id} username={self.username} password={self.password}>'
Then inside shell I created entries as below:
> admin_role = Role('Admin')
> db.session.add(admin_role)
> db.session.commit()
> admin_user = User('adminusername', 'adminpassword',admin_role.id)
> db.session.add(admin_user)
> db.session.commit()
When I try to query model I get perfect result:
>>> admin_role = Role.query.first()
>>> admin_user = User.query.first()
>>> print(admin_role)
<Role id=1 name=Admin>
>>> print(admin_user)
<User id=1 username=adminusername password=adminpassword>
But when I try to access relation
print(admin_role.users)
print(admin_user.role)
I get errors Role object has no attribute users and User object has no attribute role respectively.
Typo? You have to assign db.relationship() instance to a variable.
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(40), nullable=False, unique=False)
- db.relationship('User', backref='role', lazy='dynamic')
+ users = db.relationship('User', backref='role', lazy='dynamic')

Why does many -to-many relationship in SQLAlchemy showing Nonetype?

Base = declarative_base()
class Products(Base):
__tablename__ = 'tb_products'
#__table_args__ = {'extend_existing': True}
prod_id = Column(Integer, primary_key=True)
prod_category = Column(Integer,ForeignKey('tb_categories.ctg_id'))
category = relationship("Categories",back_populates="product") # many to one
prod_name = Column(String(100), nullable=False)
description = Column(String(100), nullable=False)
image = Column(String(100), nullable=False,default='default.jpg')
qty_available = Column(Integer, nullable=False,default=0)
regular_price = Column(DECIMAL,nullable=False,default=0.0)
discounted_price = Column(DECIMAL,nullable=False,default=0.0)
product_rating = Column(DECIMAL,nullable=False,default=0.0)
def __init__(self, prod_name = "test", description = "Dummy desciption",image =
"Dummy.png",qty_available = 10,regular_price = 1000,discounted_price =
800,product_rating = 0.0):
self.prod_name = prod_name
self.description = description
self.image = image
self.qty_available = qty_available
self.regular_price = regular_price
self.discounted_price = discounted_price
self.product_rating = product_rating
class Cart(Base):
__tablename__ = 'tb_cart'
#__table_args__ = {'extend_existing': True}
cart_id = Column(Integer, ForeignKey('tb_user.userid'), primary_key=True)
user = relationship("User",back_populates='cart') # one to one
class Cart_Products(Base):
__tablename__ = 'tb_cart_products'
__table_args__ = {'extend_existing':True}
prod_id = Column(Integer, ForeignKey('tb_products.prod_id'),primary_key=True)
cart_id = Column(Integer, ForeignKey('tb_cart.cart_id'),primary_key=True)
products = relationship("Products")
When i print tried printing
cartObj = Cart()
cart_productsObj = Cart_Products()
userObj.cart = cartObj
cart_productsObj.products.append(productObj)
userObj.cart.cart_products.append(cart_productsObj)
print(f"&&&&&&&{type(cartObj.cart_products)}")
print(f"&&&&&&&{type(cart_productsObj.products)}")
print(f"$$$$$$${type(userObj.cart)}")
print(f"$$$$$$${type(cartObj.user)}")
It is showing me following error
File "/home/devendra/Python_WS/ecommerce-project/app.py", line 248, in addToCart
cart_productsObj.products.append(productObj)
AttributeError: 'NoneType' object has no attribute 'append'
i was facing same problem when i was implementing one-to-one relation between user and cart. it was solved after i dropped whole database and created a new one.but i want to understand why does these error occurs
Also i want use Association objects for many-to-many so that i don't have to use manual queries.

SQLAlchemy Relationships Many to Many X 2

I am trying to connect a transaction table to a member table where there are many transactions per member but there are two members per transactions (a buyer and a seller). How do I create a relationship that will allow me to grab a memberID for the seller and a memberID for a buyer for each transaction?
class Member(db.Model, UserMixin):
__tablename__ = 'members'
id = db.Column(db.Integer, primary_key = True)
# exchangeID = db.Column(db.Integer(6),db.ForeignKey('exchanges.exchangeID'),nullable=False)
company_name = db.Column(db.String(64), nullable=False)
category = db.Column(db.String(64), nullable=False, default='Other')
description = db.Column(db.Text)
profile_image = db.Column(db.String(20), nullable=False, default='default_profile.png')
email = db.Column(db.String(64), unique=True, index=True)
# username = db.Column(db.String(64), unique=True, index=True, nullable=False)
# password_hash = db.Column(db.String(128), nullable=False)
address = db.Column(db.String(128))
phone = db.Column(db.Integer, nullable=False)
fein = db.Column(db.Integer)
webaddress = db.Column(db.String(64))
twitter = db.Column(db.String(24))
exchange_approved = db.Column(db.Boolean, default=False)
users = db.relationship('User', backref='company',lazy=True)
transactions = db.relationship('Transaction', backref='company',lazy=True)
listings = db.relationship('Listing', backref='company',lazy=True)
credit = db.relationship('Credit', backref='company',lazy=True)
def __init__(self,exchange_name,company_name,category,mail,address,phone,fein):
# self.exchange_name = exchange_name
self.company_name = company_name
self.category = category
self.email = email
self.address = address
self.phone = phone
self.fein = fein
def __repr__(self):
return f"Company Name: {self.company_name}"
class User(db.Model, UserMixin):
__tablename__ = 'users'
members = db.relationship(Member)
id = db.Column(db.Integer, primary_key = True)
companyID = db.Column(db.Integer, db.ForeignKey('members.id'),nullable=False)
buy_transactions = db.relationship('Transaction', backref='buyer',lazy=True)
sell_transactions = db.relationship('Transaction', backref='seller',lazy=True)
# exchangeID = db.Column(db.Integer(6), db.ForeignKey('exchanges.exchangeID'),nullable=False)
email = db.Column(db.String(64), unique=True, index=True, nullable=False)
username = db.Column(db.String(64), unique=True, index=True, nullable=False)
password_hash = db.Column(db.String(128), nullable=False)
phone_number = db.Column(db.Integer, nullable=False)
user_type = db.Column(db.String(14))
member_approved = db.Column(db.Boolean, default=False)
limited_trade = db.Column(db.Boolean, default=True)
member_imposed_limit = db.Column(db.Integer, default=0)
def __init__(self,username,password,company_name,email,phone):
self.username = username
self.password_hash = generate_password_hash(password)
self.companyID = companyID
self.email = email
self.phone = phone
def check_password(self,password):
# https://stackoverflow.com/questions/23432478/flask-generate-password-hash-not-constant-output
return check_password_hash(self.password_hash,password)
def __repr__(self):
return f"UserName: {self.username}"
class Transaction(db.Model, UserMixin):
__tablename__ = 'transactions'
members = db.relationship(Member)
transactionID = db.Column(db.Integer, primary_key=True)
date = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
companyID = db.Column(db.Integer, db.ForeignKey('members.id'),nullable=False)
sellerID = db.Column(db.Integer, db.ForeignKey('users.id'),nullable=False)
buyerID = db.Column(db.Integer, db.ForeignKey('users.id'),nullable=False)
seller = relationship("User", foreign_keys='Transaction.sellerID')
buyer = relationship("User", foreign_keys='Transaction.buyerID')
amount = db.Column(db.Numeric(5,2), nullable=False)
commission = db.Column(db.Numeric(5,2), nullable=False)
transactionDate = db.Column(db.DateTime, server_default=db.func.now())
approved = db.Column(db.Boolean, default=False)
commission_paid = db.Column(db.Boolean, default=False)
posted = db.Column(db.Boolean, default=False)
def __init__(self,sellerID,buyerID,amount):
self.sellerID = sellerID
self.buyerID = buyerID
self.amount = amount
def __repr__(self):
return f"Trasaction Id: {self.transactionID} --- Date: {self.date} --- Amount: {self.amount}"
I am getting a 'relationship' not defined but that is obvious as I don't understand how to connect the tables as described above.
What you are missing here is an association table, that should link the two tables which need to have many-many relationship. Below is a very simple example of a many-many relationship which should give you an idea on how to proceed from there.
This is how your models.py should look like
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + 'testdb.sql'
db = SQLAlchemy(app)
# Please note the below association table needs to be actual database table and not a model class.
stu_subs = db.Table('stu_subs', db.Column('student_id', db.Integer, db.ForeignKey('students.id')),
db.Column('subject_id', db.Integer, db.ForeignKey('subjects.id')))
class Student(db.Model):
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(30))
# Note the secondary attribute below, that actually sets up a many-to-many relationship
subj = db.relationship('Subjects', secondary=stu_subs, backref=db.backref('student', lazy='dynamic'))
class Subjects(db.Model):
__tablename__ = 'subjects'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20))
Finally this is how you are going to access the table fields from each table.
>>> db.create_all()
>>> from dummy import Student, Subjects
>>> student1 = Student(name="Student1")
>>> student2 = Student(name="Student2")
>>> subj1 = Subjects(name='subject1')
>>> subj2 = Subjects(name='subject2')
>>> subj3 = Subjects(name='subject3')
>>> subj4 = Subjects(name='subject4')
>>> db.session.add_all([student1, student2, subj1, subj2,subj3,subj4])
>>> db.session.commit()
>>> stu1 = Student.query.filter_by(id=1)
>>> stu2 = Student.query.filter_by(id=2).first()
>>> sub1 = Student.query.filter_by(name='subject1').first()
>>> sub2 = Subjects.query.filter_by(name='subject2').first()
>>> sub3 = Subjects.query.filter_by(name='subject3').first()
>>> sub4 = Subjects.query.filter_by(name='subject4').first()
>>> stu1.subj.extend([sub1,sub2,sub4])
>>> stu2.subj.extend([sub2,sub4])
>>> stu1.subj
[<Subjects 1>, <Subjects 2>, <Subjects 4>]
>>> subj2.student.all()
[<Student 2>, <Student 1>]
>>> >>> stu1.subj[0].name
'subject1'
>>> for subj in stu1.subj:
... print(subj.name)
...
subject1
subject2
subject4
>>> for stu in subj2.student:
... print(stu.name)
...
Student2
Student1
>>>
This is just a very basic example, ofcourse you need to extend it in your case. You would have to create multiple association tables for each db.realtionship attribute and so on.
ok...this was pretty dumb. I stopped getting the error once I put db in front of relationship so db.relationship. I haven't built out the transaction form yet to test the relationship back and forth but the site at least comes up and runs. Thx.

Many to Many relationship calling on Flask SQL Alchemy

I have the following badge (achievement) system database structure:
class Base(db.Model):
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
class User(UserMixin, Base):
__tablename__ = 'users'
username = db.Column(db.String(20), nullable=False, unique=True)
email = db.Column(db.String(50), nullable=False, unique=True)
password = db.Column(db.String(200), nullable=False)
name = db.Column(db.String(30), nullable=False)
badges = db.relationship('UserBadge', backref='ubadge',
lazy='dynamic')
class Badge(Base):
__tablename__ = 'badges'
name = db.Column(db.String(35), unique=True)
description = db.Column(db.String(300))
imgfile = db.Column(db.String(80))
badges = db.relationship('UserBadge', backref='badge',
lazy='dynamic')
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.name, self.description)
class UserBadge(Base):
__tablename__ = 'userbadges'
user_id = db.Column(db.Integer(), db.ForeignKey('users.id'))
badge_id = db.Column(db.Integer(), db.ForeignKey('badges.id'))
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.user_id, self.badge_id)
So i can return all the badges by a specific user, using:
ubadges = UserBadge.query.filter_by(user_id=user.id).all()
It returns:
[<Achievement: 1 - 1>]
But instead of 1 (user_id) and 1 (badge_id) i want to show the users.name and badges.name. How can i access those attributes?
In your UserBadge class, just use:
def __repr__(self):
return '<Achievement: {} - {}>'.format(self.ubadge.name, self.badge.name)
It has both properties because you set them up using backref in the other classes.
PS: You might need to change the User backref to user, and then use self.user.name int he function above

SqlAlchemy sort by first record in relationship column

Need a bit of help concerning relationships and filtering. Given the following models, how can I sort the Project's Milestones by the Milestone.active_date?
After trying this, I understand I can't do this with a property. I've tried and the error i get that its not a mappable table makes sense. So what's the best/correct way to perform this?
I've looked into hybrid_property and joins but I can't seem to get this working. I'm quite rusty on databases and haven't done much with ORM's before so a detailed explanation would be very much appreciated.
Thanks!
BW
class MilestoneType(db.Model):
__tablename__ = 'MilestoneType'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(25), nullable=False)
icon_name = db.Column(db.String(255))
def __repr__(self):
return self.name
class Milestone(db.Model):
__tablename__ = 'Milestone'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
milestone_type_id = db.Column(db.ForeignKey(MilestoneType.id))
milestone_type = db.relationship("MilestoneType")
milestone_dates = db.relationship("MilestoneDate", order_by="MilestoneDate.datestamp", lazy="dynamic")
notes = db.Column(db.String(255), nullable=False)
project_id = db.Column(db.Integer, db.ForeignKey('Project.id'), nullable=False)
project = db.relationship("Project")
#property
def active_date(self):
return self.milestone_dates.first()
def __repr__(self):
return str(self.name) + ' (Type: ' + str(self.milestone_type) + ')'
class MilestoneDate(db.Model):
__tablename__ = 'MilestoneDate'
id = db.Column(db.Integer, primary_key=True)
milestone_id = db.Column(db.Integer, db.ForeignKey('Milestone.id'), nullable=False)
milestone = db.relationship("Milestone")
datestamp = db.Column(db.DateTime, nullable=False)
notes = db.Column(db.String(255), nullable=False)
def __repr__(self):
return str(self.datestamp)
class Project(db.Model):
__tablename__ = 'Project'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
active = db.Column(db.Boolean, nullable=False, default=True)
name = db.Column(db.String(50), nullable=False)
code = db.Column(db.String(8), nullable=False)
responsible = db.Column(db.String(50), nullable=False)
customer_id = db.Column(db.ForeignKey(Customer.id))
customer = db.relationship("Customer")
project_type_id = db.Column(db.ForeignKey(ProjectType.id))
project_type = db.relationship("ProjectType")
product_id = db.Column(db.ForeignKey(Product.id))
product = db.relationship("Product")
site_id = db.Column(db.ForeignKey(Site.id))
site = db.relationship("Site")
milestones = db.relationship("Milestone", lazy="dynamic")
def __repr__(self):
return str(self.customer) + " - " + str(self.name) + '(' + str(self.product) + ')'

Categories

Resources