10.业务处理

一. 首页:最近的礼物(复杂SQL的编写方案)

逻辑导图:


我们将这些逻辑在app/models/gift.py中实现

from app.models.base import Base
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey, SmallInteger, desc
from sqlalchemy.orm import relationship
from flask import current_app


class Gift(Base):
    id = Column(Integer, primary_key=True)
    launched = Column(Boolean, default=False)  
    user = relationship('User')
    uid = Column(Integer, ForeignKey('user.id'))
    isbn = Column(String(15), nullable=False)

    def recent(self):   # 逻辑实现, 只筛选没有赠送出去的
        recent_gift = Gift.query.filter_by(launched=False).group_by(Gift.isbn).order_by(desc(Gift.create_time)).limit(
            current_app.config['RECENT_BOOK_COUNT']).distinct().all()
        return rencent_gift


只显示30个: limit(current_app.config['RECENT_BOOK_COUNT'])
按时间排序: order_by(desc(Gift.create_time))
去重:      group_by(Gift.isbn)   +  distinct()

Gift如果实例化了, 还存在recent方法的话,非常不合理, 所以需要变为类方法:

class Gift(Base):
    id = Column(Integer, primary_key=True)
    launched = Column(Boolean, default=False)  
    user = relationship('User')
    uid = Column(Integer, ForeignKey('user.id'))
    isbn = Column(String(15), nullable=False)

    @classmethod
    def recent(cls):   # 逻辑实现, 只筛选没有赠送出去的
        recent_gift = Gift.query.filter_by(launched=False).group_by(Gift.isbn).order_by(Gift.create_time).limit(
        current_app.config['RECENT_BOOK_COUNT']).distinct().all()
        return rencent_gift

在app/web/main.py中编写对应的视图函数index:

from .blueprint import web
from app.models.gift import Gift
from app.view_models.book import BookViewModel
from flask import render_template

__author__ = 'cannon'


@web.route('/')
def index():
    """
        首页视图函数
    """
    recent_gifts = Gift.recent()
    books = [BookViewModel(gift.book) for gift in recent_gifts]
    return render_template('index.html', recent=books)

gift.book即单本书的dict信息, 我们在Gift模型中完善book方法:

class Gift(Base):
    id = Column(Integer, primary_key=True)
    launched = Column(Boolean, default=False) 
    user = relationship('User')
    uid = Column(Integer, ForeignKey('user.id'))
    isbn = Column(String(15), nullable=False)

    @classmethod
    def recent(cls):
        recent_gift = Gift.query.filter_by(launched=False).group_by(Gift.isbn).order_by(Gift.create_time).limit(
            current_app.config['RECENT_BOOK_COUNT']).distinct().all()

        return recent_gift

    @property
    def book(self):     # 通过isbn获取书籍的信息
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)
        return yushu_book.first   # api的搜索结果中获取第一本就行


二. 我的礼物(赠送清单)页面


有两种思路: 


选第二种, 第一种循环遍历数据库是 无法接受的。

1. 循环遍历我的礼物,并取出每个isbn编号组成一个列表

在app/models/gift.py的class Gift(Base)中增加get_user_gifts类方法:

@classmethod
    def get_user_gifts(cls, uid):
        gifts = Gift.query.filter_by(uid=uid, launched=False).order_by(desc(Gift.create_time)).all()
        return gifts

在对应的app/web/gift.py视图函数my_gifts中编写对应逻辑的代码:

@web.route('/my/gifts')
@login_required
def my_gifts():
    uid = current_user.id
    gifts_of_mine = Gift.get_user_gifts(uid)
    isbn_list = [gift.isbn for gift in gifts_of_mine]   # 取出每个isbn编号组成一个列表
    pass

2. db.session.query与func.count统计联合使用

在Gift模型中,增加get_wish_counts方法,统计isbn_list中每个isbn对应的心愿数量:
filter中 in_的用法, 以及Wish_status==1不能漏(重写的是filter_by不是filter)

    @classmethod
    def get_wish_counts(cls, isbn_list):
        # 根据传入的一组isbn,到Gift表中检索出相应的礼物, 并且计算出某个礼物的Wish心愿数量
        db.session.query(Wish).filter(Wish.launched == False, Wish.isbn.in_(isbn_list), Wish.status == 1).group_by(Wish.isbn).all()  #filter接收条件表达式
        pass

这样db.session.query查到的是Wish, 并不是数量, 我们需要使用sqlalchemy的func

from sqlalchemy import func

···
    @classmethod
    def get_wish_counts(cls, isbn_list):
        # 根据传入的一组isbn,到Gift表中检索出相应的礼物, 并且计算出某个礼物的Wish心愿数量
        # query(func.count(Wish.id), Wish.isbn) 这样查询出来count_list列表的每个元素都是是 wish的数量,对应isbn 组成的元组
        count_list = db.session.query(func.count(Wish.id), Wish.isbn).filter(Wish.launched == False,
                                                                             Wish.isbn.in_(isbn_list),
                                                                             Wish.status == 1).group_by(
                                                                             Wish.isbn).all()  # filter接收条件表达式
        pass

3.不要在函数中返回元组, 应该返回字典

query(func.count(Wish.id), Wish.isbn)联合查询,返回的是以元组为元素的列表。这样不方便以后使用, 我们改为使用字典。

 @classmethod
    def get_wish_counts(cls, isbn_list):
        count_list = db.session.query(func.count(Wish.id), Wish.isbn).filter(Wish.launched == False,
                                                                             Wish.isbn.in_(isbn_list),
                                                                             Wish.status == 1).group_by(
                                                                             Wish.isbn).all() 
        count_list = [{'count': w[0], 'isbn': w[1]} for w in count_list]  # 改成字典
        return count_list

4. 编写对应的view_model

先完善app/web/gift.py的my_gifts视图函数:

@web.route('/my/gifts')
@login_required
def my_gifts():
    uid = current_user.id
    gifts_of_mine = Gift.get_user_gifts(uid)
    isbn_list = [gift.isbn for gift in gifts_of_mine]
    wish_count_list = Gift.get_wish_counts(isbn_list)
    pass

我们每本书要显示 书籍信息外, 还有心愿数目, 根据这些, 编写对应view_model/gift.py:

from .book import BookViewModel


class MyGifts:
    def __init__(self, gifts_of_mine, wish_count_list):
        self.gifts = []

        self.__gifts_of_mine = gifts_of_mine
        self.__wish_count_list = wish_count_list

        self.gifts = self.__parse()
        # 通过结果返回回来, 而不是直接在方法中修改实例变量
        # 这样一旦这个类变得很复杂, 就能在__init__中知道实例变量都在哪里被修改了。

    def __parse(self):
        temp_gifts = []
        for gift in self.__gifts_of_mine:
            my_gift = self.__matching(gift)
            temp_gifts.append(my_gift)
        return temp_gifts

    def __matching(self, gift):
        count = 0
        for wish_count in self.__wish_count_list:
            if gift.isbn == wish_count['isbn']:
                count = wish_count['count']
        my_gift = {
            'wishes_count': count,
            'book': BookViewModel(gift.book),   # gift.book没有转化为BookViewModel,需转化
            'id': gift.id
        }
        return my_gift

对应编写my_gifts视图函数:

@web.route('/my/gifts')
@login_required
def my_gifts():
    uid = current_user.id
    gifts_of_mine = Gift.get_user_gifts(uid)
    isbn_list = [gift.isbn for gift in gifts_of_mine]
    wish_count_list = Gift.get_wish_counts(isbn_list)
    view_model = MyGifts(gifts_of_mine, wish_count_list)
    return render_template('my_gifts.html', gifts=view_model.gifts)

三. 用户注销

使用flask_login的logout_user实现: 
app/web/auth.py的logout视图函数:

from flask_login import login_user,  logout_user
···

@web.route('/logout')
def logout():
    logout_user()
    return redirect(url_for('web.index'))

四. 我的心愿页面

与赠送清单的代码正好相反。 
照着app/models/gift.py修改wish.py:

from app.models.base import Base, db
from .gift import Gift
from sqlalchemy import Column, Integer, String, Boolean, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy import desc, func
from app.spider.yushu_book import YuShuBook
from flask import current_app


class Wish(Base):
    id = Column(Integer, primary_key=True)
    launched = Column(Boolean, default=False)  # 是否送出去了
    user = relationship('User')
    uid = Column(Integer, ForeignKey('user.id'))
    isbn = Column(String(15), nullable=False)

    @classmethod
    def get_user_Wishes(cls, uid):
        gifts = Wish.query.filter_by(uid=uid, launched=False).order_by(desc(Wish.create_time)).all()
        return gifts

    @classmethod
    def get_gift_counts(cls, isbn_list):
        # 根据传入的一组isbn,到Wish表中检索出相应的心愿, 并且计算出某个心愿的Gift心愿数量
        count_list = db.session.query(func.count(Gift.id), Gift.isbn).filter(Gift.launched == False,
                                                                             Gift.isbn.in_(isbn_list),
                                                                             Gift.status == 1).group_by(
            Gift.isbn).all()  # filter接收条件表达式
        count_list = [{'count': w[0], 'isbn': w[1]} for w in count_list]
        return count_list

    @classmethod
    def recent(cls):
        recent_gift = Wish.query.filter_by(launched=False).group_by(Wish.isbn).order_by(desc(Wish.create_time)).limit(
            current_app.config['RECENT_BOOK_COUNT']).distinct().all()

        return recent_gift

    @property
    def book(self):
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)
        return yushu_book.first

修改对应的视图函数 app/web/wish.py的my_wish:(参照gift.py的my_gift视图函数)

@web.route('/my/wish')
def my_wish():
    uid = current_user.id
    wished_of_mine = Wish.get_user_gifts(uid)
    isbn_list = [wish.isbn for wish in wished_of_mine]
    Gift_count_list = Wish.get_gift_counts(isbn_list)
    view_model = MyWishes(wished_of_mine, Gift_count_list)
    return render_template('my_wish.html', wishes=view_model.wishes)

复制view_models/gift.py 编写wish.py, 将MyGifts改名为MyWishes即可。

运行后报错:

    File "app/models/wish.py", line 9, in <module>
    from app.models.gift import Gift
ImportError:cannont import name 'Gift'

错误原因是  gift.py导入wish.pywish.py 导入 gift.py,造成循环导入。

再谈循环导入的解决方案

解决办法: 出现循环引用了, 一般修改成 在需要的地方导入而不是在开头导入。

gift.py中把:from .wish import Wish放入get_wish_counts方法中
wish.py中把:from .gift import Gift放入get_gift_counts方法中

运行通过。

猜你喜欢

转载自blog.csdn.net/weixin_41207499/article/details/80776853
10.