1. 程式人生 > >10.20stark元件已經完工

10.20stark元件已經完工

2018-10-20 19:37:31

stark元件已經做完!基本上和Django的admin一樣!

放上github連線:https://github.com/TrueNewBee/pythonDemo/blob/master/stark_demo.rar

放上正版的stark元件: https://github.com/TrueNewBee/pythonDemo/blob/master/stark.zip

stark元件已經完成,明天整理Django部落格!

後面的就是crm專案啦!

越努力,與幸運!永遠不要高估自己!

知識點  test.py

子類繼承父類 , 然後和父類的型別一樣 isinstance()

modelForm 把  model.Model 轉換成 form.Form

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



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() for i in form: if isinstance(i,ModelChoiceFiled): pass '''

 

pop功能:

 

1 如何在一對多和多對多欄位後渲染 +

2 +對應的跳轉路徑

上述兩步實現邏輯

def add_view(self, request):
    # 例項化form類物件
    ModelFormDemo = self.get_modelform_class()
    form = ModelFormDemo()
    for bfield in form:
        # 這個可以看原始碼,然後類呼叫所需屬性
        from django.forms.boundfield import BoundField
        print(bfield.field)             # 欄位物件
        print("name",bfield.name)       # 欄位名(字串)
        print(type(bfield.field))       # 欄位型別
        # 看原始碼可得 多對多和一對多是ModelChoiceFiled的型別
        from django.forms.models import ModelChoiceField
        if isinstance(bfield.field, ModelChoiceField):
            # 增加一個屬性,傳給前端做判斷,是否顯示這個 +按鈕
            bfield.is_pop = True
            print("=======>", bfield.field.queryset.model) # 一對多或者多對多欄位的關聯模型表
            # 通過下面兩個方法,找到表和app名字
            related_model_name = bfield.field.queryset.model._meta.model_name
            related_app_label = bfield.field.queryset.model._meta.app_label
            # 拼接url傳給前端
            _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
            # 建立一個新的屬性url 給前端呼叫
            bfield.url = _url+"?pop_res_id=id_%s" % bfield.name

上面是get請求,下面是這個函式post請求的邏輯

# POST請求
        if request.method == "POST":
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                obj = form.save()       # 儲存資料,並返回一個obj
                pop_res_id = request.GET.get("pop_res_id")
                # 判斷是點選+按鈕帶引數訪問,還是通過add頁面直接訪問的
                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())

 


3 儲存新增記錄同時,將原頁面的對應的下拉選單中新增該記錄
通過js方法!

from.html

<script>
    // 開啟一個新的視窗新增資料
    function pop(url) {
        window.open(url,"","width=600,height=400,top=100,left=100")
    }
</script>

pop.html  (作用就是一個橋樑,傳給父類頁面相應引數,然後關閉)

<script>
    // 呼叫父類的方法,並把相應引數傳給父類
    window.opener.pop_response('{{ res.pk }}',"{{ res.text }}",'{{ res.pop_res_id }}')
    // 傳過去後直接關閉頁面,由於響應很快,時間可以忽略
    window.close()
</script>

add.html(接受子頁面傳入的引數,然後通過jQuery方法把傳入的資料直接新增到select框中預設選中)

<script>
   function pop_response (pk,text,id) {

       console.log(pk,text,id);
        // 接受子頁面傳入的引數,並通過jQuery,新增相應的標籤和值
        // 選擇哪一個select標籤
        // option的文字值和value值

       var $option=$('<option>');  //  <option></option>
       $option.html(text); //  <option>南京出版社</option>
       $option.val(pk);     //  <option value=111>南京出版社</option>
       $option.attr("selected","selected") ;   //  <option value=111>南京出版社</option>
       $("#"+id).append($option)

   }
</script>

然後附上這個stark元件的主要程式碼!主要看邏輯實現!!想看原始碼就去github下一下!原始碼裡面都註釋好啦!!!!!

 stark/stark.py

from django.conf.urls import url
from django.shortcuts import render, redirect
from django.urls import reverse
from django.utils.safestring import mark_safe
from django.forms import ModelForm
from stark.utils.page import Pagination
from django.db.models import Q
from django.db.models.fields.related import ForeignKey
from django.db.models.fields.related import ManyToManyField
import copy
"""
Stark元件!
2018-10-20 19:39:08
功能簡介:
1. 使用方法和Django的admin一樣,需要在strak裡面註冊,詳情看app01/stark.py
2. 實現了對不同表的url的各級分發
3. 使用者可以自定義配置表的現實資訊 詳情可以看app01/stark.py
4. 實現了對錶新增資料pop的功能!
5. 最強大就是,你可以拿去直接用,和admin一樣,而且不需要超級使用者!
2333333333333333333333333333333
"""
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 = int(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]
        # actions   獲取actions這個配置類的列表
        self.actions = self.config.actions  # [patch_init,]

    # 處理filter欄位連線
    def get_filter_linktags(self):
        """用了兩次for迴圈,在演算法上有點綴餘!不過可以用類或函式封裝只是懶-.-能力欠缺!"""
        print("list_filter:", self.config.list_filter)
        link_dic = {}
        for filter_field in self.config.list_filter:  # ["title","publish","authors",]
            params = copy.deepcopy(self.request.GET)
            cid = self.request.GET.get(filter_field, 0)
            print("filter_field", filter_field)  # "publish"
            # 通過_meta.get_field方法,獲取該表名物件
            filter_field_obj = self.config.model._meta.get_field(filter_field)

            print("filter_field_obj", filter_field_obj)
            print(type(filter_field_obj))
            # print("rel...",filter_field_obj.rel.to.objects.all())

            # 判斷一下 如果是多對多或一對多型別的
            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                # 拿到表的所有QuerySet物件
                data_list = filter_field_obj.rel.to.objects.all()  # 【publish1,publish2...】
            else:
                # 這個則是自定義過濾欄位
                data_list = self.config.model.objects.all().values("pk", filter_field)
                print("data_list", data_list)

            temp = []
            # 處理 全部標籤
            if params.get(filter_field):
                # 如果url如果存在引數 則del
                del params[filter_field]
                temp.append("<a href='?%s'>全部</a>" % params.urlencode())
            else:
                # 反之加上class 增加顏色
                temp.append("<a  class='active' href='#'>全部</a>")

            # 處理 資料標籤
            for obj in data_list:
                # 迴圈列表中每個QuerySet的物件然後取到相應的值
                if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):
                    pk = obj.pk
                    text = str(obj)
                    params[filter_field] = pk
                else:  # data_list= [{"pk":1,"title":"go"},....]
                    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

    # 獲取下拉框 使用者配置的action_list
    def get_action_list(self):
        temp = []
        for action in self.actions:
            #  [{"name":""patch_init,"desc":"批量初始化"}]
           temp.append({
               "name": action.__name__,
               "desc": action.short_description
           })
        return temp

    # 構建表頭
    def get_header(self):
        header_list = []
        print("header", self.config.new_list_play())
        # [checkbox,"pk","name","age",edit ,deletes]     【checkbox ,"__str__", edit ,deletes】
        for field in self.config.new_list_play():

            if callable(field):
                # header_list.append(field.__name__)
                val = field(self.config, header=True)
                header_list.append(val)

            else:
                if field == "__str__":
                    header_list.append(self.config.model._meta.model_name.upper())
                else:
                    # header_list.append(field)
                    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 obj in self.page_data:
            temp = []
            for filed in self.config.new_list_play():  # ["__str__",]      ["pk","name","age",edit]
                if callable(filed):
                    val = filed(self.config, obj)
                else:
                    # 這裡捕獲一下異常,因為預設的list_play裡面有__str__ 直接找不到該欄位
                    # 所以直接用getattr方法就行啦!
                    try:
                        field_obj = self.config.model._meta.get_field(filed)
                        if isinstance(field_obj, ManyToManyField):
                            # getattr()僅取到Object, 然後.all() 則可以取到物件
                            ret = getattr(obj, filed).all()
                            t = []
                            for obj in ret:
                                t.append(str(obj))
                            val = ",".join(t)

                        else:
                            val = getattr(obj, filed)
                            if filed in self.config.list_display_links:
                                # "app01/userinfo/(\d+)/change"
                                _url = self.config.get_change_url(obj)
                                val = mark_safe("<a href='%s'>%s</a>" % (_url, val))
                    except Exception as e:
                        val = getattr(obj, filed)
                temp.append(val)
            new_data_list.append(temp)
        return new_data_list


class ModelStark(object):
    # 預設的list_play[]
    list_display = ["__str__", ]
    list_display_links = []
    modelform_class = None
    search_fields = []
    actions = []
    list_filter = []

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

    # 預設的批量刪除action
    def patch_delete(self, request, queryset):
        queryset.delete()
    patch_delete.short_description = "批量刪除"

    # 配置表頭: 刪除 編輯,複選框
    def edit(self, obj=None, header=False):
        """編輯"""
        if header:
            return "操作"
        # return mark_safe("<a href='%s/change'>編輯</a>"%obj.pk)
        _url = self.get_change_url(obj)
        return mark_safe("<a href='%s'>編輯</a>" % _url)

    def deletes(self, obj=None, header=False):
        """刪除"""
        if header:
            return "操作"
        # return mark_safe("<a href='%s/change'>編輯</a>"%obj.pk)
        _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">')
        # value的值不能寫死,
        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__"
                    labels = {
                        ""
                    }
            return ModelFormDemo
        else:
            return self.modelform_class

    # 新增的檢視函式
    def add_view(self, request):
        # 例項化form類物件
        ModelFormDemo = self.get_modelform_class()
        form = ModelFormDemo()
        for bfield in form:
            # 這個可以看原始碼,然後類呼叫所需屬性
            from django.forms.boundfield import BoundField
            print(bfield.field)             # 欄位物件
            print("name",bfield.name)       # 欄位名(字串)
            print(type(bfield.field))       # 欄位型別
            # 看原始碼可得 多對多和一對多是ModelChoiceFiled的型別
            from django.forms.models import ModelChoiceField
            if isinstance(bfield.field, ModelChoiceField):
                # 增加一個屬性,傳給前端做判斷,是否顯示這個 +按鈕
                bfield.is_pop = True
                print("=======>", bfield.field.queryset.model) # 一對多或者多對多欄位的關聯模型表
                # 通過下面兩個方法,找到表和app名字
                related_model_name = bfield.field.queryset.model._meta.model_name
                related_app_label = bfield.field.queryset.model._meta.app_label
                # 拼接url傳給前端
                _url = reverse("%s_%s_add" % (related_app_label, related_model_name))
                # 建立一個新的屬性url 給前端呼叫
                bfield.url = _url+"?pop_res_id=id_%s" % bfield.name
        # POST請求
        if request.method == "POST":
            form = ModelFormDemo(request.POST)
            if form.is_valid():
                obj = form.save()       # 儲存資料,並返回一個obj
                pop_res_id = request.GET.get("pop_res_id")
                # 判斷是點選+按鈕帶引數訪問,還是通過add頁面直接訪問的
                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, id):
        url = self.get_list_url()
        if request.method == "POST":
            self.model.objects.filter(pk=id).delete()
            return redirect(url)
        return render(request, "delete_view.html", locals())

    # 編輯的檢視函式
    def change_view(self, request, id):
        ModelFormDemo = self.get_modelform_class()
        edit_obj = self.model.objects.filter(pk=id).first()
        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, "add_view.html", locals())
        form = ModelFormDemo(instance=edit_obj)
        return render(request, "change_view.html", locals())

    # 搜尋的檢視函式
    def get_serach_conditon(self, request):
        key_word = request.GET.get("q", "")
        self.key_word = key_word
        search_connection = Q()
        if key_word:
            # self.search_fields # ["title","price"]
            search_connection.connector = "or"
            # 用Q的這種新增方法可以新增字串
            for search_field in self.search_fields:
                # search_field+"__contains"  ---->  title__contains="o"   就是title欄位裡面包含字母o的
                search_connection.children.append((search_field + "__contains", key_word))
        return search_connection

    # 過濾filter的檢視函式
    def get_filter_condition(self, request):
        filter_condition = Q()
        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":  # action
            print("POST:", request.POST)
            action = request.POST.get("action")  # patch_init
            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
        # 獲取search的Q物件
        search_connection = self.get_serach_conditon(request)

        # 獲取filter構建Q物件
        filter_condition = self.get_filter_condition(request)

        # 篩選獲取當前表所有資料
        data_list = self.model.objects.all().filter(search_connection).filter(filter_condition)  # 【obj1,obj2,....】

        # 按這ShowList展示頁面
        showlist = ShowList(self, data_list, request)

        # 構建一個檢視URL
        add_url = self.get_add_url()
        return render(request, "list_view.html", locals())

    #  獲取使用者配置類裡面的list_play[]
    def new_list_play(self):
        temp = []
        temp.append(ModelStark.checkbox)
        temp.extend(self.list_display)
        if not self.list_display_links:
            temp.append(ModelStark.edit)
        temp.append(ModelStark.deletes)
        return temp

    # 獲取使用者配置類裡面的actions 這個列表
    def new_actions(self):
        temp=[]
        temp.append(ModelStark.patch_delete)
        temp.extend(self.actions)
        return temp

    """把url進行反向解析,解耦到各自的函式中,函式中直接返回了對應的url"""
    # 獲取修改頁面的url
    def get_change_url(self, obj):
        model_name = self.model._meta.model_name
        app_label = self.model._meta.app_label

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

        return _url

    # 獲刪除改頁面的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

    # 獲取新增頁面的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

    # 獲取檢視頁面的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

    # 二級url分發函式
    def get_urls_2(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 urls_2(self):
        print(self.model)
        return self.get_urls_2(), 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)

    # 一級分發url函式
    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.urls_2))

            '''
            url(r"^app01/userinfo/",UserConfig(Userinfo).urls_2),
            url(r"^app01/book/",ModelStark(Book).urls_2), 


            '''
        return temp

    @property
    def urls(self):

        return self.get_urls(), None, None


# 建立stark的一個單例物件
site = StarkSite()

app01/stark.py

from stark.service.stark import site,ModelStark
from django.shortcuts import HttpResponse
from .models import *
from django.forms import ModelForm


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


class BookConfig(ModelStark):
    # 自定義展示列表
    list_display = ["title", "price", "publishDate", "publish", "authors"]
    # 自定義設定欄位為連線
    list_display_links = ["title"]
    modelform_class = BookModelForm
    # 自定義搜尋欄位
    search_fields = ["title", "price"]

    def patch_init(self, request, queryset):
        print(queryset)
        queryset.update(price=123)
        return HttpResponse("批量初始化OK")
    patch_init.short_description = "批量初始化"
    # 自定義處理函式
    actions = [patch_init]
    # 自定義篩選欄位
    list_filter=["title","publish","authors",]


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

app01/models.py

from django.db import models

# Create your models here.


from django.db import models

# Create your models here.
from django.db import models

# Create your models here.


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    age=models.IntegerField()

    # 與AuthorDetail建立一對一的關係
    authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)

    def __str__(self):
        return self.name

class AuthorDetail(models.Model):

    nid = models.AutoField(primary_key=True)
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)

    def __str__(self):
        return self.telephone



class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name


class Book(models.Model):

    nid = models.AutoField(primary_key=True)
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)

    # 與Publish建立一對多的關係,外來鍵欄位建立在多的一方
    publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
    # 與Author表建立多對多的關係,ManyToManyField可以建在兩個模型中的任意一個,自動建立第三張表
    authors=models.ManyToManyField(to='Author',)
    def __str__(self):
        return self.title