YOLO813

Django优化-数据取至elasticsearch

    优化真是个无底洞!终于把网站的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/