Django-CRM項目學習(二)-模仿admin實現stark

開始今日份整理html

1.stark模塊基本操做

1.1 stark模塊的啓動

保證django自動的加載每個app下的stark.py文件前端

  • 建立django項目,建立stark項目,start app stark
  • settings註冊app
  • stark.app中的apps.py建立def(函數名必須是ready纔會自動執行)
from django.utils.module_loading import autodiscover_modules

class StarkConfig(AppConfig):
    name = 'stark'
    def ready(self):
        autodiscover_modules('stark')

image

1.2 stark模塊的註冊

所謂註冊就是仿照admin模塊,對於註冊的數據表進行記錄,方便後面的url的增刪改查java

仿照admin在stark下建立一個包services,並建立一個sites.py文件,代碼以下python

from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse

class ModelStark(object):

    list_display =("__str__")

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

    def list_view(self, request):

        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        temp = [
            path("", self.list_view),
            path("add/",self. add_view),
            path("(\d+)/change/", self.change_view),
            path("(\d+)/delete/", self.delete_view),
        ]
        return (temp, None, None)



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

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已經註冊的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 項目名
            model_label = model._meta.app_label

            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp


    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()

在app01中建立stark.py文件,並註冊數據庫

from stark.services.sites import site,ModelStark
from .models import Book,Publish,Author,Author_detail

# 分別註冊書籍,出版社以及做者
site.register(Book)
site.register(Publish)
site.register(Author)

print(site._registry)

打印註冊的列表,結果以下django

{<class 'app01.models.Publish'>: <stark.services.sites.ModelStark object at 0x04B66B50>, <class 'app01.models.Book'>: <stark.services.sites.ModelStark object at 0x04B669B0>, <class 'app01.models.Author'>: <stark.services.sites.ModelStark object at 0x04B66970>}

這樣就註冊成功了vim

1.3 URL的分發功能以及頁面樣式理解(很是重要)

爲了自定義的URL。因此咱們纔會有自定義頁面,纔會有配置類。app

(1)在site中StarkSite類中建立一個URLS(self)方法,用@property方式,靜態方法ide

image

(2)將二級分發功能放在配置類模塊中函數

image

(3)配置類中self以及self.model的區別(超級重要)

self:是配置類對象

image

self.model:數據表對象,其實就是數據表的數據

經過上面:便可理解爲何在註冊的時候有一個空字典,在每個表對象進行註冊時,對每個表生成對應的配置類對象,若是一個表對象有本身的自定義樣式,則會走本身自定義樣式,無則會走默認樣式。

這樣就基本實現了url的分發功能,有一級也有二級分發。這塊內容就是理解就會以爲東西少,不理解則東西好多!,只須要記住

self是配置類,self.model就是數據表對象就能夠了。

對於默認類,self爲默認配置類,其中self.model爲傳入的表對象,展現則使用默認類中的樣式。

對於自定義類,self爲自定義類,其中self.model爲傳入的表對象,自定義類繼承默認類,優先使用自定義定義的類方法。

以下圖展現

TIM圖片20190415193021TIM圖片20190415193031

from django.contrib import admin
from django.urls import path
from django.shortcuts import render,HttpResponse

class ModelStark(object):

    list_display =("__str__")

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

    def list_view(self, request):

        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        temp = [
            path("", self.list_view),
            path("add/",self. add_view),
            path("(\d+)/change/", self.change_view),
            path("(\d+)/delete/", self.delete_view),
        ]
        return (temp, None, None)


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

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已經註冊的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 項目名
            model_label = model._meta.app_label
            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp
    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()
stark核心代碼

訪問順序

path("stark/app01/book",BookConfig(Book).list_view)
path("stark/app01/book/add",BookConfig(Book).add_view)      
path("stark/app01/publish",ModelAdmin(Publish).list_view)
path("stark/app01/publish/add",ModelAdmin(Publish).add_view)

2.stark基礎頁面

2.1 stark的數據展現

2.1.1 普通數據與一對多數據展現

2.1.1.1 基於自定義類的數據展現

普通數據就是相似於數據庫中book表中的title,price,出版日期,做者等基礎數據,django自然對一對多支持

#目標數據類型
        new_data=[
            ["python",123],
            ["java",234]
        ]

視圖層以下

def list_view(self, request):
    """
    self:當前配置類
    self.model:當前訪問的表數據
    :param request:
    :return:
    """
    # 當前要展現的數據表的數據
    querset = self.model.objects.all()
    print(querset)

    print(self.list_display)

    # 用於構建視圖中的展現數據
    new_data = []
    for obj in querset:
        temp = []
        # 構建列表嵌套中的小列表,使用的是自定義配置列表中要展現的內容
        for field_or_info in self.list_display:
            vim = getattr(obj,field_or_info)
            temp.append(vim)

        new_data.append(temp)
    print('new_data', new_data)


    return render(request,'stark/list_view.html',locals())

模板層以下

image

對於Book表,是自定義展現配置類,展現以下圖

image

2.1.1.2 基於默認類的數據展現

視圖層以及模板層如上,不過須要注意的是

image

沒有加逗號,django會默認認爲是一個字符串,並不會以一個元祖來讀取元素,最後致使報錯

對於出版社展現以下,只會展現對象名,顯示名稱是因爲模型類中有__str__方法

image

2.1.2 多對多數據的判斷

在app01中添加展現列表中添加做者這個多對多字段

2.1.2.1 錯誤顯示

添加多對多字段

image

模板展現

image

最後展現中,做者多對多數據顯示爲none,

2.1.2.2 多對多數據判斷以及自定義錯誤報錯

(1)知識補充:

使用名字訪問一個model的實例:模型名._meta.get_field("publish")

展現模型類的的名字:模型名._meta.model_name

展現模型類對應的項目名稱:模型名._meta.app_label

展現一個模型類對象的默認名稱:模型對象.verbose_name

對於模型類中的參數,若是設置了verbose.name則會顯示設置的名字,無則顯示參數名稱

(2)多對多的判斷

對於多對多的判斷,首先是導包,對於 list_display來講,普通屬性從self.list_display拿到的是字符串,一對多和多對多則是拿到的對象,拿到對象的類型判斷是不是多對多,若是是多對多則報錯並報錯

image

對於多對多來講,須要提示爲多對多,須要使用自定義列,彈出錯誤,用於提示錯誤

image

2.1.3 模型類中choice數據的反射

數據的反射,例如book的出版狀態只有已出版以及未出版,但在數據庫中記錄只有1和2,須要對1和2的內容取出並反射出具體的內容

注:這裏主要使用的是」get_屬性名_display方法」,這樣最後在頁面中展現就會變成以及出版或者是未出版,不在是數據庫中1和2

2.1.4 模型類屬性中的__str__處理方法

field_obj = self.model._meta.get_field(field_or_func) #獲取模型對象
這句話,獲取的是模型類中的方法,因爲模型類中沒有__str__方法,因此須要對其進行處理

2.1.5 自定義的展現多對多內容

2.1.5.1 APP內對stark進行自定義列設定

自定義函數,例如show_author,而後把函數名丟到list_display列表中

stites中獲取返回值,而後加入到列表中傳到前端

注:因爲list_display中有字符串也有函數,因此須要用到callable來判斷是都爲函數名

這裏的self 是類函數調用的方式.,由於原來類中須要穿參,如今增長一個參數而已,什麼都行.可是最好是self

2.1.5.2 處理多對多內容

在callable 內容中傳入obj對象,方便操做數據

在自定製配置類中,經過obj 獲取對應的做者信息

2.2 表頭數據的展現

2.2.1 默認配置類

非定製列 ,即只有 __str__,只須要返回到數據表名的大寫便可

2.2.2 自定製配置類

若是是普通屬性,則只須要丟到對應的head_list列表中便可,對於自定義列,傳入爲函數名的時候則須要對傳入作判斷

app中註冊類書寫

2.3 添加選擇框,編輯以及刪除到全部的展現頁面中

2.3.1 在html中展現指定的內容

導入mark_safe包

from django.utils.safestring import mark_safe

能夠是後臺傳入的標籤內容不會被轉化,直接成爲前端代碼

2.3.2 反向解析

導入包

from django.urls import reverse

定義類名以及表名

def __init__(self,model):

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

視圖層反向解析,設置name

@property
    def get_url(self,):
        # temp = [
        #     path("", self.list_view),
        #     path("add/",self. add_view),
        #     re_path("(\d+)/change/", self.change_view),
        #     re_path("(\d+)/delete/", self.delete_view),
        # ]
        temp = [
            path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
            path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
            re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
            re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
        ]
        return (temp, None, None)

反向解析代碼

# 反向解析當前訪問表的增刪改查URL
    def get_list_url(self):
        # 反向解析當前表的查詢的URL
        list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
        return list_url

    def get_add_url(self, obj):
        # 反向解析當前表的添加的URL
        add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
        return add_url

    def get_delete_url(self, obj):
        # 反向解析當前表的刪除的URL
        delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
        return delete_url

    def get_change_url(self, obj):
        # 反向解析當前表的修改的URL
        change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
        return change_url

分析:反向解析名字爲app名加表名,利用的是無名分組,注意無名分組爲元祖傳參,最後是三個默認列代碼

# 三個默認列
    # 選擇框
    def show_checkbox(self, obj=None, heade=False):
        if heade:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

        # 刪除框

    def show_delbtn(self, obj=None, heade=False):
        if heade:
            return '刪除'
        # return mark_safe("<a href='stark/app01/book/%s/delete'>刪除</a>" % obj.pk)
        return mark_safe("<a href='%s'>刪除</a>" % self.get_delete_url(obj))
        # 編輯框

    def show_editbtn(self, obj=None, heade=False):
        if heade:
            return '編輯'
        # return mark_safe("<a href='stark/app01/book/%s/change'>編輯</a>" % obj.pk)
        return mark_safe("<a href='%s'>編輯</a>" % self.get_change_url(obj))

####同時構建新的list_display,若是須要在默認列表中都展現,須要設定新的list_display

    #  構建新的list_display

    def get_new_list_display(self):
        temp = []
        temp.extend(self.list_display)
        temp.append(ModelStark.show_editbtn)
        temp.append(ModelStark.show_delbtn)
        temp.insert(0, ModelStark.show_checkbox)

        return temp

屬性說明:

  • self 爲當前操做的模型配置類
  • obj=None 讓默認對象的值爲None,即當獲取表頭的時候不用傳值
  • header=False  讓默認的header 爲False ,使調用數據的時候不用傳值,不返回表頭,只返回數據

獲取表頭中,是header =true 這樣能夠獲取表頭數據內容

完整site代碼以下

from django.contrib import admin
from django.urls import path,re_path
from django.shortcuts import render,HttpResponse
from django.utils.safestring import mark_safe
from django.urls import reverse

class ModelStark(object):

    list_display =("__str__",)

    def __init__(self,model):

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

    # 反向解析當前訪問表的增刪改查URL
    def get_list_url(self):
        # 反向解析當前表的查詢的URL
        list_url = reverse("%s_%s_list" % (self.app_label, self.model_name))
        return list_url

    def get_add_url(self, obj):
        # 反向解析當前表的添加的URL
        add_url = reverse("%s_%s_delete" % (self.app_label, self.model_name))
        return add_url

    def get_delete_url(self, obj):
        # 反向解析當前表的刪除的URL
        delete_url = reverse("%s_%s_delete" % (self.app_label, self.model_name), args=(obj.pk,))
        return delete_url

    def get_change_url(self, obj):
        # 反向解析當前表的修改的URL
        change_url = reverse("%s_%s_change" % (self.app_label, self.model_name), args=(obj.pk,))
        return change_url

    # 三個默認列
    # 選擇框
    def show_checkbox(self, obj=None, heade=False):
        if heade:
            return mark_safe("<input type='checkbox'>")
        return mark_safe("<input type='checkbox'>")

        # 刪除框

    def show_delbtn(self, obj=None, heade=False):
        if heade:
            return '刪除'
        # return mark_safe("<a href='stark/app01/book/%s/delete'>刪除</a>" % obj.pk)
        return mark_safe("<a href='%s'>刪除</a>" % self.get_delete_url(obj))
        # 編輯框

    def show_editbtn(self, obj=None, heade=False):
        if heade:
            return '編輯'
        # return mark_safe("<a href='stark/app01/book/%s/change'>編輯</a>" % obj.pk)
        return mark_safe("<a href='%s'>編輯</a>" % self.get_change_url(obj))
    #  構建新的list_display

    def get_new_list_display(self):
        temp = []
        temp.extend(self.list_display)
        temp.append(ModelStark.show_editbtn)
        temp.append(ModelStark.show_delbtn)
        temp.insert(0, ModelStark.show_checkbox)

        return temp

    # 視圖函數
    def list_view(self, request):
        """
        self:當前配置類
        selfmodel:當前訪問的表數據
        :param request:
        :return:
        """
        # 當前訪問表的數據
        querset = self.model.objects.all()
        print(querset)

        print(self.list_display)

        #用於展現頭部文件
        header_list=[]
        for field_or_info in self.get_new_list_display():
            #判斷是函數名或者是字符段
            if callable(field_or_info):
                vim=field_or_info(self,heade=True)
                header_list.append(vim)
            else:
                # 獲取指定字段的對象屬性,並拿出verbose_name屬性
                if field_or_info=='__str__':
                    #若是隻有默認裝飾類,只有__str__,則拿出他的表名做爲頭
                    vim = self.model._meta.model_name.upper()
                else:
                    file_obj =self.model._meta.get_field(field_or_info)
                    vim = file_obj.verbose_name
                header_list.append(vim)



        #用於構建視圖中的展現數據
        new_data=[]
        for obj in querset:
            temp=[]
            for field_or_info in self.get_new_list_display():
                # 判斷是函數仍是字符段
                if callable(field_or_info):
                    vim = field_or_info(self,obj)
                else:
                    try:
                        from django.db.models.fields.related import ManyToManyField
                        info_obj=self.model._meta.get_field(field_or_info)
                        # 判斷多對多字段
                        if type(info_obj)==ManyToManyField:
                            raise Exception("list_distplay 不能是多很少字段")
                        #判斷是不是__str__
                        # if field_or_info=='__str':
                        #     vim=getattr(obj,field_or_info)()
                        # 判斷是否有choices字段
                        if info_obj.choices:
                            vim = getattr(obj,'get_%s_display'%field_or_info)()
                        else:
                            vim =getattr(obj,field_or_info)
                    except Exception as e:
                        vim = getattr(obj, field_or_info)()
                temp.append(vim)
            new_data.append(temp)
        print('new_data',new_data)

        # 目標數據
        # new_data=[
        #     ["python",123],
        #     ["java",234]
        # ]


        return render(request,'stark/list_view.html',locals())


    def add_view(self, request):
        return HttpResponse("add_view")

    def change_view(self, request, id):
        return HttpResponse("change_view")

    def delete_view(self, request, id):
        return HttpResponse("delete_view")

    @property
    def get_url(self,):
        # temp = [
        #     path("", self.list_view),
        #     path("add/",self. add_view),
        #     re_path("(\d+)/change/", self.change_view),
        #     re_path("(\d+)/delete/", self.delete_view),
        # ]
        temp = [
            path("", self.list_view, name="%s_%s_list" % (self.app_label, self.model_name)),
            path("add/", self.add_view, name="%s_%s_add" % (self.app_label, self.model_name)),
            re_path("(\d+)/change/", self.change_view, name="%s_%s_change" % (self.app_label, self.model_name)),
            re_path("(\d+)/delete/", self.delete_view, name="%s_%s_delete" % (self.app_label, self.model_name)),
        ]
        return (temp, None, None)


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

    def register(self,model,admin_class=None,**options):
        admin_class = admin_class or ModelStark
        self._registry[model]=admin_class(model)

    def get_urls(self):
        temp =[]

        # 拿到已經註冊的全部表
        for model,config_obj in self._registry.items():
            # 表名
            model_name = model._meta.model_name
            # 項目名
            model_label = model._meta.app_label
            temp.append(
                path("%s/%s/"%(model_label,model_name),config_obj.get_url)
            )
        return temp
    @property
    def urls(self):
        return self.get_urls(),None,None


site = StarkSite()
site代碼

完整註冊stark代碼以下

from stark.services.sites import site,ModelStark
from .models import *
from django.utils.safestring import mark_safe

class BookConfig(ModelStark):

    def show_authors(self,obj=None,heade=False):
        if heade:
            return "做者信息"

        return " ".join([author.name for author in obj.author.all()])
    # # 選擇框
    # def show_checkbox(self,obj=None,heade=False):
    #     if heade:
    #         return mark_safe("<input type='checkbox'>")
    #     return mark_safe("<input type='checkbox'>")
    #
    # # 刪除框
    # def show_delbtn(self, obj=None, heade=False):
    #     if heade:
    #         return '刪除'
    #     return mark_safe("<a href='stark/app01/book/%s/delete'>刪除</a>"%obj.pk)
    #
    # # 編輯框
    # def show_editbtn(self, obj=None, heade=False):
    #     if heade:
    #         return '編輯'
    #     return mark_safe("<a href='stark/app01/book/%s/change'>編輯</a>" % obj.pk)

    list_display=["title","price","staes","publish",show_authors]
    # list_display=["title","price","staes","publish"]


site.register(Book,BookConfig)
site.register(Publish)
print(site._registry)
stark代碼

 

a

相關文章
相關標籤/搜索