From 1bb14f6b4d1de8ffdc9c1eac9bc96c663d599b36 Mon Sep 17 00:00:00 2001 From: Simo-C3 Date: Sat, 6 May 2023 19:14:44 +0900 Subject: [PATCH 1/2] =?UTF-8?q?[add]=20=E4=BD=9C=E6=88=90=E6=97=A5?= =?UTF-8?q?=E6=99=82=E3=80=81=E6=9B=B4=E6=96=B0=E6=97=A5=E6=99=82=E3=80=81?= =?UTF-8?q?=E3=81=84=E3=81=84=E3=81=AD=E6=95=B0=E3=81=A7=E3=81=AE=E3=82=BD?= =?UTF-8?q?=E3=83=BC=E3=83=88=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cruds/works/__init__.py | 95 ++++++++++++++++++++++++++++----------- routers/works/__init__.py | 6 ++- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/cruds/works/__init__.py b/cruds/works/__init__.py index 932c186..0ea8dcd 100644 --- a/cruds/works/__init__.py +++ b/cruds/works/__init__.py @@ -2,7 +2,9 @@ import markdown from fastapi import HTTPException -from sqlalchemy import desc, func, or_ +from sqlalchemy import desc, asc, func, or_, case +from sqlalchemy.orm import aliased +from sqlalchemy.sql.functions import coalesce, count from sqlalchemy.orm.session import Session from cruds.assets import delete_asset_by_id @@ -15,7 +17,6 @@ # TODO: CASCADEを導入する - def set_work( db: Session, title: str, @@ -92,25 +93,65 @@ def get_works_by_limit( tag_ids: str, user: Optional[User], search_word:str, + order_by:str = 'created_at', + order:str = 'asc' ) -> ResWorks: if tag_names != None and tag_ids != None: raise HTTPException( status_code=422, detail="tag name and ID cannot be specified at the same time." ) + favorite_count_subquery = db.query(models.Favorite.work_id, count(models.Favorite.work_id).label('favorite_count')).group_by(models.Favorite.work_id).subquery('favorite_count_subquery') + + is_favorite_subquery = None + if user is not None: + is_favorite_subquery = db.query(models.Favorite.work_id, models.Favorite.user_id.label('is_favorite_flag')).filter(models.Work.id == models.Favorite.work_id, models.Favorite.user_id == user.id).subquery('is_favorite_subquery') + works_orm = ( - db.query(models.Work) + db.query( + models.Work, + coalesce(favorite_count_subquery.c.favorite_count, 0).label('favorite_count'), + case([(is_favorite_subquery.c.is_favorite_flag == None, False)], else_=True).label('is_favorite') if is_favorite_subquery is not None else False + ) .join(models.User,models.Work.user_id==models.User.id) .join(models.Tagging,models.Work.id==models.Tagging.work_id) .join(models.Tag,models.Tagging.tag_id==models.Tag.id) - .group_by(models.Work.id) - .order_by(desc(models.Work.created_at)) + .outerjoin(favorite_count_subquery, models.Work.id==favorite_count_subquery.c.work_id) .filter(models.Work.visibility != models.Visibility.draft) ) + + if is_favorite_subquery is not None: + works_orm = works_orm.outerjoin(is_favorite_subquery, models.Work.id==is_favorite_subquery.c.work_id) + + # order_byの指定 + if order_by == 'created_at': + column_of_order_by = models.Work.created_at + elif order_by == 'updated_at': + column_of_order_by = models.Work.updated_at + elif order_by == 'favorite_count': + column_of_order_by = favorite_count_subquery.c.favorite_count + elif order_by == 'comment': + column_of_order_by = models.Work.comment_count + else: + column_of_order_by = models.Work.created_at + + # orderの指定 + if order == 'desc': + print('desc') + works_orm = works_orm.order_by(desc(column_of_order_by)) + print(works_orm) + else: + print('asc') + works_orm = works_orm.order_by(asc(column_of_order_by)) + print(works_orm) + + work_orm = works_orm.group_by(models.Work.id) + + # search_by_free_word if search_word: works_orm = works_orm.filter(or_(models.User.name.ilike(f"%{search_word}%"),models.Tag.name.ilike(f"%{search_word}%"),models.Work.title.ilike(f"%{search_word}%"))) - + # search_by_tag_ids if tag_ids: tag_id_list = tag_ids.split(",") works_orm = works_orm.filter(models.Tagging.tag_id.in_(tag_id_list)).filter( @@ -119,6 +160,8 @@ def get_works_by_limit( works_orm = works_orm.group_by(models.Work.id).having( func.count(models.Work.id) == len(tag_id_list) ) + + # search_by_tag_names if tag_names: tag_name_list = tag_names.split(",") works_orm = works_orm.filter(models.Tag.name.in_(tag_name_list)).filter( @@ -128,13 +171,16 @@ def get_works_by_limit( func.count(models.Work.id) == len(tag_name_list) ) + # check user if user is None: works_orm = works_orm.filter(models.Work.visibility == models.Visibility.public) elif visibility is not None: works_orm = works_orm.filter(models.Work.visibility == visibility) + # get works total count works_total_count = works_orm.count() + # pagination if oldest_work_id: oldest_work = ( db.query(models.Work).filter(models.Work.id == oldest_work_id).first() @@ -149,27 +195,26 @@ def get_works_by_limit( if newest_work is None: raise HTTPException(status_code=400, detail="this newest_id is invalid") works_orm = works_orm.filter(models.Work.created_at < newest_work.created_at) + works_orm = works_orm.limit(limit).all() - works = [] - for work_orm in works_orm: - work = Work.from_orm(work_orm) - if user is not None: - work.is_favorite = ( - db.query(models.Favorite) - .filter( - models.Favorite.work_id == work.id, - models.Favorite.user_id == user.id, - ) - .first() - is not None - ) - else: - work.is_favorite = False - work.favorite_count = ( - db.query(models.Favorite).filter(models.Favorite.work_id == work.id).count() - ) - works.append(work) + works = list(map(lambda work_orm: Work( + id=work_orm[0].id, + title=work_orm[0].title, + description=work_orm[0].description, + description_html=work_orm[0].description_html, + user=work_orm[0].user, + assets=work_orm[0].assets, + urls=work_orm[0].urls, + visibility=work_orm[0].visibility, + tags=work_orm[0].tags, + thumbnail=work_orm[0].thumbnail, + created_at=work_orm[0].created_at, + updated_at=work_orm[0].updated_at, + comments=work_orm[0].comments, + favorite_count=work_orm[1], + is_favorite=work_orm[2] + ), works_orm)) resWorks = ResWorks(works=works, works_total_count=works_total_count) return resWorks diff --git a/routers/works/__init__.py b/routers/works/__init__.py index f6f053c..15ef0d5 100644 --- a/routers/works/__init__.py +++ b/routers/works/__init__.py @@ -58,10 +58,12 @@ async def get_works( tag_ids:str = None, db: Session = Depends(get_db), user: User = Depends(GetCurrentUser(auto_error=False)), - search_word:str= None + search_word:str= None, + order_by: str = None, + order: str = None, ): works = get_works_by_limit( - db, limit, visibility, oldest_work_id, newest_work_id,tag_names, tag_ids, user,search_word + db, limit, visibility, oldest_work_id, newest_work_id,tag_names, tag_ids, user,search_word, order_by, order ) return works From 95f790832e0ac9e5db74717649904098e7ed3908 Mon Sep 17 00:00:00 2001 From: Simo-C3 Date: Wed, 10 May 2023 18:12:39 +0900 Subject: [PATCH 2/2] [draft] --- .vscode/settings.json | 15 +++-- cruds/works/__init__.py | 140 +++++++++++++++++++++++++++------------- 2 files changed, 105 insertions(+), 50 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c351126..7ccc5e4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,14 @@ { - "python.formatting.provider": "black", + "python.formatting.provider": "none", "[python]": { - "editor.defaultFormatter": null, + "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true - } -} + }, + "cSpell.words": [ + "fastapi", + "ilike", + "outerjoin", + "sqlalchemy", + "toybox" + ] +} \ No newline at end of file diff --git a/cruds/works/__init__.py b/cruds/works/__init__.py index 0ea8dcd..406e41b 100644 --- a/cruds/works/__init__.py +++ b/cruds/works/__init__.py @@ -17,6 +17,7 @@ # TODO: CASCADEを導入する + def set_work( db: Session, title: str, @@ -92,64 +93,104 @@ def get_works_by_limit( tag_names: str, tag_ids: str, user: Optional[User], - search_word:str, - order_by:str = 'created_at', - order:str = 'asc' + search_word: str, + order_by: str = "created_at", + order: str = "asc", ) -> ResWorks: if tag_names != None and tag_ids != None: raise HTTPException( - status_code=422, detail="tag name and ID cannot be specified at the same time." + status_code=422, + detail="tag name and ID cannot be specified at the same time.", ) - favorite_count_subquery = db.query(models.Favorite.work_id, count(models.Favorite.work_id).label('favorite_count')).group_by(models.Favorite.work_id).subquery('favorite_count_subquery') - + comment_count_subquery = ( + db.query( + models.Comment.work_id, count(models.Comment.work_id).label("comment_count") + ) + .group_by(models.Comment.work_id) + .subquery("comment_count_subquery") + ) + favorite_count_subquery = ( + db.query( + models.Favorite.work_id, + count(models.Favorite.work_id).label("favorite_count"), + ) + .group_by(models.Favorite.work_id) + .subquery("favorite_count_subquery") + ) is_favorite_subquery = None if user is not None: - is_favorite_subquery = db.query(models.Favorite.work_id, models.Favorite.user_id.label('is_favorite_flag')).filter(models.Work.id == models.Favorite.work_id, models.Favorite.user_id == user.id).subquery('is_favorite_subquery') + is_favorite_subquery = ( + db.query( + models.Favorite.work_id, + models.Favorite.user_id.label("is_favorite_flag"), + ) + .filter( + models.Work.id == models.Favorite.work_id, + models.Favorite.user_id == user.id, + ) + .subquery("is_favorite_subquery") + ) works_orm = ( db.query( models.Work, - coalesce(favorite_count_subquery.c.favorite_count, 0).label('favorite_count'), - case([(is_favorite_subquery.c.is_favorite_flag == None, False)], else_=True).label('is_favorite') if is_favorite_subquery is not None else False + coalesce(favorite_count_subquery.c.favorite_count, 0).label( + "favorite_count" + ), + case( + [(is_favorite_subquery.c.is_favorite_flag == None, False)], else_=True + ).label("is_favorite") + if is_favorite_subquery is not None + else False, + coalesce(comment_count_subquery.c.comment_count, 0).label("comment_count"), ) - .join(models.User,models.Work.user_id==models.User.id) - .join(models.Tagging,models.Work.id==models.Tagging.work_id) - .join(models.Tag,models.Tagging.tag_id==models.Tag.id) - .outerjoin(favorite_count_subquery, models.Work.id==favorite_count_subquery.c.work_id) + .join(models.User, models.Work.user_id == models.User.id) + .join(models.Tagging, models.Work.id == models.Tagging.work_id) + .join(models.Tag, models.Tagging.tag_id == models.Tag.id) + # .group_by(models.Work.id) .filter(models.Work.visibility != models.Visibility.draft) ) + works_orm = works_orm.outerjoin( + favorite_count_subquery, models.Work.id == favorite_count_subquery.c.work_id + ) + works_orm = works_orm.outerjoin( + comment_count_subquery, models.Work.id == comment_count_subquery.c.work_id + ) + if is_favorite_subquery is not None: - works_orm = works_orm.outerjoin(is_favorite_subquery, models.Work.id==is_favorite_subquery.c.work_id) + works_orm = works_orm.outerjoin( + is_favorite_subquery, models.Work.id == is_favorite_subquery.c.work_id + ) # order_byの指定 - if order_by == 'created_at': + if order_by == "created_at": column_of_order_by = models.Work.created_at - elif order_by == 'updated_at': + elif order_by == "updated_at": column_of_order_by = models.Work.updated_at - elif order_by == 'favorite_count': + elif order_by == "favorite": column_of_order_by = favorite_count_subquery.c.favorite_count - elif order_by == 'comment': - column_of_order_by = models.Work.comment_count + elif order_by == "comment": + column_of_order_by = comment_count_subquery.c.comment_count else: column_of_order_by = models.Work.created_at - + # orderの指定 - if order == 'desc': - print('desc') + if order == "desc": works_orm = works_orm.order_by(desc(column_of_order_by)) - print(works_orm) else: - print('asc') works_orm = works_orm.order_by(asc(column_of_order_by)) - print(works_orm) - - work_orm = works_orm.group_by(models.Work.id) # search_by_free_word if search_word: - works_orm = works_orm.filter(or_(models.User.name.ilike(f"%{search_word}%"),models.Tag.name.ilike(f"%{search_word}%"),models.Work.title.ilike(f"%{search_word}%"))) + works_orm = works_orm.filter( + or_( + models.User.name.ilike(f"%{search_word}%"), + models.Tag.name.ilike(f"%{search_word}%"), + models.Work.title.ilike(f"%{search_word}%"), + ) + ) # search_by_tag_ids if tag_ids: @@ -195,26 +236,33 @@ def get_works_by_limit( if newest_work is None: raise HTTPException(status_code=400, detail="this newest_id is invalid") works_orm = works_orm.filter(models.Work.created_at < newest_work.created_at) - + + # works_orm = works_orm.group_by(models.Work.id) + print(works_orm) works_orm = works_orm.limit(limit).all() - works = list(map(lambda work_orm: Work( - id=work_orm[0].id, - title=work_orm[0].title, - description=work_orm[0].description, - description_html=work_orm[0].description_html, - user=work_orm[0].user, - assets=work_orm[0].assets, - urls=work_orm[0].urls, - visibility=work_orm[0].visibility, - tags=work_orm[0].tags, - thumbnail=work_orm[0].thumbnail, - created_at=work_orm[0].created_at, - updated_at=work_orm[0].updated_at, - comments=work_orm[0].comments, - favorite_count=work_orm[1], - is_favorite=work_orm[2] - ), works_orm)) + works = list( + map( + lambda work_orm: Work( + id=work_orm[0].id, + title=work_orm[0].title, + description=work_orm[0].description, + description_html=work_orm[0].description_html, + user=work_orm[0].user, + assets=work_orm[0].assets, + urls=work_orm[0].urls, + visibility=work_orm[0].visibility, + tags=work_orm[0].tags, + thumbnail=work_orm[0].thumbnail, + created_at=work_orm[0].created_at, + updated_at=work_orm[0].updated_at, + comments=work_orm[0].comments, + favorite_count=work_orm[1], + is_favorite=work_orm[2], + ), + works_orm, + ) + ) resWorks = ResWorks(works=works, works_total_count=works_total_count) return resWorks