第十一章 鱼书业务处理(2/2)


目录


三、用户注销

目前,实现用户的登录和注销操作比较麻烦:一个用户登录后,如果要注销他,还要去清除浏览器中的cookie。

现在,在页面上添加新功能:登录、注册。

成品网站演示:

templates|base.py代码中,原来有,不过被注释了。现在放开运行:

在浏览器中访问赠送清单页面:

编写注销功能:

from flask_login import login_user, logout_user      # 导入logout_user函数


@web.route('/logout')
def logout():
    logout_user()                                  # logout_user()的本质:还是清空浏览器的cookie
    return redirect(url_for('web.index'))            # 返回重定型到主页页面

在浏览器中,点击右上角的“注销”按钮,会跳转到主页:

四、编写心愿清单页面

上面编写完了赠送清单页面,现在开始编写心愿清单页面。

4.1 成品页面的功能

在成品的心愿清单页面:

4.2 镜像关系:变量名全换还是只换类名?

MyGiftsMyWifts代码基本一样,所以直接复制。

  • 新建目录:

  • 在模型层app|models|wish.py中:

    复制镜像的Gift的代码, 变量名全倒换,因为涉及很多具体的业务逻辑,必须全换名

from app.models.gift import Gift


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):                            
        '''根据用户的id号,查询出该用户所有的礼物'''
        wishes = Wish.query.filter_by(uid=uid, launched=False).order_by(
            desc(Wish.create_time)).all()
        return wishes

    @classmethod
    def get_gift_counts(cls, isbn_list):
        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()
        count_list = [{'count': w[0], 'isbn': w[1]} for w in count_list]
        return count_list

    @property                         
    def book(self):
        '''把根据isbn取相关图书数据的代码封装'''
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)         
        return yushu_book.first
  • 在视图模型app|view_models|wish.py中:

    复制镜像的MyGifts的代码,只给类换个名儿即可。因为很多变量名字的替换是没有意义的,只是改变了变量名,但代码之间的运行逻辑是没有改变的。就是为了转换数据

from app.view_models.book import BookViewModel


class MyWishes:                                             # 给类换个名儿                                
    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()                             

    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']
        r = {
            'wishes_count': count,
            'book': BookViewModel(gift.book),
            'id': gift.id
        }                                
        return r
  • 在视图函数app|web|wish.py中调用:
...

@web.route('/my/wish')
def my_wish():
    uid = current_user.id
    wishes_of_mine = Wish.get_user_wishes(uid)
    isbn_list = [wish.isbn for wish in wishes_of_mine]
    gift_count_list = Wish.get_gift_counts(isbn_list)

    view_model = MyWishes(wishes_of_mine, gift_count_list)              
    return render_template('my_wish.html', wishes=view_model.gifts)
  • 运行报错及推荐的观看错误顺序:

以上是由循环导入所引起的问题。

Python的循环导入之所以痛疼,是因为不会直接在总结性的错误提示里说。

循环导入是很正常的,并不是设计的问题。

4.2 再谈循环导入的两种解决方案

1.解决方法1:更改导入语句的位置

  • 在模型层app|models|wish.py中:
#from app.models.gift import Gift                        # 将涉及循环导入的语句,放到最底部

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):                            
        '''根据用户的id号,查询出该用户所有的礼物'''
        wishes = Wish.query.filter_by(uid=uid, launched=False).order_by(
            desc(Wish.create_time)).all()
        return wishes

    @classmethod
    def get_gift_counts(cls, isbn_list):
        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()
        count_list = [{'count': w[0], 'isbn': w[1]} for w in count_list]
        return count_list

    @property                         
    def book(self):
        '''把根据isbn取相关图书数据的代码封装'''
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)         
        return yushu_book.first
    
    
from app.models.gift import Gift
  • 在模型层app|models|gift.py中:
# from app.models.wish import Wish                        # 将该条导入语句,放到最底部


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 get_user_gifts(cls, uid):
        '''根据用户的id号,查询出该用户所有的礼物'''
        gifts = Gift.query.filter_by(uid=uid, launched=False).order_by(
            desc(Gift.create_time)).all()
        return gifts

    @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      

    @property                          
    def book(self):
        '''把根据isbn取相关图书数据的代码封装'''
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)         
        return yushu_book.first

    @classmethod
    def recent(cls):
        '''最近上传页面的3个具体业务逻辑'''
        ''''''
        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 recent_gift
    
    
from app.models.wish import Wish
  • 在浏览器中成功访问到心愿清单页面:

注意:要想有上面的效果,必须有两个以上用户方可。

2.解决方法2:局部导入

哪个函数要使用,就在哪个函数内部导入下

  • 在模型层app|models|wish.py中:
#from app.models.gift import Gift                                    # 局部导入

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):                            
        '''根据用户的id号,查询出该用户所有的礼物'''
        wishes = Wish.query.filter_by(uid=uid, launched=False).order_by(
            desc(Wish.create_time)).all()
        return wishes

    @classmethod
    def get_gift_counts(cls, isbn_list):
        from app.models.gift import 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()
        count_list = [{'count': w[0], 'isbn': w[1]} for w in count_list]
        return count_list

    @property                         
    def book(self):
        '''把根据isbn取相关图书数据的代码封装'''
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)         
        return yushu_book.first
  • 在模型层app|models|gift.py中:
# from app.models.wish import Wish                                # 局部导入


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 get_user_gifts(cls, uid):
        '''根据用户的id号,查询出该用户所有的礼物'''
        gifts = Gift.query.filter_by(uid=uid, launched=False).order_by(
            desc(Gift.create_time)).all()
        return gifts

    @classmethod
    def get_wish_counts(cls, isbn_list):
        from app.models.wish import Wish                         # 我来这儿了
        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      

    @property                          
    def book(self):
        '''把根据isbn取相关图书数据的代码封装'''
        yushu_book = YuShuBook()
        yushu_book.search_by_isbn(self.isbn)         
        return yushu_book.first

    @classmethod
    def recent(cls):
        '''最近上传页面的3个具体业务逻辑'''
        ''''''
        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 recent_gift
  • 在浏览器中成功访问到心愿清单页面:

个人倾向于第二种方式,在使用的时候临时导入,在函数的函数内部、顶部导入。

4.3 重复代码的封装技巧

1.关于模型命名:

起名要简洁、合适,代码编写起来才轻松。

2.重复代码的封装与继承

由于在视图模型中,wish.py几乎是复制的gift.py的代码,改了个类名功能是没问题,但是充斥着于类名不相符的名字,看着就别扭。

由于两个模块是几乎一样的,所以没有必要定义两个模块。只定义一个模块即可,即trade.py。另外,如果命名不好,就会称为GiftOrWish

在视图模型app|view_models|trade.py中:

class TradeInfo():          
    ...   
     

class MyTrades:
    # 跟视图模型MyGifts、MyWishes的代码逻辑相同,换个名儿。
    pass

MyTrades其实是MyGiftsMyWishes的基类,即父类。

因为MyGiftsMyWishes代码相同,所以这里可以用一方复制代替。

但是,如果两者出现行为逻辑的差异,那么就需要单独定义一个MyGiftsMyWishes,来继承这个父类,然后在子类里面,实现自己特定的业务逻辑。

后续,如果有业务扩展,两者的行为逻辑很大可能会出现差异,彼时就可使用上面的继承思维。

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 第十一章 鱼书业务处理(2/2)


Follow excellence, and success will chase you.