优化真是个无底洞!终于把网站的amp和html模板合二为一,这样后续的维护成本会降低很多,一直都有这个想法,今天终于给完成了,记录一下优化思路。
先说下MTV中的views,一开始web界面和amp界面的views并非一套,导致很多代码需要重复写两遍,新建了一个名为NBA的python包(即带有__init__.py的文件夹),将主要代码都放在这个文件当中,例如里面的搜索类视图:
# 20210412之前django列表,导致数据经过mysql数据库,现在完全走es
class NBASearch(View):
my_template= "name.html"
@classmethod
def get(cls,request):
context = {}
if request.GET.get("q"):
context['names'] = es.multiSearchOnES(request.GET.get('q'))
else:
context['names'] = None
return render(request,template_name=cls.my_template,context=context)
这样,在其他两个模板里面,两行代码就解决了,我只需要让类视图继承NBASearch,然后替换定义好的my_template模板名称就可以了,如下:
class SearchView(NBA.NBASearch):
my_template= "amp/search.html"
不得不说python的继承属性实在是太强大了。
MTV中的templates,稍微复杂一点,首先,他们都是继承了base模板,然后html中又包含描述(head区域中)和内容(body区域中),那就抽离出两个descrip-search.html和body-search.html文件,然后在search.html中使用include标签包含进去,但由于amp的一些特性,所以在模板文件中,肯定是还得再做判断,例如:
{% if "amp" in request.path %}
<form action="{% url 'nbaamp:search' %}" method="get" target="_top">
{% else %}
<form action="{% url 'nba:search' %}" method="get">
{% endif %}
{% if "amp" in request.path %}
<amp-img class="flag_img" src="{% static 'imgs/logo.png' %}" alt="logoFlag" width="30px" height="20px"></amp-img>
{% else %}
<img class="flag_img" src="{% static 'imgs/logo.png' %}" alt="logoFlag">
{% endif %}
这里举的是form标签和img标签的例子,这是amp自身特性决定的,另外就是跳转的url链接,这也是需要考虑的,毕竟,让用户在amp页面点击了下一页结果跳转到了web界面,这个体验就不是很好,当然这样对性能肯定是有影响的,毕竟每一个页面都需要额外判断几次,但是维护成本就降低很多了,看各人取舍。
接下来说说关于python调用elasticsearch-dsl模块时的一些注意事项,之前为了赶进度,所以很多内容都没有细看,拿来能用就行,这一次为了改造django框架里面的ListView,又去看了一遍文档,关于ListView,之前在学习django记录过一篇文章视图快速建立翻页,用来建立列表页非常的便捷,好使,但是其默认会从数据库返回一个QuerySet对象,我们可以导入如下代码:
from django.views.generic import ListView
如果是使用vscode,则可以按一下F12查看ListView的定义,继承了其中一个BaseListView的类,再次F12,可以看到它有一个父级MultipleObjectMixin,再次F12,可以看见get_queryset函数:
def get_queryset(self):
"""
Return the list of items for this view.
The return value must be an iterable and may be an instance of
`QuerySet` in which case `QuerySet` specific behavior will be enabled.
"""
if self.queryset is not None:
queryset = self.queryset
if isinstance(queryset, QuerySet):
queryset = queryset.all()
elif self.model is not None:
queryset = self.model._default_manager.all()
else:
raise ImproperlyConfigured(
"%(cls)s is missing a QuerySet. Define "
"%(cls)s.model, %(cls)s.queryset, or override "
"%(cls)s.get_queryset()." % {
'cls': self.__class__.__name__
}
)
ordering = self.get_ordering()
if ordering:
if isinstance(ordering, str):
ordering = (ordering,)
queryset = queryset.order_by(*ordering)
return queryset
如果我们没有在ListView中调用这个函数,则默认返回模型列表,后面这句话很关键,“该函数的返回值必须是可迭代的,或者可以是QuerySet的实例”,这也是后面可以很轻松的将这个返回值改成elasticsearch_dsl.response. Response的原因。
之前在Django操作elasticsearch的测试这篇文章中,写过一点python操作ES的方法,虽然可以直接使用,但是现在看来,并不规范,之前写的:
def SearchOnES(params):
q = Q("multi_match", query=params, fields=['name', 'chinese'])
my_search= my_client[0:20].query(q)
return my_search
我这边直接返回了my_search,这是一个elasticsearch_dsl.search.Search对象,如果仅仅是想将其迭代取值是可以这么做的,但我在实际测试中发现Search对象无法限定数量,而且和ListView对接会有些问题,但是使用官网推荐的execute函数则没有任何问题,例如:
#es.py
def getCompanyList():
my_search = my_client[0:400]
resp = my_search.execute(ignore_cache=True)
return resp
execute函数用于向Elasticsearch发送请求,然后返回一个elasticsearch_dsl.response.Response对象,再在视图层ListView的get_queryset函数中完全重写,直接调用getCompanyList函数,如下:
# 20210413更改get_queryset函数,让其通过ES来筛选数据
class CommonNbaListView(ListView):
...
def get_context_data(self, **kwargs):
...
def get_queryset(self):
return es.getNbaList()
至此,再次使用connection.queries检查网站所有页面,几乎不存在sql语句,网站的速度提升显而易见。
参阅:
#elasticsearch-dsl
https://elasticsearch-dsl.readthedocs.io/en/latest/