目录
三、用户注销
目前,实现用户的登录和注销操作比较麻烦:一个用户登录后,如果要注销他,还要去清除浏览器中的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 镜像关系:变量名全换还是只换类名?
MyGifts
与MyWifts
代码基本一样,所以直接复制。
- 新建目录:
在模型层
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
其实是MyGifts
和MyWishes
的基类,即父类。
因为MyGifts
和MyWishes
代码相同,所以这里可以用一方复制代替。
但是,如果两者出现行为逻辑的差异,那么就需要单独定义一个MyGifts
和MyWishes
,来继承这个父类,然后在子类里面,实现自己特定的业务逻辑。
后续,如果有业务扩展,两者的行为逻辑很大可能会出现差异,彼时就可使用上面的继承思维。
Comments | NOTHING