修剪原始数据,获取真正需要的数据
在app
下新建一个view_modles
文件夹,用来存放view modle
,然后新建一个book.py
文件处理从yushu_book.py
中获取的原始数据。因为在网页中,需要获取作者
,搜索关键字
等信息,而从鱼书api中获取的信息有所不同,所以这个view modle
就是用来处理这些原始数据,把他转化成我们需要的数据结构的模块。
├── app
│ ├──forms
│ │ └── BookForm.py
│ ├── web
│ │ ├── __init_.py
│ │ ├── book.py
│ │ ├── setting.py
│ │ └── secure.py
│ ├──libs
│ │ ├── helper.py
│ │ └── HttpRequest.py
│ ├──view_modles
│ │ └── book.py
│ ├──spider
│ │ └── yushu_book.py
│ ├──models
│ │ └── sql_book.py
│ └── __init__.py
├── static
├── templates
├── fisher.py
# view_modle/book.py
class BookViewModle:
@classmethod
def package_single(cls,data,keyword):
returned = {
'books':[],
'total':0,
'keyword':keyword,
}
if data:
returned['total']= 1
returned['books']= [cls.__cut_book_data(data)]
return returned
@classmethod
def package_collection(cls,data,keyword):
returned = {
'books': [],
'total': 0,
'keyword': keyword,
}
if data:
returned['total'] = data['total']
returned['books'] = [cls.__cut_book_data(book) for book in data['books'] ]
# 运用列表推导式简化代码量
return returned
@classmethod
def __cut_book_data(cls,data):
book = {
'title':data['title'],
'publisher':data['publisher'],
'pages':data['pages'] or '', # 这里当pages的值是null的时候返回空,有利于后期模板渲染
'price':data['price'] or '',
'author':'、'.join(data['author']), # 通过join方法快速拼接作者
'summary':data['summary'] or '',
'image':data['image'],
}
return book
然后在web/book.py
接收修剪后的数据。
# web/book.py
·····
from app.view_models.book import BookViewModle
·····
if isbn_or_key == 'isbn':
result = YuShuBook.search_by_isbn(q)
result = BookViewModle.package_single(result,q)
else:
result = YuShuBook.search_by_keyword(q,page)
result = BookViewModle.package_collection(result,q)
·····
然后就可以看到修剪后的数据效果了。
推倒前面的伪面向对象
发现面向对象中绝大多数都是code>@classmethod,如果面向对象中出现大量可以被作为code>@classmethod的方法,就可以说他是一个伪面向对象,不是用面向对象的思想构建的。例如yushu_book.py
下的YuShuBook
类,之所以出现这种问题是因为这个类自身并不会去保存数据,而是把所有数据都返回给了调用方,只是包装了一系列方法,去除了这个类名,他依然可以调用,所以应该把这些类的特征保存在这个类中,而不是返回给调用方。所以重构一下YuShuBook
类
#spider/yushu_book.py
from app.libs.HttpRequest import HTTP
from flask import current_app
class YuShuBook:
isbn_url = 'http://t.yushu.im/v2/book/isbn/{}'
keyword_url = 'http://t.yushu.im/v2/book/search?q={}&start={}&count={}'
def __init__(self):
self.total = 0
self.books = []
def search_by_isbn(self,isbn):
url = self.isbn_url.format(isbn)
# 如果中没有这个属性就会从类属性中去找
result = HTTP.get(url)
self.__file_single(result)
def search_by_keyword(self,keyword,page=1):
url = self.keyword_url.format(keyword,self.get_start_page(page),current_app.config['PRE_PAGE'])
result = HTTP.get(url)
self.__file_conllection(result)
def __file_single(self,data):
if data:
self.total = 1
self.books.append(data)
def __file_conllection(self,data):
if data:
self.total = data['totak']
self.books = data['books']
def get_start_page(page):
return (page-1) * current_app.config['PRE_PAGE']
推倒整个BookViewModle
,重写view_modles
下的book.py
# view_modles/book.py
class BookViewModle:
def __init__(self,book):
self.title = book['title']
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.image= book['image']
class BookCollection:
def __init__(self):
self.total = 0
self.books =[]
self.keyword = ''
def fill(self, yushu_book, keyword):
self.total = yushu_book.total
self.keyword = keyword
self.books = [BookViewModle() for book in yushu_book.books]
重写主视图函数下的serach
视图函数
#web/book.py
@web.route('/book/search')
·····
from app.view_models.book import BookViewModle
from app.view_models.book import BookCollection
·····
def search():
form = SearchForm(request.args)
books = BookCollection()
if form.validate():
q = form.q.data.strip()
page = form.page.data
isbn_or_key = is_isbn_or_key(q)
yushu_book = YuShuBook()
if isbn_or_key == 'isbn':
yushu_book.search_by_isbn(q)
else:
yushu_book.search_by_keyword(q,page)
books.fill(yushu_book,q)
return jsonify(books)
#注意这个时候在查询是得不到数据的,因为这时候是一个对象实例,python不能把一个实例 序列化,因为之后的模板渲染不需要json所以这里不需要继续操作
else:
return jsonify(form.errors)
#这里不采用自定义的错误了,直接返回表单验证的错误信息
理解一下这个顺序,首先把BookCollection
实例化,然后如果表单验证通过,实例化YuShuBook
来接收数据,然后分别判断isbn
和keyword
,进行调用类方法来获取数据,并且保存在yushu_book
这个对象中,最后调用books
这个对象的fill
方法,来修剪得到的数据,返回我们需要的数据并且存在books
这个对象中。整个过程还是挺抽象的。
如果你想返回一个json对象,那么你可以这样做
·····
import json
·····
return json.dumps(books,default=lambda o:o.__dict__)
#将return jsonify(books)替换