stark組件之顯示頁面搭建(四)

頁面搭建包括第一如何獲取前端傳過來的數據,第二如何在前端渲染出對應標籤。前端

1、後臺獲取數據並進行處理數據庫

在路由系統中,每個路由都對應着一個處理函數,以下所示:django

  def wrapper(self, func):  # 將視圖函數加上裝飾器,這樣能夠在處理視圖以前以後均可以加上必定的功能
        @functools.wraps(func)  # 保留原函數的信息
        def inner(request, *args, **kwargs):
            self.request = request  # 將request傳給當前對象,在處理視圖函數以前
            BaseRequestForm(request)
            BaseRequestModelForm(request)
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        urlpatterns = [
            re_path('list/$', self.wrapper(self.changelist_view), name=self.get_list_url_name ),
            re_path('add/$', self.wrapper(self.add_view), name=self.get_add_url_name),
            re_path('(?P<pk>\d+)/change/$', self.wrapper(self.change_view), name=self.get_edit_url_name),
            re_path('(?P<pk>\d+)/del/$', self.wrapper(self.del_view), name=self.get_del_url_name),

        ]
        extra_urls = self.extra_urls()

其中changelist_view()就是處理顯示數據的函數,能夠看到被裝飾器包裹,其主要目的是爲了在執行函數前,加入一些邏輯,列如將request賦值給stark_class,以及賦值給modelform和form,這在以後也是頗有用處的。app

在列表頁要顯示的功能目前有搜索,過濾,批量操做,分頁等功能函數

    order_by = ['-id']
    list_display = []  # 頁面須要展現的字段
    model_form_class = None
    action_list = []
    search_list = []
    list_filter = []
    list_editable= []
    filter_horizontal=[]
    has_add_btn=True

在這裏須要構建一個分過頁的queryset返回給前端進行渲染,在返回的queryset中是已經將search_list,list_filter,order_by過濾後的數據。url

search_list條件的構建spa

...    
search_list=[]

def get_search_list(self):

        return self.search_list

    
  #######處理搜索功能############
def get_search_condition(self):
        search_list = self.get_search_list()
        q = self.request.GET.get('q', '') #前端name爲q
        con = Q()
        con.connector = 'OR'
        for field in search_list:
            con.children.append(('%s__contains' % field, q))
        return search_list, con, q
...

list_filter條件構建code

...    
    list_filter = []

    def get_list_filter(self):

        return self.list_filter

    def get_list_filter_condition(self):
        comb_condition = {}
        for option in self.get_list_filter():
            filter_list = self.request.GET.getlist(option.field)
            if filter_list:
                comb_condition["%s__in" % option.field] = filter_list
            """
            {
            title:['a','b'],
            }
            """
        return comb_condition

...

在這裏循環的是option對象,而不是像list_search同樣循環取出字段名,由於,若是單純的在list_filter中放入字段名,那麼向choice字段這樣的數據取出來的就不是須要的數據,就如同下面這樣的。orm

爲了解決choice顯示的問題,因此不能單純的添加字段,因此能夠加入一些其它參數來解決,在這裏能夠添加對象,將數據封存到對象中,固然字典也是能夠的。對象

    list_filter = [
        Option('name',),
        Option('status',text_func=lambda x:x[1],is_choice=True)
    ]

class Option(object):
    """
    將傳入的值進行封裝,也就是如今list_filter列表中不是一個個字段而是一個個option對象
    """

    def __init__(self, field, condition=None, is_choice=False, text_func=None, value_func=None, is_multi=False):
        self.field = field  # 傳遞的字段
        if not condition:
            condition = {}
        self.condition = condition  # 傳遞的顯示過濾條件
        self.is_choice = is_choice
        self.text_func = text_func  # 中文
        self.value_func = value_func
        self.is_multi = is_multi  # 是否支持多選搜索

這樣在後臺就能夠獲取到過濾條件的值comb_condition。

order_by條件的構建

    def get_order_by(self):

        return self.order_by

這樣返回給前端的queryse基本就構建完畢了

 queryset = origin_queryset.filter(con).filter(**self.get_list_filter_condition()).order_by(
            *self.get_order_by()).distinct()[page.start:page.end]  # distinct是爲了防止多對多字段中,is_multi=True出項重複數據

其中origin_queryset是預留的鉤子函數,能夠重寫這個方法進行進一步的篩選。

...

    
######獲取queryset對象,根據需求從新此方法進行篩選
    def get_queryset(self,request,*args,**kwargs):
        return self.model_class.objects

    origin_queryset=self.get_queryset(request,*args,**kwargs)

...

2、前端過濾功能url構建

上述過程主要是處理前端傳過來的數據,可是這些傳遞過來的url都是怎麼構建的呢?尤爲是過濾功能,並且搜索過濾等功能都須要在原先url的基礎上進行進一步的篩選,其本質就是url參數的變化,在這裏首先介紹一個知識點就是django的QueryDict的應用。請求中知道request.GET以及request.POST獲得的數據都是QueryDict類型,在urllib中字典形式能夠轉化爲GET形式參數,django中也是可行的。

from urllib.parse import urlencode

url_dict={'name':'fg','age':34}

print(urlencode(url_dict))  #age=34&name=fg

經過一次GET請求,傳入a=1&b=2參數

def query_dict(request):
    query_dict=request.GET
    print(query_dict)#<QueryDict: {'a': ['1'], 'b': ['2']}>
    query_dict.copy()
    query_dict._mutable = True
    query_dict['name']='fg'
    print(query_dict)#<QueryDict: {'a': ['1'], 'name': ['fg'], 'b': ['2']}>
    print(query_dict.urlencode())#a=1&b=2&name=fg
    return HttpResponse('...')

經過這種方式不只能夠構建url而且能夠保留原先的搜索條件

這裏主要說一下list_filter在頁面上url的構建,首先將過濾 的數據從數據庫中取出來,用於做爲過濾條件。

    def list_filter_rows(self):
        #####################組合搜索 能夠經過yield返回一個個的RowQueryset對象#############
        list_filter = self.stark_class.get_list_filter()  # 獲取對象列表['Option()','Option()']
        for option in list_filter:
            _field_obj = self.stark_class.model_class._meta.get_field(option.field)  # 取到每個字段對象
            row_queryset = option.get_queryset(_field_obj, self.stark_class.model_class,
                                               self.stark_class)  # 取到數據庫中對應的一行數據
            yield row_queryset

循環list_filter獲得每個option對象,而且調用option類中get_queryset()方法,注意返回的是每個字段對象對應的一行數據。

    def get_queryset(self, _field_obj, model_class, view_class):

        if isinstance(_field_obj, ForeignKey) or isinstance(_field_obj,ManyToManyField):  # 若是對應字段對象是Foreignkey類型,就取出另外一張表中的數據
            # row_queryset=_field_obj.remote_field.model.objects.filter(**self.condition) #rel若是不行就是用remote_field
            row_queryset = RowQueryset(_field_obj.remote_field.model.objects.filter(**self.condition), self, view_class)  # 傳入self是由於須要用到option中的一些參數text_func
        else:
            if self.is_choice:
                # row_queryset=_field_obj.choices
                row_queryset = RowQueryset(_field_obj.choices, self, view_class)
            else:
                # row_queryset=model_class.objects.filter(**self.condition)
                row_queryset = RowQueryset(model_class.objects.filter(**self.condition), self, view_class)
        return row_queryset

在這裏對每個字段對象進行判斷,ForeignKey、ManyToMany,choice仍是普通的字段。而且將每個字段對象的處理交給RowQueryset類進行處理。這裏知道RoWQueryset類中接收的要不就是queryset的集合要不就是choice的元組對象,其次是option對象以及stark_class.。

class RowQueryset(object):

    def __init__(self, row_queryset, option, view_class):
        """

        :param row_queryset: 封裝的row_queryset的數據
        :param option: 每個option對象
        """
        self.row_queryset = row_queryset
        self.option = option
        self.view_class = view_class  # 這是changeView類,裏面封裝了請求的request對象

    def __iter__(self):  # 爲了循環這個對象,實現iter方法,iter返回的是什麼,頁面顯示的就是什麼

        yield """<div class="whole">"""
        ##################生成所有選項的url############
        total_query_dict = self.view_class.request.GET.copy()  # 深拷貝一份
        total_query_dict._mutable = True
        origin_url_list = self.view_class.request.GET.getlist(self.option.field)  # 獲取全部GET的搜索參數[],['2'],['3']
        if origin_url_list:  # 請求中有值,就將本身字段去掉
            total_query_dict.pop(self.option.field)
            yield """<a href="?%s">所有</a>""" % total_query_dict.urlencode()
        else:
            yield """<a class="active" href="?%s">所有</a>""" % total_query_dict.urlencode()
        yield """</div>"""
        yield """<div class="others">"""

        for obj in self.row_queryset:  # 循環獲得的要不就是queryset中的對象,要不就是元組對象
            query_dict = self.view_class.request.GET.copy()  # 深拷貝一份
            query_dict._mutable = True  # 修改參數
            if not self.option.is_multi:  # 支持單選
                query_dict[self.option.field] = self.option.get_value(obj)  # 獲取數據庫中對應的值
                val = self.option.get_value(obj)  # 從數據庫中取出值與以前請求做比較,看是否加入選中的樣式
                if str(val) in origin_url_list:
                    query_dict.pop(self.option.field)  # 點擊第二次後會移除自身
                    yield '<a class="active" href="?%s">%s</a>' % (
                    query_dict.urlencode(), self.option.get_text(obj))  # 引入text_func是爲了解決choice,元組的問題
                else:
                    yield '<a href="?%s">%s</a>' % (
                    query_dict.urlencode(), self.option.get_text(obj))  # 引入text_func是爲了解決choice,元組的問題
            else:
                multi_value_list = self.view_class.request.GET.getlist(self.option.field)  # 獲取請求的全部搜索參數
                val = self.option.get_value(obj)
                if str(val) in origin_url_list:
                    multi_value_list.remove(str(val))  # 若是請求中已經存在就將其移除
                    query_dict.setlist(self.option.field, multi_value_list)  # 將已經變化的參數從新設置到querydict中
                    yield '<a class="active" href="?%s">%s</a>' % (
                    query_dict.urlencode(), self.option.get_text(obj))  # 引入text_func是爲了解決choice,元組的問題

                else:
                    multi_value_list.append(val)
                    query_dict.setlist(self.option.field, multi_value_list)
                    yield '<a href="?%s">%s</a>' % (
                    query_dict.urlencode(), self.option.get_text(obj))  # 引入text_func是爲了解決choice,元組的問題
        yield """</div>"""

 在前端頁面上只須要循環這個對象就執行內部的__iter__()方法,拿到其執行的結果,從上面的代碼能夠看出來,基本就是從請求中取出參數值與數據庫中取出的值作比較,若是存在就讓前端的顯示加上active選中的樣式,而且將querydict賦值,在這裏支持單選和多選的多選的道理與單選相似,只不過設置querydict的值略有不一樣而已。

        {% for row in cl.list_filter_rows %}<!--前端渲染list_filter-->
            <div class="row">
                {% for obj in row %}
                    {{ obj|safe }}
                    {% endfor %}
            </div>
        {% endfor %}
相關文章
相關標籤/搜索