Access/Query List of Object SQLAlchemy - sqlalchemy

I have a relationship that yields a list of objects
class Category(db.Model):
_id = db.Column("id", db.Integer, primary_key = True)
book_category = db.Column("book_category", db.String)
booklist = db.relationship('Books', backref = "book_category")
def __init__(self, book_category):
self.book_category = book_category
class Books(db.Model):
_id = db.Column("id", db.Integer, primary_key = True)
bookname = db.Column("bookname", db.String)
filename = db.Column("filename", db.String)
category = db.Column(db.Integer, db.ForeignKey('category.book_category'))
def __init__(self, bookname,filename):
self.bookname = bookname
self.filename = filename
So when I'm querying the category I have an access to category.booklist
Which would yield something like this
[<book1>, <book3>, <book4>]
I can access each using for loop however, I'm feeling that it is not the most efficient way to do.
Is there any way that I can do like
category.booklist.query.filter(books.bookname == variable).all()
Querying and filtering the list of objects yielded by category.booklist

Related

How to add a column with a count result to a sqlalchemy query and pass the pydantic check?

Description
Previously, my query returned the contents of a single Stories table. Now I want to add more information: I need to output the prizes_count for each Story. There is no field prizes_count in the Stories table so I made the following query.
db.query(models.Stories, func.count(models.Stories.prizes).label("prizes_count")).join(models.Prizes)\
.group_by(models.Stories.id).all()
But I have two problems with it.
I get validation errors from Pydantic, because this query returns a list of tuples like (<database.models.Stories object at 0x0000026BB0055E20>, 1). I have to insert the prizes_count value into the Stories object or vice versa, pull all fields into the tuple. I can do it manually, of course, but I think there is a better way.
With this query I lose all stories with 0 prizes because my join ignores them.
Code
endpoint
#app.get("/stories/", response_model=List[schemas.StoryFullInfo])
def get_stories(db: Session = Depends(get_db)):
return crud.get_stories(db)
crud
def get_stories(db: Session):
return db.query(models.Stories, func.count(models.Stories.prizes).label("prizes_count")).join(models.Prizes)\
.group_by(models.Stories.id).all()
models
class Stories(Base):
__tablename__ = "stories"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), index=True)
text = Column(String(length=1000))
author_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
status = Column(TINYINT(unsigned=True), server_default="0")
genre_type = Column(TINYINT(unsigned=True), server_default="0")
likes_count = Column(INTEGER(unsigned=True), server_default="0")
image = Column(Text)
added_to_best_by = Column(INTEGER(unsigned=True))
creation_DT = Column(DateTime, server_default=func.now())
change_status_DT = Column(DateTime)
author = relationship("Users", back_populates="stories")
comments = relationship("Comments", back_populates="story")
prizes = relationship("Prizes", back_populates="story")
class Prizes(Base):
__tablename__ = "prizes"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), nullable=False)
image_id = Column(TINYINT(unsigned=True))
story_id = Column(INTEGER(unsigned=True), ForeignKey("stories.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
user_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
text = Column(String(length=512), nullable=False)
creation_DT = Column(DateTime, server_default=func.now())
story = relationship("Stories", back_populates="prizes")
author = relationship("Users", back_populates="prizes")
schemas
class StoryBaseInfo(BaseModel):
id: int
title: str = None
author_id: int
class Config:
orm_mode = True
class StoryUpdateInfo(StoryBaseInfo):
#title: str = None
text: str = None
status: int
genre_type: int
likes_count: int
image: str = None
added_to_best_by: int = None
change_status_DT: datetime = None
class Config:
orm_mode = True
class StoryFullInfo(StoryUpdateInfo):
creation_DT: datetime
author: UserBaseInfo
prizes_count: int
class Config:
orm_mode = True
class PrizeBaseInfo(BaseModel):
id: int
story_id: int
class Config:
orm_mode = True
class PrizeInfo(PrizeBaseInfo):
title: str
image_id: int
text: str
creation_DT: datetime
author: UserBaseInfo
story: StoryBaseInfo
class Config:
orm_mode = True
Well, it turns out I was thinking in the wrong direction when I asked this. The problem is solved by the features of SQLAlchemy. I can count prizes by using my configured relationship.
My solution is to add the hybrid property to Stories SQLAlchemy model
class Stories(Base):
__tablename__ = "stories"
id = Column(INTEGER(unsigned=True), primary_key=True)
title = Column(String(length=128), index=True)
text = Column(String(length=1000))
author_id = Column(INTEGER(unsigned=True), ForeignKey("users.id", onupdate="CASCADE", ondelete="CASCADE"),
nullable=False)
status = Column(TINYINT(unsigned=True), server_default="0")
genre_type = Column(TINYINT(unsigned=True), server_default="0")
likes_count = Column(INTEGER(unsigned=True), server_default="0")
image = Column(Text)
added_to_best_by = Column(INTEGER(unsigned=True))
creation_DT = Column(DateTime, server_default=func.now())
change_status_DT = Column(DateTime)
author = relationship("Users", back_populates="stories")
comments = relationship("Comments", back_populates="story")
prizes = relationship("Prizes", back_populates="story")
#hybrid_property
def prizes_count(self):
return len(self.prizes)
And then the following query will satisfy the Pydantic scheme.
def get_stories(db: Session):
return db.query(models.Stories).all()

SqlAlchemy Relationship and Marshmallow

I am trying to return JSON or even a complete string of a returned one to many sqlalchemy query. I am using Marshmallow at this point to try do it but it keeps returning incomplete data
I have two models defined as :
class UserModel(db.Model):
__tablename__ = 'usermodel'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
password = db.Column(db.String(120))
weekday = db.relationship('weekDay', cascade='all,delete-orphan', single_parent=True, backref=db.backref('usermodel', lazy='joined'))
class weekDay(db.Model):
__tablename__ = 'weekday'
id = db.Column(db.Integer, primary_key=True)
#Defining the Foreign Key on the Child Table
dayname = db.Column(db.String(15))
usermodel_id = db.Column(db.Integer, db.ForeignKey('usermodel.id'))
I have defined two schemas
class WeekdaySchema(Schema):
id = fields.Int(dump_only=True)
dayname = fields.Str()
class UserSchema(Schema):
id = fields.Int(dump_only=True)
username = fields.Str()
password = fields.Str()
weekday = fields.Nested(WeekdaySchema)
and finally I run the command (I pass the name in userName variable)
userlist = UserModel.query.filter_by(parentuser=userName).all()
full_schema = UserSchema(many=True)
result, errors = full_schema.dump(userlist)
print (result)
I print the result to see before I attempt to Jsonify it:
My weekday object is completely empty
'weekday': {}
Doesn anyone know how I can do this correctly
It's a one-to-many relationship and you must indicate it on UserSchema, like that
class UserSchema(Schema):
id = fields.Int(dump_only=True)
username = fields.Str()
password = fields.Str()
weekday = fields.Nested(WeekdaySchema, many=True)
read more on documentation

Sqlalchemy - Insert into table if not exist in a column (Mysql)

I have a simple Table like this
class Employeecode(Base):
__tablename__ = 'employeecode'
id = Column(Integer, primary_key=True)
unique_code = Column(String(5), unique=True)
employe_name = Column(String(50))
designation = Column(String(50))
How i can create a function which will insert value into 'employeecode' table if 'unique_code' column doesn't have that value.
insert_unique(unique_code,employe_name,designation)
First you can store all previous unicode_value in a list and then you can check and run your query..
def function(**kwargs):
session = Session()
unique_list =[]
employeecodes = db.session.query(Employeecode).all()
for employee in employeecodes:
unique_list.append(employee.unique_code)
if kwargs['unique_code'] not in unique_list:
employee = Employeecode()
employee.unique_code = kwargs['unique_code']
employee.employe_name = kwargs['employe_name']
employee.designation = kwargs['designation']
session.commit()
retval = row2dict(employee)
session.close()
return retval
else:
pass

Sqlalchemy eager loading of parent all properties in joined table inheritance

I have the following problem:
I have a hierachy of classes with joined table inheritance:
class AdGroupModel(Base, AdwordsRequestMixin):
__tablename__ = 'ad_groups'
db_id = Column(BigInteger, primary_key=True)
created_at = Column(DateTime(timezone=False), nullable=False, default=datetime.datetime.now())
# ----RELATIONS-----
# campaign MANY-to-ONE
campaign_db_id = Column(BigInteger,
ForeignKey('campaigns.db_id', ondelete='CASCADE'),
nullable = True,
)
# # ads ONE-to-MANY
ads = relationship("AdModel",
backref="ad_group",
lazy="subquery",
passive_deletes=True,
single_parent=True,
cascade="all, delete, delete-orphan")
# # # keywords ONE-to-MANY
criteria = relationship("AdGroupCriterionModel",
backref="ad_group",
lazy="subquery",
passive_deletes=True,
single_parent=True,
cascade="all, delete, delete-orphan")
# Joined Table Inheritance
type = Column(Unicode(50))
__mapper_args__ = {
'polymorphic_identity': 'ad_group',
'polymorphic_on': type
}
class AdGroupCriterionModel(Base, AdGroupDependenceMixin):
__tablename__ = 'ad_group_criterion'
db_id = Column(BigInteger, primary_key=True)
destination_url = Column(Unicode, nullable=True)
status = Column(Enum("PAUSED", "ACTIVE", "DELETED",
name='criterion_status'), default="ACTIVE")
# ----RELATIONS---
# ad_group ONE-to-MANY
ad_group_db_id = Column(BigInteger, ForeignKey('ad_groups.db_id',
ondelete='CASCADE'), nullable=True)
# Joined Table Inheritance
criterion_sub_type = Column(Unicode(50))
__mapper_args__ = {
'polymorphic_on': criterion_sub_type
}
class AdGroupKeywordModel(AdGroupCriterionModel):
__tablename__ = 'ad_group_keyword'
__mapper_args__ = {'polymorphic_identity': 'Keyword'}
db_id = Column(Integer, ForeignKey('ad_group_criterion.db_id'), primary_key=True)
text = Column(Unicode, nullable=False)
class AdGroupDependenceMixin(object):
_aggad_id = Column(BigInteger, nullable=True)
_agname = Column(Unicode, nullable=True)
#hybrid_property
def ad_group_GAD_id(self):
if self.ad_group is None:
res = self._aggad_id
else:
res = self.ad_group.GAD_id
return res
#ad_group_GAD_id.setter
def ad_group_GAD_id(self, value):
self._aggad_id = value
if value is not None:
self.ad_group = None
#ad_group_GAD_id.expression
def ad_group_GAD_id(cls):
what = case([( cls._aggad_id != None, cls._aggad_id)], else_=AdGroupModel.GAD_id)
return what.label('adgroupgadid_expression')
#hybrid_property
def ad_group_name(self):
if self.ad_group is None:
return self._agname
else:
return self.ad_group.name
#ad_group_name.setter
def ad_group_name(self, value):
self._agname = value
if value is not None:
self.campaign = None
#ad_group_name.expression
def ad_group_name(cls):
what = case([( cls._agname != None, cls._agname)], else_=AdGroupModel.name)
return what.label('adgroupname_expression')
And I load the Keywords objects from the database with the following query:
all_objects1 = self.database.session.query(AdGroupKeywordModel).join(AdGroupModel)\
.options(subqueryload('ad_group'))\
.filter(AdGroupModel.GAD_id!=None)\
.limit(self.options.limit).all()
which returns obejcts of type AdGroupKeywordModel.
Unfortunately every time I try to access the properties of the AdGroupKeywordModel which are in the parent table (AdGroupCriterionModel) a query of this type is emitted:
sqlalchemy.engine.base.Engine
SELECT ad_group_criterion.destination_url AS ad_group_criterion_destination_url, ad_group_criterion.status AS ad_group_criterion_status, ad_group_criterion.ad_group_db_id AS ad_group_criterion_ad_group_db_id, ad_group_criterion.criterion_sub_type AS ad_group_criterion_criterion_sub_type, ad_group_keyword.text AS ad_group_keyword_text
FROM ad_group_criterion JOIN ad_group_keyword ON ad_group_criterion.db_id = ad_group_keyword.db_id
which is strongly compromising the performace.
What I would like to have is that all the attributes for the class AdGroupKeywordModel which are related to the parent (and other classes defined in the relationship) to be loaded with the initial query and be cached for further use. So that when I access them I do not get any overhead from further sqlstatements.
It seems that eager loading is only defined for relationships but not for hierarchies. Is it possible to have this behaviour in sqlalchemy for hierarchies as well?
Thanks
What I see is: only AdGroupModel has a relationship with a lazy= definition (which is the keyword which defines eager loading for relationships), and the query only has a subqueryload('ad_group').
The only point, in which ad_group or AdGroupModel touch with AdGroupKeywordModel is in AdGroupModel.criteria, which has as backref AdGroupCriterionModel.ad_group. I'm not familiar with the subqueryload syntax, but If I would want to eager-load AdGroupCriterionModel.ad_group, I'd define criteria like this:
criteria = relationship(
"AdGroupCriterionModel", backref=backref("ad_group", lazy="subquery"),
lazy="subquery", passive_deletes=True, single_parent=True,
cascade="all, delete, delete-orphan")
The key would be in defining the right lazy also for the backref.

Accessing extra column in many-to-many relationship with simplified association objects

I'm trying to work with the example in the SQLAlchemy docs: Simplifying Association Objects
What I am struggling with understanding is how I can access the special_key. Ultimately I'd like to be able to do something like this:
for user in users
for keyword in user.keywords
keyword.special_key
Here is the code from the example:
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
name = Column(String(64))
# association proxy of "user_keywords" collection
# to "keyword" attribute
keywords = association_proxy('user_keywords', 'keyword')
def __init__(self, name):
self.name = name
class UserKeyword(Base):
__tablename__ = 'user_keyword'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
keyword_id = Column(Integer, ForeignKey('keyword.id'), primary_key=True)
special_key = Column(String(50))
# bidirectional attribute/collection of "user"/"user_keywords"
user = relationship(User,
backref=backref("user_keywords",
cascade="all, delete-orphan")
)
# reference to the "Keyword" object
keyword = relationship("Keyword")
def __init__(self, keyword=None, user=None, special_key=None):
self.user = user
self.keyword = keyword
self.special_key = special_key
class Keyword(Base):
__tablename__ = 'keyword'
id = Column(Integer, primary_key=True)
keyword = Column('keyword', String(64))
def __init__(self, keyword):
self.keyword = keyword
def __repr__(self):
return 'Keyword(%s)' % repr(self.keyword)
Am I on the right track in following this pattern here?
My goal is essentially many-to-many with an extra column containing a boolean value.
This should work:
for user in users:
for keyword in user.user_keywords:
print keyword.special_key