博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
自定义admin组件
阅读量:5231 次
发布时间:2019-06-14

本文共 78523 字,大约阅读时间需要 261 分钟。

配置路由

1 新建一个项目, 创建一个app01和stark应用,stark创建一个service包,并在service下创建stark.py。然后注册app

 

2 仿照site.py的注册代码,写stark.py代码:

class ModelStark(object):    def __init__(self, model, site):        self.model = model        self.site = siteclass 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)site = StarkSite()
View Code

3 stark应用下的app.py代码:

from django.apps import AppConfigfrom django.utils.module_loading import autodiscover_modulesclass StarkConfig(AppConfig):    name = 'stark'    def ready(self):        autodiscover_modules('stark')
View Code

4 app01 下model.py:

class UserInfo(models.Model):    name=models.CharField(max_length=32)    age=models.IntegerField()    def __str__(self):        return self.nameclass Book(models.Model):    title=models.CharField(max_length=32)    def __str__(self):        return self.title
View Code

5 在app01下stark.py的注册模型:

from stark.service.stark import site, ModelStarkfrom .models import *site.register(Book)site.register(UserInfo)print("_registry", site._registry)
View Code

6 在项目的urls.py写路由。

from django.conf.urls import urlfrom django.contrib import adminfrom stark.service.stark import siteurlpatterns = [    url(r'^admin/', admin.site.urls),    url(r'^stark/', site.urls),]
View Code

7 在service下stark.py写整套urls路由

from django.conf.urls import urlfrom django.shortcuts import HttpResponse,renderclass ModelStark(object):    def __init__(self, model, site):        self.model = model        self.site = siteclass 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 add(self, request):        return HttpResponse("add")    def delete(self, request, id):        return HttpResponse("delete")    def change(self, request, id):        return HttpResponse("change")    def list_view(self, request):        return HttpResponse("list_view")    def get_urls2(self):        temp = []        # 添加每个app/model的增删改查url        temp.append(url(r'^add/', self.add))        temp.append(url(r'^(\d+)/delete/', self.delete))        temp.append(url(r'^(\d+)/change/', self.change))        temp.append(url(r'^$', self.list_view))        return temp    @property    def urls2(self):        return self.get_urls2(), None, None    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            # 添加路由            # url(r'app01/user/',)            temp.append(url(r'^%s/%s/' % (app_label, model_name), self.urls2))        return temp    @property    def urls(self):        return self.get_urls(), None, Nonesite = StarkSite()
View Code

此时运行项目,就会有stark开头的8条路由。但是每个模型的增删改查的返回数据一样,我们要做到根据不同的app和model返回对应的数据,因此要把增删改查的路由重新划分。

8 在service下stark.py修改urls路由,此时的代码:

from django.conf.urls import urlfrom django.shortcuts import HttpResponse,renderclass ModelStark(object):    def __init__(self, model, site):        self.model = model        self.site = site    def add(self, request):        return HttpResponse("add")    def delete(self, request, id):        return HttpResponse("delete")    def change(self, request, id):        return HttpResponse("change")    def list_view(self, request):        return HttpResponse("list_view")    def get_urls2(self):        temp = []        # 添加每个app/model的增删改查url        temp.append(url(r'^add/', self.add))        temp.append(url(r'^(\d+)/delete/', self.delete))        temp.append(url(r'^(\d+)/change/', self.change))        temp.append(url(r'^$', self.list_view))        return temp    @property    def urls2(self):        return self.get_urls2(), None, Noneclass 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            # 添加路由            # url(r'app01/user/',)            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, Nonesite = StarkSite()
View Code

因为每个app和模型类的数据不一样以及各自定制的显示方式不一样,所以对于增删改查就要分开对待,因此就把增删改查放到ModelStark类中,既然四个视图函数都放到ModelStark中了,把调用他们的get_urls2也放进去,这样方便调用,其实就是把self和调用对象保持一致。get_urls2都放进去了,urls2也顺便放进去吧,正好他们是一套。

把他们放到ModelStark的目的就是根据不同的app和model以及他们在注册时定制的配置类显示对应的数据和展示方式。下面的增删改查都会在ModelStark类中进行配置,并且有一个对象会一致被调用:stark_class_obj

 假设app01 下stark.py为Book模型定制一个配置类,Userinfo不配置:

class BookConfig(ModelStark):    passsite.register(Book, BookConfig)site.register(UserInfo)

 

此时的路由算是配置好了,后面再设置反向解析,下面开始配置视图。

 

list_display

首先先看下ModelStark类中的self.model

1 向UserIfo表中,填充一些数据。并写Userinfo配置类:

class UserConfig(ModelStark):    list_display = ["name", "age"]
View Code

在service/stark.py的ModelStark类中添加代码:

class ModelStark(object):    list_display = []    .....    def list_view(self, request):        print(self.model)   # UserInfo        print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list  = []        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.list_display:      # 获取每一个要展示的字段   ["name", "age"]                val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())        ........
View Code

添加list.html文件,代码:

数据列表

{
% for data_list in new_data_list %}
{ % for data in data_list %}
{ % endfor %}
{
% endfor %}
{ { data }}
View Code

 访问/strak/app01/userinfo,此时页面就能显示数据了

2 此时想在每一列的后面放在编辑按钮。

在app01/strak.py中给添加一个方法,使每一条数据都有一个编辑按钮。

from django.utils.safestring import mark_safe     ........class UserConfig(ModelStark):    def edit(self):        user_id = obj.id        return mark_safe("编辑")    list_display = ["name", "age", edit]......
View Code

 在service/stark.py的list_view中修改代码:

def list_view(self, request):        print(self.model)   # UserInfo        print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.list_display:      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self)                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())
View Code

 此时访问/strak/app01/userinfo

3 此时每一列的后面都有一个编辑连接,但是并不能跳转到对应编辑页面,因此修改url,修改app01/strak.py中的edit方法。

def edit(self, obj):        user_id = obj.id        return mark_safe("编辑" % user_id)
View Code

 edit方法需要一个obj参数来获取用户id,在service/stark.py的list_view中给它传递,

def list_view(self, request):        print(self.model)   # UserInfo        print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.list_display:      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())
View Code

 此时访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。

4 但是这样写url地址并不是最完美的,然而这样也行,为了更加完美,那就使用反向解析。

修改service/stark.py中get_urls2:

def get_urls2(self):        temp = []       # 添加每个app/model的增删改查url        model_name = self.model._meta.model_name        app_label = self.model._meta.app_label        temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name)))        temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name)))        temp.append(url(r'^(\d+)/change/', self.change, 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
View Code

 修改app01/strak.py中的edit:

from django.core.urlresolvers import reverseclass UserConfig(ModelStark):    def edit(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.id,))        return mark_safe("编辑" % _url)
View Code

此时再访问/strak/app01/userinfo,发现每一个编辑按钮都能跳到对应的编辑页面。

5 既然编辑都完成了,那就再添加一个删除和checkbox,简直易如反掌。

修改app01/strak.py:

class UserConfig(ModelStark):    def edit(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.id,))        return mark_safe("编辑" % _url)    def deletes(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.id,))        return mark_safe("删除" % _url)    def checkbox(self, obj):        return mark_safe("")    list_display = [ checkbox, "name", "age", edit, deletes]
View Code

此时访问/strak/app01/userinfo,页面效果:

 

5 但是,如果某个模型类没有定制自己的配置类,也能展示自己的默认字段,并且也有复选框、编辑和删除功能。

把app01/strak.py中的edit、delete、checkbox三个方法全部剪切放到service/stark.py的ModelStark类中,然后把list_display改为list_display = ["__str__"]。为了保证每个模型字段和checkbox、编辑、删除的展示顺序,定义一个new_list_display方法,动态的获取所有的展示字段。具体代码:

class ModelStark(object):    list_display = ["__str__"]    def __init__(self, model, site):        self.model = model        self.site = site    def edit(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.id,))        return mark_safe("编辑" % _url)    def deletes(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.id,))        return mark_safe("删除" % _url)    def checkbox(self, obj):        """复选框"""        return mark_safe("")    def add(self, request):        return HttpResponse("add")    def delete(self, request, id):        return HttpResponse("delete")    def change(self, request, id):        return HttpResponse("change")    def new_list_display(self):        temp = []        temp.append(ModelStark.checkbox)        temp.extend(self.list_display)        temp.append(ModelStark.edit)        temp.append(ModelStark.deletes)        return temp    def list_view(self, request):        """列表展示页"""        print(self.model)   # UserInfo        print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.new_list_display():      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())    ..........
View Code

此时访问/strak/app01/userinfo和/strak/app01/book,都能展示复选框、默认字段、编辑、删除。

现在表单数据有了,但是表头还没有,那获取表头数据。如果是复选框列,也在表头发一个复选框;如果是编辑或者删除,表头就显示操作;如果是其他就显示字段名称。

6 修改service/stark.py中checkbox、edit、deletes方法,判断获取的是表头还是表单

def edit(self, obj=None, header=False):        """编辑按钮"""        if header:      # 判断是不是表头            return "操作"        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.id,))        return mark_safe("编辑" % _url)    def deletes(self, obj=None, header=False):        """删除按钮"""        if header:      # 判断是不是表头            return "操作"        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.id,))        return mark_safe("删除" % _url)    def checkbox(self, obj=None, header=False):        """复选框"""        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("")
View Code

7 修改service/stark.py中view_list方法,添加获取表头的代码;

# 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.new_list_display():       # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.model._meta.model_name.upper()   # 返回模型类的名称                else:                    val = self.model._meta.get_field(field).verbose_name    # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)
View Code

此时ModelStark类的代码:

class ModelStark(object):    list_display = ["__str__"]    def __init__(self, model, site):        self.model = model        self.site = site    def edit(self, obj=None, header=False):        """编辑按钮"""        if header:      # 判断是不是表头            return "操作"        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.id,))        return mark_safe("编辑" % _url)    def deletes(self, obj=None, header=False):        """删除按钮"""        if header:      # 判断是不是表头            return "操作"        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.id,))        return mark_safe("删除" % _url)    def checkbox(self, obj=None, header=False):        """复选框"""        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("")    def add(self, request):        return HttpResponse("add")    def delete(self, request, id):        return HttpResponse("delete")    def change(self, request, id):        return HttpResponse("change")    def new_list_display(self):        temp = []        temp.append(ModelStark.checkbox)        temp.extend(self.list_display)        temp.append(ModelStark.edit)        temp.append(ModelStark.deletes)        return temp    def list_view(self, request):        """列表展示页"""        print(self.model)   # UserInfo        print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.new_list_display():       # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.model._meta.model_name.upper()   # 返回模型类的名称                else:                    val = self.model._meta.get_field(field).verbose_name    # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.new_list_display():      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())    def get_urls2(self):        temp = []       # 添加每个app/model的增删改查url        model_name = self.model._meta.model_name        app_label = self.model._meta.app_label        temp.append(url(r'^add/', self.add, name="%s_%s_add" % (app_label, model_name)))        temp.append(url(r'^(\d+)/delete/', self.delete, name="%s_%s_delete" % (app_label, model_name)))        temp.append(url(r'^(\d+)/change/', self.change, 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
View Code

8 修改list.html的代码,并添加复选框的点击事件:

数据列表

{ % for head_name in head_list %}
{ % endfor %}
{
% for data_list in new_data_list %}
{ % for data in data_list %}
{ % endfor %}
{
% endfor %}
{ { head_name }}
{ { data }}
View Code

此时访问/strak/app01/userinfo和/strak/app01/book,表头和表单都有数据了。

 list_display_links

首先判断模型类有没有配置list_display_links,如果没有就显示编辑列,如果指定了可点击的字段,那就把这个字段变成可点击的a标签,再把编辑列去掉。

 在ModelStart类中,添加类属性list_display_links=[],然后修改new_list_display方法,

1 修改list_viwe中获取表单数据的代码,ModelStart类的部分代码:

class ModelStark(object):    list_display = ["__str__"]    list_display_links = []    ......    def new_list_display(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    def list_view(self, request):        """列表展示页"""        # print(self.model)   # UserInfo        # print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.new_list_display():       # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.model._meta.model_name.upper()   # 返回模型类的名称                else:                    val = self.model._meta.get_field(field).verbose_name    # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        # print(ModelStark.list_display_links)        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.new_list_display():      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                    if field in self.list_display_links:     # 判断字段是否在list_display_links中,                        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.id,))                        val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())    ......
View Code

2 在app01/strak.py中修改Userinfo的配置类:

class UserConfig(ModelStark):    list_display = ["name", "age"]    list_display_links = ["name"]
View Code

 访问/strak/app01/userinfo,

 

 效果有了,但是下面的代码在很多地方重复使用:

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.id,))                        val = mark_safe("%s" % (_url, val))
View Code

3 这些代码都是在获取url,因此直接封装四个获取url的方法:get_change_url,get_delete_url,get_add_url,get_list_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, obj):        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
View Code

4 修改edit、deletes、checkbox的内部代码:

"""编辑按钮"""    def edit(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_change_url(obj)        return mark_safe("编辑" % _url)    """删除按钮"""    def deletes(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_delete_url(obj)        return mark_safe("删除" % _url)    """复选框"""    def checkbox(self, obj=None, header=False):        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("")
View Code

5 修改list_view的获取list_display_links的字段的部分代码,list_view的代码:

"""列表展示页"""    def list_view(self, request):        # print(self.model)   # UserInfo        # print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.new_list_display():       # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.model._meta.model_name.upper()   # 返回模型类的名称                else:                    val = self.model._meta.get_field(field).verbose_name    # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        # print(ModelStark.list_display_links)        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.new_list_display():      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                    if field in self.list_display_links:     # 判断字段是否在list_display_links中,                        _url = self.get_change_url(obj)                        val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())
View Code

6 同时整理下方法的命名,此时ModelStark类的全部代码:

class ModelStark(object):    list_display = ["__str__"]    list_display_links = []    def __init__(self, model, site):        self.model = model        self.site = site    """编辑按钮"""    def edit(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_change_url(obj)        return mark_safe("编辑" % _url)    """删除按钮"""    def deletes(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_delete_url(obj)        return mark_safe("删除" % _url)    """复选框"""    def checkbox(self, obj=None, header=False):        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("")    """获取编辑的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, obj):        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 new_list_display(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    def add_view(self, request):        return HttpResponse("add")    def delete_view(self, request, id):        return HttpResponse("delete")    def change_view(self, request, id):        return HttpResponse("change")    """列表展示页"""    def list_view(self, request):        # print(self.model)   # UserInfo        # print(self.list_display)    # ["name", "age"]        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.new_list_display():       # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.model._meta.model_name.upper()   # 返回模型类的名称                else:                    val = self.model._meta.get_field(field).verbose_name    # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        # print(ModelStark.list_display_links)        for obj in data_list:     # 获取data_list中的每一个对象            temp = []       # 定义一个内层列表,存储一个对象所有字段的值            for field in self.new_list_display():      # 获取每一个要展示的字段   ["name", "age"]                if callable(field):             # 判断字段是否可被调用                    val = field(self, obj)      # 给自定义方法传递参数                else:                    val = getattr(obj, field)   # field是字符串,利用反射获取对象每个字段的值,                    if field in self.list_display_links:     # 判断字段是否在list_display_links中,                        _url = self.get_change_url(obj)                        val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return render(request, 'list.html', locals())    def get_urls2(self):        temp = []       # 添加每个app/model的增删改查url        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
View Code

 

增加

现在查的页面已经有了,把增删改的功能也做了。先做增加的。为了表单的复杂,把app01的model.py的模型类都删了,把stark.py里面的注册代码和配置类代码也删了。

1 把下面的模型类代码放到app01的model.py里面,然后执行迁移。

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.nameclass 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.telephoneclass 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.nameclass 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
View Code

2 app01的stark.py里面的代码:

from stark.service.stark import site, ModelStarkfrom .models import *class BookConfig(ModelStark):    list_display = ["title", "price", "publishDate"]site.register(Author)site.register(Publish)site.register(AuthorDetail)site.register(Book,BookConfig)
View Code

在list.html里面添加一个跳转到添加数据页面的连接或按钮,并且在list_view里把添加的url传到list.html。

3 使用ModelForm来做表单的处理。ModelStark类中的add_view方法:

def add_view(self, request):        class ModelFormDemo(ModelForm):            class Meta:                model = self.model                fields = "__all__"        form_obj = ModelFormDemo()        return render(request, 'add_view.html', locals())
View Code

4 添加add_view.html文件,代码:

添加数据

{
% csrf_token %} {
% for field in form_obj %}
{
{ field }}
{ { field.errors.0 }}
{
% endfor %}
View Code
 

此时访问添加页面,效果

如果想让字段显示中文怎么办。在add_view的ModelFormDemo里面加label? 但是我们并不知道此时访问的是那张数据表,所以不能写死。。那怎么办?让用户自己定义,用户未定义就用默认的。

5 在ModelStark类中定义一个类属性:model_class = None。 定义一个获取用户定义的ModelFormDemo类的方法。然后修改add_view方法。

class ModelStark(object):    list_display = ["__str__"]    list_display_links = []    model_class = None    .........    # 获取定义的ModelFormDemo类    def get_modelform_class(self):        if not self.model_class:    # 如果用户为定义,返回默认的ModelFormDemo类名            class ModelFormDemo(ModelForm):                class Meta:                    model = self.model                    fields = "__all__"            return ModelFormDemo        else:   # 返回用户定义的ModelFormDemo类名            return self.model_class    # 添加视图    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        form_obj = ModelFormDemo()        return render(request, 'add_view.html', locals())      .......
View Code

6 现在去app01下的stark.py中定制一个ModelForm类:

from django.forms import ModelFormclass ModelFormDemo(ModelForm):    class Meta:        model = Book        fields = "__all__"        labels = {            "title": "书籍名称",            "price": "价格"        }class BookConfig(ModelStark):    list_display = ["title", "price", "publishDate"]    model_class = ModelFormDemo      .......
View Code

此时去页面访问,

 

OK,现在就做post请求。

7 add_view.py

def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        if request.method == "POST":            form_obj = ModelFormDemo(request.POST)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'add_view.html', locals())        form_obj = ModelFormDemo()        return render(request, 'add_view.html', locals())
View Code

现在就可以去页面添加数据了。如果进入添加页面时报错没有__str__字段, 在模型类的 __str__方法中将返回值强转str就好了。

ok添加做好了,接下来编辑。

 

编辑

 添加和编辑使用的表单一样,因此两个页面都导入表单的html代码。创建form.html。

1 form.html:

{
% csrf_token %} {
% for field in form_obj %}
{
{ field }}
{ { field.errors.0 }}
{
% endfor %}
View Code

2 add_view.html

添加数据

{
% include 'form.html' %}
View Code

3 edit.html

修改数据

{
% include 'form.html' %}
View Code

4 ok,页面完成了,写编辑的视图函数。修改change_view方法:

def change_view(self, request, id):        ModelFormDemo = self.get_modelform_class()  # 取到的是类名        edit_obj = self.model.objects.get(pk=id)        if request.method == "POST":            form_obj = ModelFormDemo(request.POST, instance=edit_obj)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'edit_view.html', locals())        form_obj = ModelFormDemo(instance=edit_obj)        return render(request, 'edit_view.html', locals())
View Code

 现在就可以去页面修改数据了。修改做好了,接下来删除。

 

删除

1 创建delete_view.html

{
% csrf_token %}
取消
View Code

2 修改delete_view方法

def delete_view(self, request, id):        list_url = self.get_list_url()        if request.method == "POST":            self.model.objects.get(pk=id).delete()            return redirect(list_url)        return render(request, 'delete_view.html', locals())
View Code

增删改现在算是大功告成。下面继续查询,因为admin的查询姿势有很多,所以如果继续在list_view方法里写代码会显得比较乱,因此把查询封装在一个方法里面。

3 定义ShowList类:

class ShowList(object):    def __init__(self, config, data_list):        self.config = config        self.data_list = data_list    def show_header(self):        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.config.new_list_display():  # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self.config, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.config.model._meta.model_name.upper()  # 返回模型类的名称                else:                    val = self.config.model._meta.get_field(field).verbose_name  # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        return head_list    def show_body(self):        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        # print(ModelStark.list_display_links)        for obj in self.data_list:  # 获取data_list中的每一个对象            temp = []  # 定义一个内层列表,存储一个对象所有字段的值            for field in self.config.new_list_display():  # 获取每一个要展示的字段   ["name", "age"]                if callable(field):  # 判断字段是否可被调用                    val = field(self.config, obj)  # 给自定义方法传递参数                else:                    val = getattr(obj, field)  # field是字符串,利用反射获取对象每个字段的值,                    if field in self.config.list_display_links:  # 判断字段是否在list_display_links中,                        _url = self.config.get_change_url(obj)                        val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return new_data_list
View Code

4 修改ModelStark类的list_view,此时ModelStark类的代码:

class ModelStark(object):    list_display = ["__str__"]    list_display_links = []    model_class = None    def __init__(self, model, site):        self.model = model        self.site = site    """编辑按钮"""    def edit(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_change_url(obj)        return mark_safe("编辑" % _url)    """删除按钮"""    def deletes(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_delete_url(obj)        return mark_safe("删除" % _url)    """复选框"""    def checkbox(self, obj=None, header=False):        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("")    """获取编辑的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    # 获取被指定的所有字段    def new_list_display(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    # 获取定义的ModelFormDemo类    def get_modelform_class(self):        if not self.model_class:    # 如果用户为定义,返回默认的ModelFormDemo类名            class ModelFormDemo(ModelForm):                class Meta:                    model = self.model                    fields = "__all__"            return ModelFormDemo        else:   # 返回用户定义的ModelFormDemo类名            return self.model_class    # 添加视图    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        if request.method == "POST":            form_obj = ModelFormDemo(request.POST)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'add_view.html', locals())        form_obj = ModelFormDemo()        return render(request, 'add_view.html', locals())    def delete_view(self, request, id):        list_url = self.get_list_url()        if request.method == "POST":            self.model.objects.get(pk=id).delete()            return redirect(list_url)        return render(request, 'delete_view.html', locals())    def change_view(self, request, id):        ModelFormDemo = self.get_modelform_class()  # 取到的是类名        edit_obj = self.model.objects.get(pk=id)        if request.method == "POST":            form_obj = ModelFormDemo(request.POST, instance=edit_obj)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'edit_view.html', locals())        form_obj = ModelFormDemo(instance=edit_obj)        return render(request, 'edit_view.html', locals())    """列表展示页"""    def list_view(self, request):        # 获取userinfo 的数据        data_list = self.model.objects.all()     # ["obj1", "obj2",.....]        # 获取表头        show_list = ShowList(self, data_list)        head_list = show_list.show_header()        # 获取表体        new_data_list = show_list.show_body()        # 获取添加的url        add_url = self.get_add_url()        return render(request, 'list.html', locals())    def get_urls2(self):        temp = []       # 添加每个app/model的增删改查url        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
View Code

 

分页

1 在stark app下创建一个utils包,然后创建一个page.py,代码:

class Pagination(object):    def __init__(self, current_page, all_count, base_url,params, per_page_num=8, pager_count=11, ):        """        封装分页相关数据        :param current_page: 当前页        :param all_count:    数据库中的数据总条数        :param per_page_num: 每页显示的数据条数        :param base_url: 分页中显示的URL前缀        :param pager_count:  最多显示的页码个数        """        try:            current_page = int(current_page)        except Exception as e:            current_page = 1        if current_page < 1:            current_page = 1        self.current_page = current_page        self.all_count = all_count        self.per_page_num = per_page_num        self.base_url = base_url        # 总页码        all_pager, tmp = divmod(all_count, per_page_num)        if tmp:            all_pager += 1        self.all_pager = all_pager        self.pager_count = pager_count  # 最多显示页码数        self.pager_count_half = int((pager_count - 1) / 2)        import copy        params = copy.deepcopy(params)        params._mutable = True        self.params = params  # self.params : {"page":77,"title":"python","nid":1}    @property    def start(self):        return (self.current_page - 1) * self.per_page_num    @property    def end(self):        return self.current_page * self.per_page_num    def page_html(self):        # 如果总页码 < 11个:        if self.all_pager <= self.pager_count:            pager_start = 1            pager_end = self.all_pager + 1        # 总页码  > 11        else:            # 当前页如果<=页面上最多显示(11-1)/2个页码            if self.current_page <= self.pager_count_half:                pager_start = 1                pager_end = self.pager_count + 1            # 当前页大于5            else:                # 页码翻到最后                if (self.current_page + self.pager_count_half) > self.all_pager:                    pager_start = self.all_pager - self.pager_count + 1                    pager_end = self.all_pager + 1                else:                    pager_start = self.current_page - self.pager_count_half                    pager_end = self.current_page + self.pager_count_half + 1        page_html_list = []        self.params["page"] = 1        first_page = '
  • 首页
  • ' % (self.base_url, self.params.urlencode(),) page_html_list.append(first_page) if self.current_page <= 1: prev_page = '
  • 上一页
  • ' else: self.params["page"] = self.current_page - 1 prev_page = '
  • 上一页
  • ' % (self.base_url, self.params.urlencode(),) page_html_list.append(prev_page) for i in range(pager_start, pager_end): self.params["page"] = i if i == self.current_page: temp = '
  • %s
  • ' % (self.base_url, self.params.urlencode(), i,) else: temp = '
  • %s
  • ' % (self.base_url, self.params.urlencode(), i,) page_html_list.append(temp) if self.current_page >= self.all_pager: next_page = '
  • 下一页
  • ' else: self.params["page"] = self.current_page + 1 next_page = '
  • 下一页
  • ' % (self.base_url, self.params.urlencode(),) page_html_list.append(next_page) self.params["page"] = self.all_pager last_page = '
  • 尾页
  • ' % (self.base_url, self.params.urlencode(),) page_html_list.append(last_page) return ''.join(page_html_list)
    View Code

    2 在ShowList类中生成分页对象和每一页的数据。

    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_url = self.request.path    # 获取url(不带参数)        # 生成分页对象        self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=1, pager_count=11)        # 当前页的数据列表        self.page_data = self.data_list[self.paginator.start: self.paginator.end]            。。。。。。。    #  list_view传入request
    View Code

    3 然后修改get_body方法中的代码:

    4 修改list_view.html,插入页码列表

    添加数据
    { % for head_name in head_list %}
    { % endfor %}
    {
    % for data_list in new_data_list %}
    { % for data in data_list %}
    { % endfor %}
    {
    % endfor %}
    { { head_name }}
    { { data }}
    View Code

     

    Search

     1 ModelStark类中添加类属性search_fields = [],然后添加一个get_search方法,根据search关键字进行模糊查询。

    def get_search(self, request):        key_word = request.GET.get("q", "")        search_connection = Q()        if key_word:            search_connection.connector = 'or'            for field in self.search_fields:                search_connection.children.append((field+"__contains", key_word))        return key_word, search_connection
    View Code

    2 修改list_view方法的代码 

    def list_view(self, request):        # 获取search的key_word,Q对象        key_word, search_connection = self.get_search(request)        # 获取userinfo 的数据,并进行search过滤        data_list = self.model.objects.all().filter(search_connection)        # 获取表头        show_list = ShowList(self, data_list, request)        head_list = show_list.show_header()        # 获取表体        new_data_list = show_list.show_body()        # 获取添加的url        add_url = self.get_add_url()        return render(request, 'list.html', locals())
    View Code

    3 修改list_view.html

    添加数据
    {
    % if show_list.config.search_fields %}
    {
    % endif %}
    { % for head_name in head_list %}
    { % endfor %}
    {
    % for data_list in new_data_list %}
    { % for data in data_list %}
    { % endfor %}
    {
    % endfor %}
    { { head_name }}
    { { data }}
    View Code

    在app01的stark.py中定义了search_fields后,就能根据设定的字段进行search查询了。 

     

    action

     1 在ModelStark类中添加类属性actions = [],在ShowList类中定义get_actions_list方法,获取用户定制的所有action操作。

    ...... class ShowList(object):    def __init__(self, config, data_list, request):         ........# actions        self.actions = self.config.actions    # 获取action操作    def get_actions_list(self):        temp = []        for action in self.actions:            temp.append({                "name": action.__name__,                "desc": action.short_description            })        return temp.......
    View Code

    2 修改ModelStark类中的checkbox方法:

    """复选框"""    def checkbox(self, obj=None, header=False):        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("" % obj.pk)
    View Code

    3 修改list_view.html,添加下拉框

    添加数据
    {
    % if show_list.config.search_fields %}
    {
    % endif %}
    {
    % csrf_token %}
    { % for head_name in head_list %}
    { % endfor %}
    { % for data_list in new_data_list %}
    { % for data in data_list %}
    { % endfor %}
    { % endfor %}
    { { head_name }}
    { { data }}
    View Code

    4 在app01 stark.py中的BookConfig类中定义一个修改价格的action

    class BookConfig(ModelStark):    list_display = ["title", "price", "publishDate"]    model_class = ModelFormDemo    search_fields = ["title", "price"]    def edit_price_action(self, request, queryset):        queryset.update(price=111)    edit_price_action.short_description = "修改价格"    actions = [edit_price_action]
    View Code

    5 修改ModelStark类中list_view方法。

    def list_view(self, request):        if request.method == "POST":            action_name = request.POST.get("action")      # 获取执行的action名称            id_list = request.POST.getlist("selected_id")   # 获取被选中的id            action_func = getattr(self, action_name)    # 反射获取函数            queryset = self.model.objects.filter(pk__in=id_list)    # 过滤被选中的查询集            action_func(request, queryset)   # 执行action            return redirect(self.get_list_url())            。。。。。。。
    View Code

    ok,现在就能批量的修改书籍价格。

    然而admin的action有一个默认的批量删除,so,下面添加这个功能。

    1 在ModelStark类中添加一个delete_action方法

    def delete_action(self, request, queryset):        queryset.delete()    delete_action.short_description = "批量删除"
    View Code

    2 然后再定义一个new_action方法

    def new_actions(self):        temp = []        temp.append(ModelStark.delete_action)        temp.extend(self.actions)        return temp
    View Code

    3 然后修改ShowList的__init__中self.action

    self.actions = self.config.new_actions()
    View Code

    现在就能批量删除了。此时ModelStark类的代码:

    class ModelStark(object):    list_display = ["__str__"]    list_display_links = []    model_class = None    search_fields = []    actions = []    filter_fields = []    def __init__(self, model, site):        self.model = model        self.site = site    # 批量删除    def delete_action(self, request, queryset):        queryset.delete()    delete_action.short_description = "批量删除"    """编辑按钮"""    def edit(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_change_url(obj)        return mark_safe("编辑" % _url)    """删除按钮"""    def deletes(self, obj=None, header=False):        if header:      # 判断是不是表头            return "操作"        _url = self.get_delete_url(obj)        return mark_safe("删除" % _url)    """复选框"""    def checkbox(self, obj=None, header=False):        if header:      # 判断是不是表头            return mark_safe("")        return mark_safe("" % obj.pk)    """获取编辑的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    # 获取所有的action    def new_actions(self):        temp = []        temp.append(ModelStark.delete_action)        temp.extend(self.actions)        return temp    # 获取被指定的所有字段    def new_list_display(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    # 获取定义的ModelFormDemo类    def get_modelform_class(self):        if not self.model_class:    # 如果用户为定义,返回默认的ModelFormDemo类名            class ModelFormDemo(ModelForm):                class Meta:                    model = self.model                    fields = "__all__"            return ModelFormDemo        else:   # 返回用户定义的ModelFormDemo类名            return self.model_class    # 添加视图    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        if request.method == "POST":            form_obj = ModelFormDemo(request.POST)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'add_view.html', locals())        form_obj = ModelFormDemo()        print(form_obj)        return render(request, 'add_view.html', locals())    # 删除视图    def delete_view(self, request, id):        list_url = self.get_list_url()        if request.method == "POST":            self.model.objects.get(pk=id).delete()            return redirect(list_url)        return render(request, 'delete_view.html', locals())    # 编辑视图    def change_view(self, request, id):        ModelFormDemo = self.get_modelform_class()  # 取到的是类名        edit_obj = self.model.objects.get(pk=id)        if request.method == "POST":            form_obj = ModelFormDemo(request.POST, instance=edit_obj)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())            else:                return render(request, 'edit_view.html', locals())        form_obj = ModelFormDemo(instance=edit_obj)        return render(request, 'edit_view.html', locals())    # 获取search关键字和search字段    def get_search(self, request):        key_word = request.GET.get("q", "")        search_connection = Q()        if key_word:            key_word = key_word.strip()            search_connection.connector = 'or'            for field in self.search_fields:                search_connection.children.append((field+"__contains", key_word))        return key_word, search_connection    """列表展示页"""    def list_view(self, request):        if request.method == "POST":            action_name = request.POST.get("action")      # 获取执行的action名称            id_list = request.POST.getlist("selected_id")   # 获取被选中的id            action_func = getattr(self, action_name)    # 反射获取函数            queryset = self.model.objects.filter(pk__in=id_list)    # 过滤被选中的查询集            action_func(request, queryset)   # 执行action            return redirect(self.get_list_url())        # 获取search的key_word,Q对象        key_word, search_connection = self.get_search(request)        # 获取userinfo 的数据,并进行search过滤        data_list = self.model.objects.all().filter(search_connection)        # 获取表头        show_list = ShowList(self, data_list, request)        head_list = show_list.show_header()        # 获取表体        new_data_list = show_list.show_body()        # 获取添加的url        add_url = self.get_add_url()        return render(request, 'list.html', locals())    def get_urls2(self):        temp = []       # 添加每个app/model的增删改查url        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
    View Code

    ShowList类的代码;

    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_url = self.request.path    # 获取url(不带参数)        # 生成分页对象        self.paginator = Pagination(current_page, data_count, base_url, self.request.GET, per_page_num=2, pager_count=11)        # 当前页的数据列表        self.page_data = self.data_list[self.paginator.start: self.paginator.end]        # actions        self.actions = self.config.new_actions()    # 获取action操作    def get_actions_list(self):        temp = []        for action in self.actions:            temp.append({                "name": action.__name__,                "desc": action.short_description            })        return temp    def show_header(self):        # 获取表头信息        # 定义一个列表,格式:["复选框", name , age, "操作"....]        head_list = []        for field in self.config.new_list_display():  # [checkbox,__str__, name,age,edit,deletes......]            if callable(field):                val = field(self.config, header=True)                head_list.append(val)            else:                if field == '__str__':                    val = self.config.model._meta.model_name.upper()  # 返回模型类的名称                else:                    val = self.config.model._meta.get_field(field).verbose_name  # 获取字段的verbose_name,不存在就返回Model勒种定义的field名称                head_list.append(val)        return head_list    def show_body(self):        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in self.page_data:  # 获取data_list中的每一个对象            temp = []  # 定义一个内层列表,存储一个对象所有字段的值            for field in self.config.new_list_display():  # 获取每一个要展示的字段   ["name", "age"]                if callable(field):  # 判断字段是否可被调用                    val = field(self.config, obj)  # 给自定义方法传递参数                else:                    val = getattr(obj, field)  # field是字符串,利用反射获取对象每个字段的值,                    if field in self.config.list_display_links:  # 判断字段是否在list_display_links中,                        _url = self.config.get_change_url(obj)                        val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return new_data_list
    View Code

     

    filter_fields

    1 在ModelStark类中定义一个类属性filter_fields=[],然后再ShowList类中定义一个get_filter_linktags方法,代码:

    2 app01 stark.py的BookConfig类中定义过滤字段

    class BookConfig(ModelStark):    list_display = ["title", "price", "publishDate"]    model_class = ModelFormDemo    search_fields = ["title", "price"]    def edit_price_action(self, request, queryset):        queryset.update(price=111)    edit_price_action.short_description = "修改价格"    actions = [edit_price_action]    filter_fields = ["authors", "publish"]     # 先用多对多和一对多的字段
    View Code

    3 修改list_view.html代码,加一个过滤布局并传数据:

    添加数据
    {
    % if show_list.config.search_fields %}
    {
    % endif %}
    {
    % csrf_token %}
    { % for head_name in head_list %}
    { % endfor %}
    { % for data_list in new_data_list %}
    { % for data in data_list %}
    { % endfor %}
    { % endfor %}
    { { head_name }}
    { { data }}

    Filter

    {
    % for field, link_tag in show_list.get_filter_linktags.items %}

    { { field|upper }}

    {
    % for tag in link_tag %}

    { { tag|safe }}

    {
    % endfor %}
    {
    % endfor %}
    View Code

    此时就能显示要过滤的所有字段和对应的数据,而且连接也拼接无误。现在改下a标签的样式。

    4 修改get_filter_linktags方法:

    5 给list_view.html中的a标签加样式

    View Code

    6 现在给每个过滤的字段都加一个all标签。

    修改get_filter_linktags方法

    View Code

    OK 样式有了,url也有了,进行数据过滤。

    7 在ModelStark类中添加get_filter_data方法,并修改list_view方法:

    # 过滤数据的查询条件    def get_filter_data(self, request):        filter_condition = Q()        for field, pk in request.GET.items():            if field in self.filter_fields:                filter_condition.children.append((field, pk))        return filter_condition    """列表展示页"""    def list_view(self, request):        if request.method == "POST":            action_name = request.POST.get("action")      # 获取执行的action名称            id_list = request.POST.getlist("selected_id")   # 获取被选中的id            action_func = getattr(self, action_name)    # 反射获取函数            queryset = self.model.objects.filter(pk__in=id_list)    # 过滤被选中的查询集            action_func(request, queryset)   # 执行action            return redirect(self.get_list_url())        # 获取search的key_word,Q对象        key_word, search_connection = self.get_search(request)        # 过滤        filter_connection = self.get_filter_data(request)        # 获取userinfo 的数据,并进行search过滤        data_list = self.model.objects.all().filter(search_connection).filter(filter_connection)        # 获取表头        show_list = ShowList(self, data_list, request)        head_list = show_list.show_header()        # 获取表体        new_data_list = show_list.show_body()        # 获取添加的url        add_url = self.get_add_url()        return render(request, 'list.html', locals())
    View Code

    8 现在就能进行正常的过滤了,只不过现在的能过滤的字段只能是一对多或者多对多。下面处理普通字段的过滤。

    修改get_filter_linktags方法

    def get_filter_linktags(self):        link_dict = {}         # 定义字段对应的a连接    {"book":["金平..", ""], ...}        for filter_field in self.config.filter_fields:  # 获取要过滤的字段 ["book", "author",..... ]            url_params = copy.deepcopy(self.request.GET)  # 获取参数            current_field_id = self.request.GET.get(filter_field, 0)   # 获取当前被选中的字段的id            filter_field_obj = self.config.model._meta.get_field(filter_field)      # 获取字段对象            if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):   # 如果字段对象是一对多或者多对多                data_list = filter_field_obj.rel.to.objects.all()    # 根据字段对象获取该模型类的queryset对象["book1","book2",...]            else:                data_list = self.config.model.objects.all().values("pk", filter_field)     # 取普通字段的pk和该字段的所有数据            temp = []   # 定义一个临时列表            # all标签            if url_params.get(filter_field):    # if GET请求参数中包含当前循环的字段,就把这个参数(字段)删除                del url_params[filter_field]                temp.append("ALL" % url_params.urlencode())            else:   # 不存在就说明该字段没有被选中                temp.append("ALL")            # 数据标签            for obj in data_list:                # 继续判断,如果是一对多或者多对多,就用对象去获取pk和值                if isinstance(filter_field_obj, ForeignKey) or isinstance(filter_field_obj, ManyToManyField):                    pk = obj.pk                    text = str(obj)                    url_params[filter_field] = pk       # 字段作为键,pk作为值       ?publish=1&authors=2                else:                    pk = obj.get("pk")                    text = obj.get(filter_field)                    url_params[filter_field] = text     # 字段作为键,实际数据作为值     ?title="金平没"                _url = url_params.urlencode()                if current_field_id == str(pk) or current_field_id == text:                    link_tag = "%s" % (_url, text)                else:                    link_tag = "%s" % (_url, text)                temp.append(link_tag)            link_dict[filter_field] = temp        return link_dict
    View Code

    现在在app01 stark.py中的BookConfig类中filter_fields添加"title"字段

    class BookConfig(ModelStark):    list_display = ["title", "price", "publishDate"]    model_class = ModelFormDemo    search_fields = ["title", "price"]    def edit_price_action(self, request, queryset):        queryset.update(price=111)    edit_price_action.short_description = "修改价格"    actions = [edit_price_action]    filter_fields = ["title", "authors", "publish"]
    View Code

    此时页面上的filter中也能显示title字段的所有值,并且也能完成过滤。

    9 然而现在页面上不能显示多对多的字段数据,因为多对多的字段有不止一个值,所以页面的显示效果可能会乱,下面做个简单处理。。

    修改ShowList中show_body方法。

    def show_body(self):        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in self.page_data:  # 获取data_list中的每一个对象            temp = []  # 定义一个内层列表,存储一个对象所有字段的值            for field in self.config.new_list_display():  # 获取每一个要展示的字段   ["name", "age"]                if callable(field):  # 判断字段是否可被调用                    val = field(self.config, obj)  # 给自定义方法传递参数                else:                    field_obj = self.config.model._meta.get_field(field)                    if isinstance(field_obj, ManyToManyField):                        vals = getattr(obj, field).all()    # 获取所有数据                        new_temp = []                        for i in vals:                            new_temp.append(str(i))                        val = ",".join(new_temp)                    else:                        val = getattr(obj, field)  # field是字符串,利用反射获取对象每个字段的值,                        if field in self.config.list_display_links:  # 判断字段是否在list_display_links中,                            _url = self.config.get_change_url(obj)                            val = mark_safe("%s" % (_url, val))                temp.append(val)            new_data_list.append(temp)        return new_data_list
    View Code

    在app01 stark.py中的BookConfig类中list_display添加"authors"字段

    此时访问/stark/app01/book/时, 就能显示authors这一列了。但是如果我们访问其他model的列表页时可能会报错,假如访问stark/app01/author/,然后就会有这样的提示

    这是因为(以author表为例):如果用户没有给author配置list_display,那么就会使用默认的__str__,但是当程序走到show_body的这里时,

    查不到__str__的字段对象,因此会报错。解决办法,异常捕获。

    修改show_body方法,show_body代码:

    def show_body(self):        # 获取表单信息        # 定义一个新的数据列表  格式:        """        [            ["name", "age"]            ["name", "age"]            .......        ]        """        new_data_list = []        for obj in self.page_data:  # 获取data_list中的每一个对象            temp = []  # 定义一个内层列表,存储一个对象所有字段的值            for field in self.config.new_list_display():  # 获取每一个要展示的字段   ["name", "age"]                if callable(field):  # 判断字段是否可被调用                    val = field(self.config, obj)  # 给自定义方法传递参数                else:                    try:                        field_obj = self.config.model._meta.get_field(field)                        if isinstance(field_obj, ManyToManyField):                            vals = getattr(obj, field).all()    # 获取所有数据                            new_temp = []                            for i in vals:                                new_temp.append(str(i))                            val = ",".join(new_temp)                        else:                            val = getattr(obj, field)  # field是字符串,利用反射获取对象每个字段的值,                            if field in self.config.list_display_links:  # 判断字段是否在list_display_links中,                                _url = self.config.get_change_url(obj)                                val = mark_safe("%s" % (_url, val))                    except Exception as e:                        val = getattr(obj, field)                temp.append(val)            new_data_list.append(temp)        return new_data_list
    View Code

     

     pop

     当我们在admin添加数据的时候,如果哪个字段和其他表有关联,可以在输入框的后面点击加号去添加关联表的数据。下面做这个功能。

    1 修改添加页面的样式,修改add_view.html,因为add_view.html使用form.html,因此在form.html上修改。

    {
    % csrf_token %} {
    % for field in form_obj %}
    {
    { field }}
    { { field.errors.0 }}
    +
    {
    % endfor %}
    View Code

    此时页面上每个表单的后面都有加号按钮,但是一些和其他表没有关联的字段是不应该有加号的,因此应该在后台进行判断。因为使用的是ModelForm组件,因此判断字段的类型是不是ModelChoiceField即可。

    2 修改ModelStark类中的add_view

    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        form_obj = ModelFormDemo()        for bfield in form_obj:            if isinstance(bfield.field, ModelChoiceField):  # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;                bfield.is_related = True        if request.method == "POST":            form_obj = ModelFormDemo(request.POST)            if form_obj.is_valid():                form_obj.save()                return redirect(self.get_list_url())        return render(request, 'add_view.html', locals())
    View Code

    这时候只有publish和author后面有加号。

    3 现在是做点击事件,修改form.html,给a标签添加一个click事件,让它跳转到对应的添加页面,因此还需要一个url。

    先去add_view把url获取了

    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        form_obj = ModelFormDemo()        for bfield in form_obj:            if isinstance(bfield.field, ModelChoiceField):  # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;                bfield.is_related = True                # 获取该字段的模型表和模型表的app                # bfield.field.queryset.model   一对多或者多对多字段的关联模型表                relateed_model_name = bfield.field.queryset.model._meta.model_name                relateed_app_label = bfield.field.queryset.model._meta.app_label                _url = reverse("%s_%s_add" % (relateed_app_label, relateed_model_name))                bfield.add_url = _url
    View Code

    4 修改form.html,给a标签添加一个click事件

    {
    % csrf_token %} {
    % for field in form_obj %}
    {
    { field }}
    { { field.errors.0 }} {
    % if field.is_related %}
    + {
    % endif %}
    {
    % endfor %}
    View Code

    现在就能点击加号然后跳转到对应的模型添加页面。

     Ok  ,现在能跳转到对应的添加页面,但是我们需要知道在添加完数据之后给哪个字段添加数据,并且在提交表单之后要返回添加的数据,而且要把值放到字段对应的select标签里。

    解决步骤:(book添加页面为例)

    (1)给url加参数,修改add_view方法,在每条url的后面加上一个参数,以pop_id为键,字段名为值,即只修改下面这句代码:

    bfield.add_url = _url+"?pop_id=id_%s" % bfield.name         # id_%s 和select标签的id对应
    View Code

    当点击publish后面加号时,会弹出一个publish添加页面的小窗口。然而在添加完数据后会跳到publish的列表页,但是并不希望跳到列表页,而是返回之前的book添加页面。并且返回book的添加页面时,把刚才添加的publish数据放到publish的select里。因此需要一个一个页面作为中间人来处理。这个中间人需要完成的工作:1.执行add_view.html中的js,将publish的添加数据放在publish下拉列表中;2. 关闭publish添加页面的小窗口。

    (2) 首先修改add_view方法中对request.POST的处理:

    def add_view(self, request):        ModelFormDemo = self.get_modelform_class()   # 取到的是类名        form_obj = ModelFormDemo()        for bfield in form_obj:            if isinstance(bfield.field, ModelChoiceField):  # bfield.field 获取的是字段对象;bfield.name 获取的是字段名称,类型是字符串;                bfield.is_related = True                # 获取该字段的模型表和模型表的app                # bfield.field.queryset.model   一对多或者多对多字段的关联模型表                relateed_model_name = bfield.field.queryset.model._meta.model_name                relateed_app_label = bfield.field.queryset.model._meta.app_label                _url = reverse("%s_%s_add" % (relateed_app_label, relateed_model_name))                bfield.add_url = _url+"?pop_id=id_%s" % bfield.name         # id_%s 和select标签的id对应        if request.method == "POST":            form_obj = ModelFormDemo(request.POST)            if form_obj.is_valid():                obj = form_obj.save()                pop_id = request.GET.get("pop_id")                if pop_id:      # 判断是否为小窗口的添加                    ret = {
    "pk": obj.pk, "value": str(obj), "pop_id": pop_id} return render(request, 'pop.html', ret) else: return redirect(self.get_list_url()) return render(request, 'add_view.html', locals())
    View Code

    (3)添加pop.html文件

    (4)修改add_view.html,添加js

    View Code

    (5)修改pop.html,添加js

    View Code

    现在就实现pop了。也算是实现了自定义的admin的增删改查。

     

    转载于:https://www.cnblogs.com/ForT/p/10747383.html

    你可能感兴趣的文章
    linux后台运行和关闭SSH运行,查看后台任务
    查看>>
    cookies相关概念
    查看>>
    CAN总线波形中ACK位电平为什么会偏高?
    查看>>
    MyBatis课程2
    查看>>
    桥接模式-Bridge(Java实现)
    查看>>
    如何破解域管理员密码
    查看>>
    Windows Server 2008 R2忘记管理员密码后的解决方法
    查看>>
    IE11兼容IE8的设置
    查看>>
    windows server 2008 R2 怎么集成USB3.0驱动
    查看>>
    vue中router与route的区别
    查看>>
    js 时间对象方法
    查看>>
    网络请求返回HTTP状态码(404,400,500)
    查看>>
    Spring的JdbcTemplate、NamedParameterJdbcTemplate、SimpleJdbcTemplate
    查看>>
    Mac下使用crontab来实现定时任务
    查看>>
    303. Range Sum Query - Immutable
    查看>>
    图片加载失败显示默认图片占位符
    查看>>
    【★】浅谈计算机与随机数
    查看>>
    解决 sublime text3 运行python文件无法input的问题
    查看>>
    javascript面相对象编程,封装与继承
    查看>>
    Atlas命名空间Sys.Data下控件介绍——DataColumn,DataRow和DataTable
    查看>>