内容简介:flask鱼书项目这里的每次添加鱼豆的个数卸载配置文件内,自行添加上面的判断显然是不严谨的,需要添加判断条件,来确认是否将书籍添加到礼物列表中,在
flask鱼书项目 实战 七
实现保存礼物
#web/gitf.py from flask import current_app from app.models.base import db from app.models.sql_gift import Gift from . import web from flask_login import login_required, current_user ······ @web.route('/gifts/book/<isbn>') @login_required def save_to_gifts(isbn): gift = Gift() gift.isbn = isbn #这里的current_user指代实例化的User对象,通过之前的git_id方法,在flask_login底层实例话的对象 gift.uid = current_user.id current_user.beans += current_app.config['BEANS_UPLOAD_ONE_BOOK'] db.session.add(gift) db.session.commit()
这里的每次添加鱼豆的个数卸载配置文件内,自行添加
#setting.py BEANS_UPLOAD_ONE_BOOK = 0.5
上面的判断显然是不严谨的,需要添加判断条件,来确认是否将书籍添加到礼物列表中,在 sql_user.py
下进行判断
from sqlalchemy import Column, Integer, Float, String, Boolean from werkzeug.security import generate_password_hash, check_password_hash from app.libs.helper import is_isbn_or_key from app.models.base import Base, db from app import login_manager from flask_login import UserMixin from app.models.sql_gift import Gift from app.models.sql_wish import Wish from app.spider.yushu_book import YuShuBook class User(Base, UserMixin): id = Column(Integer, primary_key=True) _password = Column('password', String(128), nullable=False) nickname = Column(String(24), nullable=False) phone_number = Column(String(18), unique=True) email = Column(String(50), unique=True, nullable=False) confirmed = Column(Boolean, default=False) beans = Column(Float, default=0) send_counter = Column(Integer, default=0) receive_counter = Column(Integer, default=0) wx_open_id = Column(String(50)) wx_name = Column(String(32)) @property def password(self): return self._password @password.setter def password(self, raw): self._password = generate_password_hash(raw) def check_password(self, raw): return check_password_hash(self._password, raw) # 新增验证gitf方法,先判断isbn输入是否正确,再判断api中是否存在这个isbn书籍 def can_save_to_list(self, isbn): if is_isbn_or_key(isbn) != 'isbn': return False yushu_book = YuShuBook() yushu_book.search_by_isbn(isbn) if not yushu_book.first: return False # 不允许一个用户同时赠送多本相同的图书 # 一个用户不可能同时成为赠送者和索要者 gifting = Gift.query.filter_by(uid=self.id, isbn=isbn, launched=False).first() #Wisth和sql_gift.py模块一样,把类名改一下即可 wishing = Wish.query.filter_by(uid=self.id, isbn=isbn, launched=False).first() if not gifting and not wishing: return True else: return False @login_manager.user_loader def get_user(uid): return User.query.get(int(uid))
事务与回滚
事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行,要么全部不执行
这里操作了两个模型,即两个数据表,所以涉及到数据库的事务处理,但是 sqlalchemy
模块本身就支持事务,因为只有在执行了 commit
的时候才会一起提交到数据库,否则就不提交,所以这里使用异常处理,如果捕获异常,则释放,否则后面的sql语句也不会被提交,即数据回滚
@web.route('/gifts/book/<isbn>') @login_required def save_to_gifts(isbn): if current_user.can_save_to_list(isbn): try: gift = Gift() gift.isbn = isbn gift.uid = current_user.id current_user.beans += current_app.config['BEANS_UPLOAD_ONE_BOOK'] db.session.add(gift) db.session.commit() except Exception as e: db.session.rollback() raise e else: flash('这本书已经添加至你的赠送清单或者存在你的心愿清单')
上下文管理器
上面把添加数据库的代码做了事务处理,那么不难想到其他的操作数据库应该也需要相同的处理,这样每一个数据库操作的时候都会添加同样的代码,这里就可以使用上下文管理器来简化操作
#modles/base.py #这里把导入的SQLAlchemy重新命名为_SQLAlchemy,想不出子类的名字的时候可以这么使用= from flask_sqlalchemy import SQLAlchemy as _SQLAlchemy #继承_SQLAlchemy,并且创建上下文管理器 class SQLAlchemy(_SQLAlchemy): #python提供了简化创建上下文管理器的装饰器,否则需要定义类方法 @contextmanager def auto_commit(self): try: #这里的yield就是返回出去执行的处理数据库的方法执行完之后执行下面的代码 yield self.session.commit() except Exception as e: self.session.rollback() raise e #这里需要把db放到子类下面,否则找不到SQLAlchemy() db = SQLAlchemy()
然后就可以替换刚刚的代码
#web/gitf.py @web.route('/gifts/book/<isbn>') @login_required def save_to_gifts(isbn): if current_user.can_save_to_list(isbn): with db.auto_commit(): gift = Gift() gift.isbn = isbn gift.uid = current_user.id current_user.beans += current_app.config['BEANS_UPLOAD_ONE_BOOK'] db.session.add(gift) else: flash('这本书已经添加至你的赠送清单或者存在你的心愿清单')
同样的,之前注册的代码
#web/auth.py @web.route('/register/', methods=['GET', 'POST']) def register(): form = RegisterForm(request.form) if request.method == 'POST' and form.validate(): with db.auto_commit(): user = User() user.set_attrs(form.data) db.session.add(user) return redirect(url_for('web.login')) return render_template('auth/register.html', form=form)
变量的陷阱
这里再测试赠送礼物功能之前,还有几个小问题,第一个是创建时间再 base.py
中定义了 create_time
,但是还没有给这个赋值,这里线给他赋值
from datetime import datetime ····· class Base(db.Model): __abstract__ = True create_time = Column('create_time', Integer) status = Column(SmallInteger, default=1) def __init__(self): self.create_time = int(datetime.now().timestamp())
这里有一个问题,就是既然 status
可以设置默认值,为什么不把 create_time
也设置成默认值。
这里这个问题的原因就是, create_time
被设置成了类变量,类变量再 app
启动的时候就已经被赋值成功了,所以如果设置成类变量,所有的 create_time
都会变成同一个时间,然而, self
指向实例, create_time
就是指对象被实例化的时间,所以这里要使用 self
来设置创建时间
启动 app
测试提交书籍,还是失败,原因是 gift.py
里的 save_to_gift
没有返回,再flask中视图函数必须要有返回,否贼就会报错
合理使用Ajax
这里如果使用 return redirect(url_for('web.book_detail', isbn=isbn))
把他重定向回到详情页面,会刷新一次,用户体验不是特别好,像这种提交数据,但不希望刷新页面的场景,就可以使用 Ajax
来异步提交,这里的 Ajax
,,之后补上,先用 redirect
, orz
添加心愿清单
这块视图函数几乎和 save_to_gift
一模一样,直接给出代码
#web/wish.py from flask import current_app, flash, redirect, url_for from flask_login import login_required, current_user from app.models.base import db from app.models.sql_wish import Wish from . import web @web.route('/wish/book/<isbn>') @login_required def save_to_wish(isbn): if current_user.can_save_to_list(isbn): with db.auto_commit(): wish = Wish() wish.isbn = isbn wish.uid = current_user.id db.session.add(wish) else: flash('这本书已经添加至你的赠送清单或者存在你的心愿清单') return redirect(url_for('web.book_detail', isbn=isbn))
处理数据
首先再 BookViewModle.py
中添加两个新的数据,使书籍详情页面显示完整
class BookViewModle: def __init__(self,book): self.title = book['title'] self.publisher= book['publisher'] self.pages= book['pages'] self.price = book['price'] self.author = '、'.join(book['author']) self.summary = book['summary'] self.isbn = book['isbn'] self.image= book['image'] #新数据 self.pubdate = book['pubdate'] self.binding = book['binding']
然后在处理赠送书籍列表,前面说过了,在原始数据和试图数据之前需要有个处理数据的 viewmodle
,所以在 view_modles
下创建新的 trade.py
作为书籍详情页的 viewmodle
#view_modles/trade.py class TradeInfo: def __init__(self, goods): self.total = 0 self.trades = [] self.__parse(goods) def __parse(self, goods): self.total = len(goods) self.trades = [self.__map_to_trade(single) for single in goods] #处理单本书 #判断创建时间是否存在,如果存在就格式化显示时间,不存在就显示未知 def __map_to_trade(self, single): if single.create_datetime: time = single.create_datetime.strftime("%Y-%m-%d") else: time = '未知' return dict( user_name=single.user.nickname, time=time, id=single.id )
然后在视图函数中编写试图
#web/book.py @web.route('/book/<isbn>/detail') def book_detail(isbn): has_in_gifts = False has_in_wishes = False # 取书籍详情数据 yushu_book = YuShuBook() yushu_book.search_by_isbn(isbn) book = BookViewModle(yushu_book.first) if current_user.is_authenticated: if Gift.query.filter_by(uid=current_user.id, isbn=isbn, launched=False).first(): has_in_gifts = True if Wish.query.filter_by(uid=current_user.id, isbn=isbn, launched=False).first(): has_in_wishes = True trade_gifts = Gift.query.filter_by(isbn=isbn, launched=False).all() trade_wishes = Wish.query.filter_by(isbn=isbn, launched=False).all() trade_gifts_modle = TradeInfo(trade_gifts) trade_wishes_modle = TradeInfo(trade_wishes) return render_template('book_detail.html', book=book, wishes=trade_wishes_modle, gifts=trade_gifts_modle, has_in_gifts=has_in_gifts, has_in_wishes=has_in_wishes)
要理解这个函数里的一些参数,需要结合模板来看
{% if not has_in_gifts and not has_in_wishes %} <div class="col-md-1"> <a class="btn btn-outline" href="#modal"> 赠送此书 </a> </div> <div style="margin-left:30px;" class="col-md-1"> <a class="btn btn-outline" href="{{ url_for('web.save_to_wish', isbn=book.isbn) }}"> 加入到心愿清单 </a> </div> {% elif has_in_wishes %} <div class="col-md-3"> <span class="bg-info">已添加至心愿清单</span> </div> {% else %} <div class="col-md-3"> <span class="bg-info">已添加至赠送清单</span> </div> {% endif %}
这里时用于区分三种状态,用 has_in_wishes
和 has_in_gifts
来区分。
重写基类的filter_by
因为再查询的时候我们要确定这条记录有没有被删除,前面说过了,删除使用标志位 status
来表示的,但是每条查询都传入标志位, 显然是个废话(因为查询肯定是要查询没有被删除的)所以采用重写 filter_by
的方法来解决。
首先, filter_by
继承了 flask_sqlalchemy
, flask_sqlalchemy
继承了 sqlalchemy
的 BaseQuery
所以定义的 Query
只需要继承 BaseQuery
即可
class Query(BaseQuery): def filter_by(self, **kwargs): if 'status' not in kwargs.keys(): kwargs['status'] = 1 #调用父类的filter_by方法,把新的kwargs传入进行查询并返回 return super(Query, self).filter_by(**kwargs)
然后在 db
中覆盖这个方法即可
db = SQLAlchemy(query_class=Query)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 「Flask实战」鱼书项目实战一
- 「Flask实战」鱼书项目实战三
- 「Flask实战」鱼书项目实战四
- 「Flask实战」鱼书项目实战六
- 「Flask实战」flask鱼书项目实战二
- go语言实战教程:Redis实战项目应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
程序员的数学思维修炼(趣味解读)
周颖 / 清华大学出版社 / 2014-4-1 / 45.00元
本书是一本专门为程序员而写的数学书,介绍了程序设计中常用的数学知识。本书门槛不高,不需要读者精通很多高深的数学知识,只需要读者具备基本的四则运算、乘方等数学基础知识和日常生活中的基本逻辑判断能力即可。本书拒绝枯燥乏味的讲解,而是代之以轻松活泼的风格。书中列举了大量读者都很熟悉,而且非常有趣的数学实例,并结合程序设计的思维和算法加以剖析,可以训练读者的数学思维能力和程序设计能力,进而拓宽读者的视野,......一起来看看 《程序员的数学思维修炼(趣味解读)》 这本书的介绍吧!
随机密码生成器
多种字符组合密码
HEX HSV 转换工具
HEX HSV 互换工具