1.实现效果

  研究了下django admin的功能,自己实现了一个简单的可插拔式后台管理系统,方便自定义特殊的功能,而且作为一个独立单独的django app,可以整体拷贝到其他项目中作为后台数据管理系统,对数据进行增删改查和自定义操作。下图是拷贝到一个图书管理系统中的后台效果:

可插拔式后台管理系统(Django)

2.实现思路

  2.1 url的设计和分发

         Django自带的admin,对于不同app的不同model表,都会动态的生成类似下面的四条url,分别对应着后台数据的增删改查页面。而为了实现动态路由需要配置两处,一是在项目全局urls.py文件中urlpatterns = [ url(r'^admin/', admin.site.urls),], 二是在每个app的admin.py文件中对model表进行了注册admin.site.register(model),这两处都涉及到了一个admin.site对象,因此我们需要实现自己的site对象即可。

      查看:http://127.0.0.1:8008/admin/app01/book/

      添加:http://127.0.0.1:8008/admin/app01/book/add/

      更新:http://127.0.0.1:8008/admin/app01/book/1/change/

      删除:http://127.0.0.1:8008/admin/app01/book/1/delete/

    另外,对于django的url多级路由格式需要了解下:url(r" ",([ url(), url()], None, None)), 为 一个三元元祖([],None,None),而元祖中的列表[]又可以嵌套多个相同格式的url([],None,None),如下面的代码实现了三条路由:

     url(r'^myAdmin2/', ([ url(r'^book1/',views.index1),
        url(r'^book2/',([ url(r'^change/',views.index2), url(r'^add/',views.index3)],None,None ))],
         None,None),)

  对应的url如下:

      http://127.0.0.1:8008/myAdmin2/book1/

      http://127.0.0.1:8008/myAdmin2/book2/change/

      http://127.0.0.1:8008/myAdmin2/book2/add/

  根据上述的思路和多级url路由,可以定义同样的路由设置,一是设置urls.py中全局路由,二是在app的admin.py文件中注册model,三是实现自己的myAdmin.site对象。对应的代码依次如下:

urls.py

from django.conf.urls import urlfrom myAdmin.service.site import site  # 引入自定义的site.py 文件中生成的site单例对象
urlpatterns = [
    url(r'^myAdmin/', site.urls),
]

app01/admin.py

from myAdmin.service.site import site
from app01 import models
site.register(models.Book)
site.register(models.Author)
site.register(models.Publish)

myAdmin/service/site.py

class ModelAdmin(object):
    def __init__(self, model):
        self.model = model
        self.model_name = self.model._meta.model_name
        self.app_label = self.model._meta.app_label
    
    @property
    def urls(self):
        return self.get_urls(), None, None

    def get_urls(self):
        patterns = [url(r'^$', self.list_view, name='%s_%s_list'%(self.model_name,self.app_label)),
                    url(r'^add/$', self.add_view,name='%s_%s_add'%(self.model_name,self.app_label)),
                    url(r'^(.+)/change/$', self.change_view,name='%s_%s_change'%(self.model_name,self.app_label)),
                    url(r'^(.+)/delete/$', self.delete_view,name='%s_%s_delete'%(self.model_name,self.app_label)),
                    ]
        return patterns

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

    def register(self, model, admin_class=None):      #对应model表注册时的site.register()
        if not admin_class:
            admin_class = ModelAdmin
        admin_obj = admin_class(model)
        self._registry[model] = admin_obj

    @property
    def urls(self):   #对应全局路由中的site.urls
        return self.get_urls(), None, None

    def get_urls(self):
        patterns = []
        for model, admin_obj in self._registry.items():
            urls = url(r'^{0}/{1}/'.format(model._meta.app_label, model._meta.model_name), admin_obj.urls)
            patterns.append(urls)
        return patterns

site = AdminSite()

   在site.py代码中有三处值得注意,

    1. site = AdminSite(),  这里是采用了python模块的天然单例模式,由于每个app中都会采用site对象,因此在整个项目中只能有一个site对象。

    2. AdminSite中的get_urls(self)函数

      urls = url(r'^{0}/{1}/'.format(model._meta.app_label, model._meta.model_name), admin_obj.urls) 实现了第一级动态路由,即/app01/model/

    3. ModelAdmin中的get_urls(self)函数

      patterns = [url(r'^$', self.list_view, name='%s_%s_list'%(self.model_name,self.app_label)),

             url(r'^add/$', self.add_view,name='%s_%s_add'%(self.model_name,self.app_label)),

             url(r'^(.+)/change/$', self.change_view,name='%s_%s_change'%(self.model_name,self.app_label)),

                                       url(r'^(.+)/delete/$', self.delete_view,name='%s_%s_delete'%(self.model_name,self.app_label)), ]

     实现了第二级动态路由,即 app01/model, app01/model/add/, app01/model/id/change/, app01/model/id/delete 增删改查四条路径。

 2.2 实现增删改查处理函数

   在上面url设计中,在ModelAdmin类中定义了相应的处理函数,如下面self.list_view,self.add_view,self.change_viewself.delete_view,需要对其依次实现。

      patterns = [url(r'^$', self.list_view, name='%s_%s_list'%(self.model_name,self.app_label)),

             url(r'^add/$', self.add_view,name='%s_%s_add'%(self.model_name,self.app_label)),

             url(r'^(.+)/change/$', self.change_view,name='%s_%s_change'%(self.model_name,self.app_label)),

                                       url(r'^(.+)/delete/$', self.delete_view,name='%s_%s_delete'%(self.model_name,self.app_label)), ]

  实现后的site.py代码如下:由于需要用到ModelForm类,定义了两个辅助方法get_modelform_class()和 change_modelform()
 def get_modelform_class(self):
        class Model_form(ModelForm):
            class Meta:
                model = self.model
                fields = '__all__'
        return Model_form                  #返回类对象

    def change_modelform(self,modelform):
        for item in modelform:
            if isinstance(item.field, ModelChoiceField):  # ModelChoiceField表示field字段对应的为外键或多对对关系
                pop_item_name = item.name
                item.is_pop=True #为实例动态绑定属性
                item_model_name = item.field.queryset.model._meta.model_name
                item_app_label = item.field.queryset.model._meta.app_label
                item.pop_url = '/myAdmin/{0}/{1}/add/?pop_item_name={2}'.format(item_app_label, item_model_name,pop_item_name)
        return modelform   
   

  #添加数据
    def add_view(self, request):
        modelform_class = self.get_modelform_class()
        form = modelform_class()
        form = self.change_modelform(form)
        if request.method == 'POST':
            form = modelform_class(request.POST)
            field_obj = form.save()
            # url = request.path[:-4]
            # print url
            pop_item_name = request.GET.get('pop_item_name')
            if pop_item_name:
                result = {'pk':field_obj.pk,'text':str(field_obj),'pop_item_name':pop_item_name}
                return render(request, 'process_pop.html', {'result':result})
            return redirect(self.get_list_url())
        return render(request, 'add_view.html', locals())

    # 改变数据
    def change_view(self, request, number):
        modelform_class = self.get_modelform_class()
        model_obj = self.model.objects.filter(id=number).first()
        form = modelform_class(instance=model_obj)
        form = self.change_modelform(form)
        if request.method == 'POST':
            form = modelform_class(request.POST, instance=model_obj)
            form.save()
            return redirect(self.get_list_url())
        return render(request, 'change_view.html', locals())

    # 删除数据
    def delete_view(self, request, number):
        model_obj = self.model.objects.get(id=number)
        list_url = self.get_list_url()
        edit_url = self.get_change_url(model_obj)
        if request.method=='POST':
            model_obj.delete()
            return redirect(list_url)
        return render(request, 'delete_view.html', locals())
site.py

相关文章:

  • 2021-05-27
  • 2021-12-26
  • 2022-12-23
  • 2021-12-01
  • 2021-11-21
  • 2021-12-01
猜你喜欢
  • 2021-04-01
  • 2021-12-19
  • 2021-05-21
  • 2021-11-21
  • 2021-07-14
  • 2021-08-19
  • 2021-09-06
相关资源
相似解决方案