stark - filter、pop、總結

1、filter

效果圖

 

知識點

1.配置得顯示Filter,不配置就不顯示了
list_filter = ['title','publish', 'authors']

2.前端顯示
後端返回 字典
eg:{"publish":["<a href=''>所有</a>","<a href=''>南京出版社</a>","<a href=''>上海出版社</a>"]
"authors":["<a href=''>所有</a>","<a href=''>yuan</a>","<a href=''>egon</a>"]
}

{% if showlist.config.list_filter %}
<h4>Filter</h4>
{% for filter_field, linktags in showlist.get_filter_linktags.items %}
<div class="well">
<p>{{ filter_field.upper }}</p>
{% for link in linktags %}
<p>{{ link|safe }}</p>
{% endfor %}

</div>
{% endfor %}
{% endif %}

3.get_filter_linktags 返回 字典
知識:
1.根據字段 str 取到模型得字段對象
filter_field_obj = self.config.model._meta.get_field(filter_field)
model_name = self.config.model._meta.model_name # 模型名 book
app_label = self.config.model._meta.app_label # app名 app01

2.一對多,多對多,
就是 ForeignKey ManyToManyField 得對象
app01.Book.publish
<class 'django.db.models.fields.related.ForeignKey'>
app01.Book.authors
<class 'django.db.models.fields.related.ManyToManyField'>

3.根據一對多,多對多得對象 找關聯得模型表,數據
print("rel...", filter_field_obj.rel.to.objects.all())
rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>]>
rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]>

4.取數據,普通字段,和關聯字段分開取
if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
data_list = filter_field_obj.rel.to.objects.all() # 關聯對象 適用 一對多,多對多
else: # 普通字段
data_list = self.config.model.objects.all().values('pk', filter_field)

5.處理所有標籤時
注意:url 上面已經有了該field, 所有,就取消該field, del params[filter_field]
url 上面沒有,那就沒有
temp = []
if params.get(filter_field):
del params[filter_field]
temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
else:
temp.append("<a href='#' class='active'>所有</a>")

6.處理數據標籤
注意:分開處理關聯字段和普通字段
params = copy.deepcopy(self.request.GET)
爲url加一個params
params[filter_field] = pk
params[filter_field] = text

每一次取數據都要保留 正在訪問得 url 並加上正在訪問得field!

_url = params.urlencode()
link_tag = "<a href='?%s'>%s</a>" % (_url, text)

正在點擊得,得加上樣式 active
if cid == str(pk) or cid == text:
link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
else:
link_tag = "<a href='?%s'>%s</a>" % (_url, text)
        # 處理數據標籤
        for obj in data_list:
            if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
                pk = obj.pk
                text = str(obj)
                params[filter_field] = pk
            else:
                pk = obj.get('pk')
                text = obj.get(filter_field)
                params[filter_field] = text

            _url = params.urlencode() # 序列化後得結構

            if cid == str(pk) or cid == text:
                link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
            else:
                link_tag = "<a href='?%s'>%s</a>" % (_url, text)

            temp.append(link_tag)

        link_dic[filter_field] = temp

 

    def get_filter_linktags(self):
        print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors']

        link_dic = {}
        import copy

        for filter_field in self.config.list_filter:
            params = copy.deepcopy(self.request.GET)

            cid = self.request.GET.get(filter_field, 0)

            filter_field_obj = self.config.model._meta.get_field(filter_field)
            print(filter_field_obj)
            print(type(filter_field_obj))
            """
            app01.Book.title
            <class 'django.db.models.fields.CharField'>
            app01.Book.publish
            <class 'django.db.models.fields.related.ForeignKey'>
            app01.Book.authors
            <class 'django.db.models.fields.related.ManyToManyField'>
            """

            from django.db.models.fields.related import ForeignKey
            from django.db.models.fields.related import ManyToManyField

            # print("rel...", filter_field_obj.rel.to.objects.all())
            # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>]>
            # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]>

            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                data_list = filter_field_obj.rel.to.objects.all()  # 關聯對象 適用 一對多,多對多
            else: # 普通字段
                data_list = self.config.model.objects.all().values('pk', filter_field)

            # 處理所有標籤
            temp = []
            if params.get(filter_field):
                del params[filter_field]
                temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
            else:
                temp.append("<a href='#' class='active'>所有</a>")

            # 處理數據標籤
            for obj in data_list:
                if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
                    pk = obj.pk
                    text = str(obj)
                    params[filter_field] = pk
                else:
                    pk = obj.get('pk')
                    text = obj.get(filter_field)
                    params[filter_field] = text

                _url = params.urlencode() # 序列化後得結構

                if cid == str(pk) or cid == text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)

                temp.append(link_tag)

            link_dic[filter_field] = temp

        return link_dic


4.Q查詢 and
def list_view(self, request):
...
...
# 獲取filter得Q對象
filter_condition = self.get_filter_condition(request)

# 篩選當前表得全部數據
data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)
...
...

# Q對象
def get_filter_condition(self, request):
filter_condition = Q() # 默認是 and 不是 or, 根據str,查找val
for filter_field, val in request.GET.items():
if filter_field in self.list_filter:
filter_condition.children.append((filter_field, val))

return filter_condition

補充:
Q查詢根據字段
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
Q查詢根據str
filter_condition = Q()
filter_consition.connector = "or" # 若是不寫默認是 and
filter_condition.children.append(("title","yuan"))
filter_condition.children.append(("date","2018-12-12"))

Book.object.filter(filter_condition)

app01/stark.py

# -*- coding:utf-8 -*-
from stark.service import stark
from .models import *
from django.forms import ModelForm

class BookModelForm(ModelForm):
    class Meta:
        model = Book
        fields = "__all__"
        labels = {
            "title": '書籍名稱',
            "price": '價格',
            'publishDate': '出版日期'
        }

from django.shortcuts import HttpResponse
class BookConfig(stark.ModelStark):

    list_display = ['title', 'price','publishDate']
    list_display_links = ['title']
    modelform_class = BookModelForm
    search_fields = ['title', 'price']

    def patch_init(self, request, queryset):
        # print("queryset",queryset)
        queryset.update(price=123)

        return HttpResponse('批量初始化OK')

    patch_init.short_description = "批量初始化"
    actions = [patch_init]

    list_filter = ['title','publish', 'authors']


stark.site.register(Book, BookConfig)
stark.site.register(Publish)
stark.site.register(Author)
stark.site.register(AuthorDetail)
stark.py

 

stark/service/stark.py

from django.conf.urls import url
from django.shortcuts import HttpResponse, reverse, redirect, render
from django.utils.safestring import mark_safe
from django.urls import reverse
from django.forms import ModelForm
from stark.utils.page import Pagination
from django.db.models import Q

class ShowList(object):
    def __init__(self, config, data_list, request):
        self.config = config
        self.data_list = data_list
        self.request = request

        # 分頁
        data_count = self.data_list.count()
        current_page = self.request.GET.get('page',1)
        base_path = self.request.path

        self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=3, pager_count=11)
        self.page_data = self.data_list[self.pagination.start:self.pagination.end]

        self.actions = self.config.new_actions()


    def get_filter_linktags(self):
        print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors']

        link_dic = {}
        import copy

        for filter_field in self.config.list_filter:
            params = copy.deepcopy(self.request.GET)

            cid = self.request.GET.get(filter_field, 0)

            filter_field_obj = self.config.model._meta.get_field(filter_field)
            print(filter_field_obj)
            print(type(filter_field_obj))
            """
            app01.Book.title
            <class 'django.db.models.fields.CharField'>
            app01.Book.publish
            <class 'django.db.models.fields.related.ForeignKey'>
            app01.Book.authors
            <class 'django.db.models.fields.related.ManyToManyField'>
            """

            from django.db.models.fields.related import ForeignKey
            from django.db.models.fields.related import ManyToManyField

            # print("rel...", filter_field_obj.rel.to.objects.all())
            # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>, <Publish: 3>]>
            # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]>

            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                data_list = filter_field_obj.rel.to.objects.all()  # 關聯對象 適用 一對多,多對多
            else: # 普通字段
                data_list = self.config.model.objects.all().values('pk', filter_field)

            # 處理所有標籤
            temp = []
            if params.get(filter_field):
                del params[filter_field]
                temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
            else:
                temp.append("<a href='#' class='active'>所有</a>")

            # 處理數據標籤
            for obj in data_list:
                if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
                    pk = obj.pk
                    text = str(obj)
                    params[filter_field] = pk
                else:
                    pk = obj.get('pk')
                    text = obj.get(filter_field)
                    params[filter_field] = text

                _url = params.urlencode() # 序列化後得結構

                if cid == str(pk) or cid == text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)

                temp.append(link_tag)

            link_dic[filter_field] = temp

        return link_dic

    def get_action_list(self):
        temp = []
        for action in self.actions:
            temp.append({
                "name": action.__name__,
                "desc":action.short_description
            })

        return temp
    

    def get_header(self):
        # 構建表頭
        header_list = []
        # header_list = ['選擇','pk',...'操做','操做']
        for field in self.config .new_list_play():
            if callable(field):
                val = field(self.config, header=True)
            else:
                if field == "__str__":
                    val = self.config .model._meta.model_name.upper()
                else:  # 根據str 拿字段對象 取中文
                    val = self.config .model._meta.get_field(field).verbose_name

            header_list.append(val)

        return header_list

    def get_body(self):
        # 構建表單
        new_data_list = []
        for data in self.page_data:
            temp = []
            for field in self.config.new_list_play():  # ['title','price'] 字符串找對象得屬性 反射
                # print('field:', field)
                if callable(field):
                    val = field(self.config, data)
                else:
                    val = getattr(data, field)

                    if field in self.config.list_display_links:
                        _url = self.config.get_change_url(data)
                        val = mark_safe("<a href='%s'>%s</a>" % (_url, val))

                temp.append(val)

            new_data_list.append(temp)  # [['yuan', 12], ['alex', 18], ['egon', 22]]

        return new_data_list


class ModelStark(object):
    list_display = ["__str__"]
    list_display_links = []
    modelform_class = []
    search_fields = []
    actions = []
    list_filter = []

    def __init__(self, model, site):
        self.model = model
        self.site = site

    def patch_delete(self, request, queryset):
        queryset.delete()
    patch_delete.short_description = "批量刪除"


    def edit(self, obj=None, header=False):
        if header:
            return "操做"

        _url = self.get_change_url(obj)
        return mark_safe("<a href='%s'>編輯</a>" % _url)

    def deletes(self, obj=None, header=False):
        if header:
            return '操做'

        _url = self.get_delete_url(obj)
        return mark_safe("<a href='%s'>刪除</a>" % _url)

    def checkbox(self, obj=None, header = False):
        if header:
            return mark_safe("<input id='choice' type='checkbox'>")
        return mark_safe("<input class='choice_item' type='checkbox' name='selected_pk' value='%s'>" % obj.pk)

    def get_modelform_class(self):
        if not self.modelform_class:
            class ModelFormDemo(ModelForm):
                class Meta:
                    model = self.model
                    fields = "__all__"
            return ModelFormDemo
        else:
            return self.modelform_class

    def add_view(self, request):
        ModelFormDemo = self.get_modelform_class()
        form = ModelFormDemo()

        if request.method == 'POST':
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())

        return render(request, 'add_view.html', locals())

    def delete_view(self, request, delete_id):
        url = self.get_list_url()

        if request.method == 'POST':
            self.model.objects.filter(pk=delete_id).delete()
            return redirect(url)

        return render(request, 'delete_view.html', locals())

    def change_view(self, request, change_id):
        ModelFormDemo = self.get_modelform_class()
        edit_obj = self.model.objects.filter(pk=change_id).first()
        form = ModelFormDemo(instance=edit_obj)

        if request.method == "POST":
            form = ModelFormDemo(request.POST, instance=edit_obj)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())

        return render(request,'change_view.html', locals())

    def new_list_play(self):
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        temp.append(ModelStark.edit)
        temp.append(ModelStark.deletes)

        return temp

    def new_actions(self):
        temp = []
        temp.append(ModelStark.patch_delete)
        temp.extend(self.actions)

        return temp

    def get_change_url(self, obj):
        model_name = self.model._meta.model_name
        app_lable = self.model._meta.app_label

        _url = reverse("%s_%s_change" % (app_lable, model_name), args=(obj.pk,))

        return _url

    def get_delete_url(self, obj):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))

        return _url

    def get_add_url(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_add" % (app_label, model_name))

        return _url

    def get_list_url(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_list" % (app_label, model_name))

        return _url

    def get_search_condition(self, request):
        key_words = request.GET.get('q', "")
        self.key_words = key_words
        search_connection = Q()
        if key_words:
            search_connection.connector = "or"
            for search_field in self.search_fields:
                search_connection.children.append((search_field+"__contains", key_words))

        return search_connection

    def get_filter_condition(self, request):
        filter_condition = Q()   # 默認是 and 不是 or
        for filter_field, val in request.GET.items():
            if filter_field in self.list_filter:
                filter_condition.children.append((filter_field, val))

        return filter_condition

    def list_view(self, request):
        if request.method == "POST":
            print("request.POST:",request.POST)
            # 'action': ['patch_init'], 'selected_pk': ['1', '2']
            action = request.POST.get('action')
            selected_pk = request.POST.getlist('selected_pk')
            action_func = getattr(self, action) # 反射
            queryset = self.model.objects.filter(pk__in=selected_pk)  # 秒!!!
            ret = action_func(request, queryset)
            # return ret

        # 獲取searchd得Q對象
        search_connection = self.get_search_condition(request)

        # 獲取filter得Q對象
        filter_condition = self.get_filter_condition(request)

        # 篩選當前表得全部數據
        data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)
        
        # 展現數據
        showlist = ShowList(self, data_list, request)

        # 構建一個查看url
        add_url = self.get_add_url()
        return render(request, 'list_view.html',locals())

    def get_urls2(self):
        temp = []

        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        temp.append(url(r'add/', self.add_view, name="%s_%s_add" % (app_label, model_name)))
        temp.append(url(r'(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
        temp.append(url(r'(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name)))
        temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))

        return temp

    @property
    def urls2(self):

        return self.get_urls2(), None, None


class StarkSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, model, stark_class=None):
        if not stark_class:
            stark_class = ModelStark

        self._registry[model] = stark_class(model,self)


    def get_urls(self):
        temp = []
        
        # 模型表,配置類對象
        for model, stark_class_obj in self._registry.items():
            model_name = model._meta.model_name
            app_label = model._meta.app_label

            # 分發增刪改查
            temp.append(url(r'%s/%s/'%(app_label,model_name), stark_class_obj.urls2))

        return temp

    @property
    def urls(self):

        return self.get_urls(), None, None


site = StarkSite()
stark.py

 

list_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery-1.12.4.min.js"></script>
    <style type="text/css">
        .filter a{text-decoration: none; color: grey}
        .active{ color: red!important;}
    </style>
</head>
<body>

<h4>數據列表</h4>

<div class="container">
    <div class="row">
        <div class="col-md-9">
            <a href="{{ add_url }}" class="btn btn-primary">添加數據</a>

            {% if showlist.config.search_fields %}
                <form action="" class="pull-right">
                    <input type="text" name="q" value="{{ showlist.config.key_words }}"><button>submit</button>
                </form>
            {% endif %}

            <form action="" method="post">
                {% csrf_token %}
                <select name="action" id="" style="width: 200px; padding: 5px 8px; display: inline-block; ">
                    <option value="">------------</option>
                    {% for item in showlist.get_action_list %}
                        <option value="{{ item.name }}">{{ item.desc }}</option>
                    {% endfor %}

                </select><button type="submit" class="btn btn-info">GO</button>

                <table class="table table-bordered table-striped">
                    <thead>
                    <tr>
                        {% for item in showlist.get_header %}
                            <th>{{ item }}</th>
                        {% endfor %}

                    </tr>

                    </thead>
                    <tbody>
                    {% for data in showlist.get_body %}
                        <tr>
                            {% for item in data %}
                                <td>{{ item }}</td>
                            {% endfor %}

                        </tr>
                    {% endfor %}

                    </tbody>
                </table>

                <nav>
                    <ul class="pagination">
                        {{ showlist.pagination.page_html|safe }}
                    </ul>
                </nav>

            </form>


        </div>

        <div class="col-md-3">
            {% if showlist.config.list_filter %}
                <h4>Filter</h4>
                {% for filter_field, linktags in showlist.get_filter_linktags.items %}
                    <div class="well">
                        <p>{{ filter_field.upper }}</p>
                        {% for link in linktags %}
                            <p>{{ link|safe }}</p>
                        {% endfor %}

                    </div>
                {% endfor %}
            {% endif %}

        </div>
    </div>
</div>

<script type="text/javascript">
    $('#choice').click(function () {
        if($(this).prop('checked')){
            $('.choice_item').prop('checked',true)
        }else{
            $('.choice_item').prop('checked',false)
        }
    })


</script>

</body>
</html>
list_view.html

 

2、pop

效果圖

 

知識點

pop功能:
1.在一對多和多對多字段後渲染 +
2.+對應的跳轉路徑
3.保存添加記錄同時,將原頁面的對應的下拉菜單中添加該記錄

--------------------------
知識點:
1.在一對多和多對多字段後渲染+
後臺須要先判斷 是不是 一對多 多對多字段
modelform, 遍歷
for bfield in form:
print(bfield.field) # 是每一個form字段對象
因此:判斷是不是一對多,多對多字段
if isinstance(bfield.field, ModelChoiceField):
bfield.is_pop = True # 爲特殊字段加上特有屬性,方便前端判斷。

注意:ModelMultipleChoiceField(多對多) 繼承 ModelChoiceField(一對多)
ModelChoiceField 繼承 ChoiceField
    for bfield in form:
        print(type(bfield))  # 字段類型
            # <class 'django.forms.boundfield.BoundField'>

        print('name', bfield.name)  # 字段名字符串 publish authors

        from django.forms.boundfield import BoundField
        print(bfield.field)  # 字段對象
        # <django.forms.fields.CharField object at 0x0000014BD355EBA8>
        # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8>
        # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68>

        from django.forms.models import ModelChoiceField
            if isinstance(bfield.field, ModelChoiceField):
                # 給一對多,多對多字段,對象加個屬性is_pop;前端就能夠判斷在哪一個位置加+
                bfield.is_pop = True

    注意: ModelMultipleChoiceField 繼承 ModelChoiceField
         因此:只須要判斷 isinstance(bfield.field, ModelChoiceField)

2.+對應的跳轉路徑
form表單得一對多,多對多得字段對象
bfield.field.queryset.model # 相關聯得模型!!<class 'app01.models.Publish'>
#(模型表得對象去找 filter_field_obj.rel.to.objects.all(),到to是模型。)

根據queryset找model queryset.model
根據model找queryset model.objects.all()

related_model_name = bfield.field.queryset.model._meta.model_name # publish
related_app_label = bfield.field.queryset.model._meta.app_label # app01

反向解析url
_url = reverse("%s_%s_add" % (related_app_label, related_model_name))

# 爲bfield添加本身特有的url,
# ?後面是爲了,區分是top,仍是正常頁面打開/add/,方便以後關閉,以及賦值。
bfield.url = _url + "?pop_res_id=id_%s" % bfield.name

    if isinstance(bfield.field, ModelChoiceField):
        bfield.is_pop = True

        print('---',bfield.field.queryset.model)
        # <class 'app01.models.Publish'>
        # <class 'app01.models.Author'>
        related_model_name = bfield.field.queryset.model._meta.model_name
        related_app_label = bfield.field.queryset.model._meta.app_label
        _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
        bfield.url = _url + "?pop_res_id=id_%s" % bfield.name
 
3.關閉彈出得top頁面
top彈出,關閉頁面,回到到原頁面;
正常添加,跳轉到list頁面;

返回 res = {"pk":obj.pk, 'text':str(obj), "pop_res_id": pop_res_id}
pop.html,方便關閉pop頁面

pop.html:
關閉以及將接收得數據傳到原頁面上!
window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}");
window.close()

    # 兩種狀況
    pop_res_id = request.GET.get('pop_res_id')
    if pop_res_id:
        res = {"pk":obj.pk, 'text':str(obj), "pop_res_id": pop_res_id}
        return render(request,'pop.html',{"res":res})
    else:
        return redirect(self.get_list_url())

    <script type="text/javascript">
        window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}");
        window.close()
    </script>

4.原頁面接收數據,並顯示剛添加得數據(window.opener
拿到 id, text, pop_res_id
動態建立 option append到對應得下拉框中,並選中;
var $option = $("<option>");
$option.html(text);
$option.val(pk);
$option.attr('selected','selected');

$('#'+pop_res_id).append($option)

 pop.html
        window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}");
        window.close()

     add_view.html
<script type="text/javascript"> function pop_response(pk, text,pop_res_id) { console.log(pk); console.log(text); console.log(pop_res_id); // optiond得文本值 和value var $option = $("<option>"); //<option></option> $option.html(text); $option.val(pk); $option.attr('selected','selected'); $('#'+pop_res_id).append($option) } </script>
 
    前端展現:定位,(父相子絕)
     <div style="position: relative">
        {% if field.is_pop %}
            <a onclick="pop('{{ field.url }}')"><span style="font-size: 23px;position: absolute; right: -23px; top: 25px;">+</span></a>
        {% endif %}
     </div>

def add_view(self, request): ModelFormDemo = self.get_modelform_class() form = ModelFormDemo() for bfield in form: print('i::', type(bfield)) # 字段類型 # print('i::', bfield.field) # 字段對象 # print('name', bfield.name) # 字段名字符串 publish authors # <class 'django.forms.boundfield.BoundField'> from django.forms.boundfield import BoundField print(bfield.field) from django.forms.models import ModelChoiceField from django.forms.models import ModelMultipleChoiceField # <django.forms.fields.CharField object at 0x0000014BD355EBA8> # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8> # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68> from django.forms.models import ModelChoiceField if isinstance(bfield.field, ModelChoiceField): bfield.is_pop = True print('---',bfield.field.queryset.model) # <class 'app01.models.Publish'> # <class 'app01.models.Author'> related_model_name = bfield.field.queryset.model._meta.model_name related_app_label = bfield.field.queryset.model._meta.app_label _url = reverse("%s_%s_add" % (related_app_label, related_model_name)) bfield.url = _url + "?pop_res_id=id_%s" % bfield.name if request.method == 'POST': form = ModelFormDemo(request.POST) if form.is_valid(): obj = form.save() # 兩種狀況 pop_res_id = request.GET.get('pop_res_id') if pop_res_id: res = {"pk":obj.pk, 'text':str(obj), "pop_res_id": pop_res_id} return render(request,'pop.html',{"res":res}) else: return redirect(self.get_list_url()) return render(request, 'add_view.html', locals())

補充:javascript

ChoiceFiled
ModelChoiceFiled(ChoiceFiled) ---- select(單選) --- ForeignKey
MultiModelChoiceFiled (ModelChoiceFiled)----select(多選) --- ManyToManyField

Book模型,form表單,modelform;
modelform幫咱們轉成了form表單;

bootstrap頁面自適應: col-xs-8

class Book(model.Model):
    title = models.CharField(max_length=32)
    price = models.IntegerField()
    publish=model.Foreignkey("Publish")
    authors=model.ManyToMany("Author")

from django.forms import ModelForm
class BookForm(ModelForm):
    class Meta:
        model=Book
        fields="__all__"

from django import forms
class BookForm(forms.Form):
    title=forms.CharField(max_length=32)
    price=forms.IntegerField()
    publish = forms.ModelChoiceFiled("Publish")
    authors = forms.ModelMultipleChoiceField("Author")

form=BookForm()

 

stark/service/stark.py

from django.conf.urls import url
from django.shortcuts import HttpResponse, reverse, redirect, render
from django.utils.safestring import mark_safe
from django.urls import reverse
from django.forms import ModelForm
from stark.utils.page import Pagination
from django.db.models import Q

class ShowList(object):
    def __init__(self, config, data_list, request):
        self.config = config
        self.data_list = data_list
        self.request = request

        # 分頁
        data_count = self.data_list.count()
        current_page = self.request.GET.get('page',1)
        base_path = self.request.path

        self.pagination = Pagination(current_page, data_count, base_path, self.request.GET, per_page_num=3, pager_count=11)
        self.page_data = self.data_list[self.pagination.start:self.pagination.end]

        self.actions = self.config.new_actions()


    def get_filter_linktags(self):
        print('list_filter:',self.config.list_filter) # ['title', 'publish', 'authors']

        link_dic = {}
        import copy

        for filter_field in self.config.list_filter:
            params = copy.deepcopy(self.request.GET)

            cid = self.request.GET.get(filter_field, 0)

            filter_field_obj = self.config.model._meta.get_field(filter_field)
            print(filter_field_obj)
            print(type(filter_field_obj))
            """
            app01.Book.title
            <class 'django.db.models.fields.CharField'>
            app01.Book.publish
            <class 'django.db.models.fields.related.ForeignKey'>
            app01.Book.authors
            <class 'django.db.models.fields.related.ManyToManyField'>
            """

            from django.db.models.fields.related import ForeignKey
            from django.db.models.fields.related import ManyToManyField

            # print("rel...", filter_field_obj.rel.to.objects.all())
            # rel... <QuerySet [<Publish: 南京出版社>, <Publish: 上海出版社>, <Publish: 河北出版社>, <Publish: 3>]>
            # rel... <QuerySet [<Author: yuan>, <Author: egon>, <Author: alex>]>

            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                data_list = filter_field_obj.rel.to.objects.all()  # 關聯對象 適用 一對多,多對多
            else: # 普通字段
                data_list = self.config.model.objects.all().values('pk', filter_field)

            # 處理所有標籤
            temp = []
            if params.get(filter_field):
                del params[filter_field]
                temp.append("<a href='?%s'>所有</a>" % (params.urlencode()))
            else:
                temp.append("<a href='#' class='active'>所有</a>")

            # 處理數據標籤
            for obj in data_list:
                if isinstance(filter_field_obj,ForeignKey) or isinstance(filter_field_obj,ManyToManyField):
                    pk = obj.pk
                    text = str(obj)
                    params[filter_field] = pk
                else:
                    pk = obj.get('pk')
                    text = obj.get(filter_field)
                    params[filter_field] = text

                _url = params.urlencode() # 序列化後得結構

                if cid == str(pk) or cid == text:
                    link_tag = "<a class='active' href='?%s'>%s</a>" % (_url, text)
                else:
                    link_tag = "<a href='?%s'>%s</a>" % (_url, text)

                temp.append(link_tag)

            link_dic[filter_field] = temp

        return link_dic

    def get_action_list(self):
        temp = []
        for action in self.actions:
            temp.append({
                "name": action.__name__,
                "desc":action.short_description
            })

        return temp
    

    def get_header(self):
        # 構建表頭
        header_list = []
        # header_list = ['選擇','pk',...'操做','操做']
        for field in self.config .new_list_play():
            if callable(field):
                val = field(self.config, header=True)
            else:
                if field == "__str__":
                    val = self.config .model._meta.model_name.upper()
                else:  # 根據str 拿字段對象 取中文
                    val = self.config .model._meta.get_field(field).verbose_name

            header_list.append(val)

        return header_list

    def get_body(self):
        # 構建表單
        new_data_list = []
        for data in self.page_data:
            temp = []
            for field in self.config.new_list_play():  # ['title','price'] 字符串找對象得屬性 反射
                # print('field:', field)
                if callable(field):
                    val = field(self.config, data)
                else:
                    val = getattr(data, field)

                    if field in self.config.list_display_links:
                        _url = self.config.get_change_url(data)
                        val = mark_safe("<a href='%s'>%s</a>" % (_url, val))

                temp.append(val)

            new_data_list.append(temp)  # [['yuan', 12], ['alex', 18], ['egon', 22]]

        return new_data_list


class ModelStark(object):
    list_display = ["__str__"]
    list_display_links = []
    modelform_class = []
    search_fields = []
    actions = []
    list_filter = []

    def __init__(self, model, site):
        self.model = model
        self.site = site

    def patch_delete(self, request, queryset):
        queryset.delete()
    patch_delete.short_description = "批量刪除"


    def edit(self, obj=None, header=False):
        if header:
            return "操做"

        _url = self.get_change_url(obj)
        return mark_safe("<a href='%s'>編輯</a>" % _url)

    def deletes(self, obj=None, header=False):
        if header:
            return '操做'

        _url = self.get_delete_url(obj)
        return mark_safe("<a href='%s'>刪除</a>" % _url)

    def checkbox(self, obj=None, header = False):
        if header:
            return mark_safe("<input id='choice' type='checkbox'>")
        return mark_safe("<input class='choice_item' type='checkbox' name='selected_pk' value='%s'>" % obj.pk)

    def get_modelform_class(self):
        if not self.modelform_class:
            class ModelFormDemo(ModelForm):
                class Meta:
                    model = self.model
                    fields = "__all__"
            return ModelFormDemo
        else:
            return self.modelform_class

    def add_view(self, request):
        ModelFormDemo = self.get_modelform_class()
        form = ModelFormDemo()
        
        for bfield in form:
            print('i::', type(bfield))  # 字段類型
            # print('i::', bfield.field)  # 字段對象
            # print('name', bfield.name)  # 字段名字符串 publish authors

            # <class 'django.forms.boundfield.BoundField'>
            from django.forms.boundfield import BoundField
            print(bfield.field)
            from django.forms.models import ModelChoiceField
            from django.forms.models import ModelMultipleChoiceField
            # <django.forms.fields.CharField object at 0x0000014BD355EBA8>
            # <django.forms.models.ModelChoiceField object at 0x0000014BD355ECF8>
            # <django.forms.models.ModelMultipleChoiceField object at 0x0000014BD355ED68>
            from django.forms.models import ModelChoiceField
            if isinstance(bfield.field, ModelChoiceField):
                bfield.is_pop = True

                print('---',bfield.field.queryset.model)
                # <class 'app01.models.Publish'>
                # <class 'app01.models.Author'>
                related_model_name = bfield.field.queryset.model._meta.model_name
                related_app_label = bfield.field.queryset.model._meta.app_label
                _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
                bfield.url = _url + "?pop_res_id=id_%s" % bfield.name

        if request.method == 'POST':
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                obj = form.save()

                # 兩種狀況
                pop_res_id = request.GET.get('pop_res_id')
                if pop_res_id:
                    res = {"pk":obj.pk, 'text':str(obj), "pop_res_id": pop_res_id}
                    return render(request,'pop.html',{"res":res})
                else:
                    return redirect(self.get_list_url())

        return render(request, 'add_view.html', locals())

    def delete_view(self, request, delete_id):
        url = self.get_list_url()

        if request.method == 'POST':
            self.model.objects.filter(pk=delete_id).delete()
            return redirect(url)

        return render(request, 'delete_view.html', locals())

    def change_view(self, request, change_id):
        ModelFormDemo = self.get_modelform_class()
        edit_obj = self.model.objects.filter(pk=change_id).first()
        form = ModelFormDemo(instance=edit_obj)

        if request.method == "POST":
            form = ModelFormDemo(request.POST, instance=edit_obj)
            if form.is_valid():
                form.save()
                return redirect(self.get_list_url())

        return render(request,'change_view.html', locals())

    def new_list_play(self):
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        temp.append(ModelStark.edit)
        temp.append(ModelStark.deletes)

        return temp

    def new_actions(self):
        temp = []
        temp.append(ModelStark.patch_delete)
        temp.extend(self.actions)

        return temp

    def get_change_url(self, obj):
        model_name = self.model._meta.model_name
        app_lable = self.model._meta.app_label

        _url = reverse("%s_%s_change" % (app_lable, model_name), args=(obj.pk,))

        return _url

    def get_delete_url(self, obj):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_delete" % (app_label, model_name), args=(obj.pk,))

        return _url

    def get_add_url(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_add" % (app_label, model_name))

        return _url

    def get_list_url(self):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        _url = reverse("%s_%s_list" % (app_label, model_name))

        return _url

    def get_search_condition(self, request):
        key_words = request.GET.get('q', "")
        self.key_words = key_words
        search_connection = Q()
        if key_words:
            search_connection.connector = "or"
            for search_field in self.search_fields:
                search_connection.children.append((search_field+"__contains", key_words))

        return search_connection

    def get_filter_condition(self, request):
        filter_condition = Q()   # 默認是 and 不是 or
        for filter_field, val in request.GET.items():
            if filter_field in self.list_filter:
                filter_condition.children.append((filter_field, val))

        return filter_condition

    def list_view(self, request):
        if request.method == "POST":
            print("request.POST:",request.POST)
            # 'action': ['patch_init'], 'selected_pk': ['1', '2']
            action = request.POST.get('action')
            selected_pk = request.POST.getlist('selected_pk')
            action_func = getattr(self, action) # 反射
            queryset = self.model.objects.filter(pk__in=selected_pk)  # 秒!!!
            ret = action_func(request, queryset)
            # return ret

        # 獲取searchd得Q對象
        search_connection = self.get_search_condition(request)

        # 獲取filter得Q對象
        filter_condition = self.get_filter_condition(request)

        # 篩選當前表得全部數據
        data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)
        
        # 展現數據
        showlist = ShowList(self, data_list, request)

        # 構建一個查看url
        add_url = self.get_add_url()
        return render(request, 'list_view.html',locals())

    def get_urls2(self):
        temp = []

        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

        temp.append(url(r'add/', self.add_view, name="%s_%s_add" % (app_label, model_name)))
        temp.append(url(r'(\d+)/delete/', self.delete_view, name="%s_%s_delete" % (app_label, model_name)))
        temp.append(url(r'(\d+)/change/', self.change_view, name="%s_%s_change" % (app_label, model_name)))
        temp.append(url(r'^$', self.list_view, name="%s_%s_list" % (app_label, model_name)))

        return temp

    @property
    def urls2(self):

        return self.get_urls2(), None, None


class StarkSite(object):
    def __init__(self):
        self._registry = {}

    def register(self, model, stark_class=None):
        if not stark_class:
            stark_class = ModelStark

        self._registry[model] = stark_class(model,self)


    def get_urls(self):
        temp = []
        
        # 模型表,配置類對象
        for model, stark_class_obj in self._registry.items():
            model_name = model._meta.model_name
            app_label = model._meta.app_label

            # 分發增刪改查
            temp.append(url(r'%s/%s/'%(app_label,model_name), stark_class_obj.urls2))

        return temp

    @property
    def urls(self):

        return self.get_urls(), None, None


site = StarkSite()
stark.py

 

add_view.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
    <script src="/static/js/jquery-1.12.4.min.js"></script>
    <style type="text/css">
        input,select {
            display: block;
            width: 100%;
            height: 34px;
            padding: 6px 12px;
            font-size: 14px;
            line-height: 1.42857143;
            color: #555;
            background-color: #fff;
            background-image: none;
            border: 1px solid #ccc;
            border-radius: 4px;
            -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
            -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
            -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
            transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
        }
        .error{
            color: red;
        }

    </style>

</head>
<body>
<h3>添加頁面</h3>

{% include 'form.html' %}

<script type="text/javascript">
    function pop_response(pk, text,pop_res_id) {
        console.log(pk);
        console.log(text);
        console.log(pop_res_id);

         // optiond得文本值 和value
        var $option = $("<option>"); //<option></option>
        $option.html(text);
        $option.val(pk);
        $option.attr('selected','selected');

        $('#'+pop_res_id).append($option)
    }

</script>

</body>
</html>
add_view.html

 

form.html

<div class="container">
    <div class="row">
        <div class="col-md-6 col-xs-10 col-md-offset-1">
            <form action="" method="post" novalidate>
                {% csrf_token %}
                {% for field in form %}
                    <div style="position: relative">
                        <label for="">{{ field.label }}</label>
                        {{ field }} <span class="error pull-right">{{ field.errors.0 }}</span>

                        {% if field.is_pop %}
                            <a onclick="pop('{{ field.url }}')"><span style="font-size: 23px;position: absolute; right: -23px; top: 25px;">+</span></a>
                        {% endif %}

                    </div>
                {% endfor %}

                <button type="submit" class="btn btn-info">btn</button>
            </form>

        </div>
    </div>

</div>
<script type="text/javascript">
    function pop(url) {
        window.open(url,"","width=500,height=300,top=100,left=100")
    }
</script>
form.html

 

pop.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<script type="text/javascript">
    window.opener.pop_response("{{ res.pk}}","{{ res.text }}","{{ res.pop_res_id }}");
    window.close()

</script>

</body>
</html>
pop.html

 

3、stark - 總結

總結

(單例,繼承,反射,面向對象,modelform 應用得很好!!)

1.註冊表
單例模式 site = StarkSite()

2.生成url
url(r'^stark/', ([],None,None))

3.數據列表展現
可自定義配置顯示:
list_display = ["__str__"]
list_display_links = []
modelform_class = []
search_fields = []
actions = []
list_filter = []

4.增刪改頁面 modelform

5.分頁
自定義分頁組件 stark/utils/page.py
class Pagination(object):
...
...

6.search模糊查詢
Q查詢 or
search_connection = Q()
...
data_list = self.model.objects.all().filter(search_connection)

7.action批量處理
def patch_init(self, request, queryset):
queryset.update(price=123)
...
patch_init.short_description = "批量初始化"

actions = [patch_init]

queryset = self.model.objects.filter(pk__in=selected_pk)

8.filter過濾
list_filter = ['title','publish', 'authors']
eg:{"publish":["<a href=''>所有</a>","<a href=''>南京出版社</a>","<a href=''>上海出版社</a>"]
"authors":["<a href=''>所有</a>","<a href=''>yuan</a>","<a href=''>egon</a>"]
}

Q查詢 and
filter_condition = Q()
data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)

9.pop彈出
在一對多和多對多字段後渲染 +
+對應的跳轉路徑
保存添加記錄同時,將原頁面的對應的下拉菜單中添加該記錄

test.py

from django.test import TestCase

# Create your tests here.

#
# class  A(object):
#
#     x=12
#     def __init__(self,m):
#         self.z = m
#     def xxx(self):
#         print(self.x)
#         print(self.z)
#
# class B(A):
#     x=5
#     z = 11
#
# b=B(10)
# b.xxx()

#######################################
#
# class Person(object):
#     def __init__(self,name):
#         self.name = name
#
# alex = Person('alex')
# print(alex.name)
#
# s = 'name'
#
# # print(alex.s)  # 用反射
#
# getattr(alex,s)
#
# print(getattr(alex,s))

#######################################
 # 沒學面向對象以前,都是函數 ,

# 函數 方法

# class Person(object):
#     def __init__(self,name):
#         self.name = name
#
#     def eat(self):  # 方法!
#         print(self)
#         print('eating...')
#
# # 實例方法
# # egon = Person('egon')
# # egon.eat()
#
# # 函數
# Person.eat('ss')

#######################################

# list = [1,2,3]
# list.append(4)
# print(list)
# list.insert(0,100)
# print(list)

#######################################
# li = []
# print(len(li))
#
# s = "sss"
# print(isinstance(s,str))

#######################################

# class Person(object):
#     def __init__(self,name):
#         self.name = name
#
#     def __str__(self):
#         return self.name
#
# alex = Person('alex')
# # print(alex.name)
# # print(alex)
#
# print(alex.__str__())
# print(str(alex))
#
# print(getattr(alex,'__str__')())


#######################################
# temp = []
# temp.append(1)
# temp.extend([1,2,3])
# print(temp)

#######################################

# def foo():
#     return
#
# print(foo.__name__)


#######################################
# 查詢是字段名稱
# Book.objects.filter(Q(title='yuan')|Q(price='123'))

# Q() 查詢放str,
# q = Q()
# q.connection = 'or'
# q.children.append(('title','yuan'))
# q.children.append(('price',123))

#######################################

# ret = self.model.objects.filter(title__startswith='py')
# ret = self.model.objects.filter(price__in=[123, 111, 21, 11])
# ret = self.model.objects.filter(price__range=[10, 100])
# ret = self.model.objects.filter(title__contains='y')
# ret = self.model.objects.filter(title__contains='o')
# ret = self.model.objects.filter(title__icontains='o')
# print(ret)
# return HttpResponse('ok')

#######################################

# def foo():
#     print('ok')
#
# print(foo.__name__)
# print(type(foo.__name__))
# foo.desc = '123'
# print(foo.desc)
# a = foo()
# a.desc = 12
# print(a.desc)

#######################################

# class A():

# str = "http://127.0.0.1:8000/stark/app01/book/?publish=1&author=5"

#######################################
# class A(object):
#     pass
# 
# class B(A):
#     pass
# 
# b = B()
# print(isinstance(b,A)) # True
# print(isinstance(b,B)) # True
test.py

 

code

 

原始版css

  https://github.com/alice-bj/stark_pro_0html

簡潔版前端

  https://github.com/alice-bj/stark_adminjava

相關文章
相關標籤/搜索