Django基于Admin原理实现的CURD组件

Django基于Admin原理实现的CURD组件



Django管理后台admin

常用共29个功能: 参考

  1. 在项目的app中的admin.py文件中注册models类,示例如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    # cat app_name/admin.py

    from django.contrib import admin
    from django.contrib.admin import ModelAdmin
    from . import models
    from django.shortcuts import HttpResponse
    from django.forms import ModelForm
    from django.forms import fields
    from django.forms import widgets

    # 用户表,自定义admin后台错误信息提示
    class UserModelForm(ModelForm):
    others = fields.CharField()
    class Meta:
    model = models.UserInfo
    fields = "__all__"
    error_messages = {
    'name':{'required':'用户名不能问空'}
    }

    class UserInfoModelAdmin(ModelAdmin):
    # 使用自定义的form表单验证功能
    form = UserModelForm

    # 1.定义列表页面,显示列数据
    list_display = ['name','pwd','email']

    def email(self, obj):
    return obj.name + obj.pwd

    email.empty_value_display = "默认为空时显示的值"

    # 2.定义列表页面,列可以进行点击进入编辑页面
    list_display_links = ['pwd']

    # 3.定义列表页面,快速搜索
    list_filter = ['ut']

    # 4.定义列表页面,分页功能
    list_per_page = 10

    # 5. 列是否可编辑
    list_editable = ['name']

    # 6. 查询列
    search_fields = ['name','pwd']

    # 7. 是否在页面顶端显示保存按钮
    # save_on_top = True

    # 8. 下拉选项的批量操作,类似于批量删除功能
    def func(self, request, queryset):
    print(self, request, queryset)
    id_list = request.POST.getlist('_selected_action')
    # models.UserInfo.objects.filter(id__in=id_list).delete()

    func.short_description = "批量初始化"

    actions = [func, ]

    # 9. 列表页面使用模板文件
    change_list_template = ['xxx.html']

    # raw_id_fields = ['ut',]
    # fields = ['name']
    # exclude = ['name',]

    # 10. 分类展示
    # fieldsets = (
    # ('基本数据', {
    # 'fields': ('name',)
    # }),
    # ('其他', {
    # 'classes': ('collapse', 'wide', 'extrapretty'), # 'collapse','wide', 'extrapretty'
    # 'fields': ('pwd', 'ut'),
    # }),
    # )

    # 11.快速过滤
    # filter_vertical = ("roles",)
    filter_horizontal = ("roles",)

    # 12. 排序
    ordering = ['-id']

    # 注册使用自定义的UserInfoModelAdmin类的页面展示规则
    admin.site.register(models.UserInfo,UserInfoModelAdmin)


    # 2. 用户类型表(基于ModelAdmin类)
    class UserTypeModelAdmin(ModelAdmin):
    list_display = ['title']

    admin.site.register(models.UserType,UserTypeModelAdmin)

注: 默认使用ModelAdmin类进行后台操作,可以定义自己的calss,通过继承ModelAdmin类实现定制页面展示功能
2. Django自带Admin原理
2.1 每注册一个model类,Django内部会生成4个对应的增删改查URL,如下所示:

1
2
3
4
5
6
URL:
/admin/app_name/model_name/
/admin/app_name/model_name/add/
/admin/app_name/model_name/1/change/
/admin/app_name/model_name/1/delete/
例:/admin/app01/userinfo/

2.2 内部实现机制

  • 在app下面的admin.py文件中注册models类;
  • Django程序在启动时会循环所有app下面的admin文件中注册的models类,为每个models类生成对应的增删改查4个URL;
  • 每个注册的model类会封装一个用于处理当前类所对应的URL的view对象,用于处理CURD操作,默认为ModelAdmin类对象;
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    1. self.model=models.UserInfo
    /admin/app01/userinfo/ obj1.changelist_view
    /admin/app01/userinfo/add/ obj1.add_view
    /admin/app01/userinfo/(\d+)/delete/ obj1.delete_view
    /admin/app01/userinfo/(\d+)/change/ obj1.change_view

    2. self.model=models.UserType
    /admin/app01/usertype/ obj2.changelist_view
    /admin/app01/usertype/add/ obj2.add_view
    /admin/app01/usertype/(\d+)/delete/ obj2.delete_view
    /admin/app01/usertype/(\d+)/change/ obj2.change_view

自定义实现CURD类似Admin功能

步骤

  • 了解django启动时执行的启动文件顺序
  • 制作启动文件
  • settings.py配置文件中注册启动文件使全局生效
  • 实现组件CURD业务逻辑
  • 主要参考Django Admin的实现,利用单利模式和include原理实现路由分发

实现

假设组件app名称为: arya

  1. Django程序启动顺序
    在程序启动时,django会根据配置项INSTALLED_APPS中注册的启动文件,遍历每个App下面对应的注册启动文件,在arya组件下面的app.py文件中实现ready方法激活启动文件,如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # arya/app.py

    from django.apps import AppConfig

    class AryaConfig(AppConfig):
    name = 'arya'

    # Django启动时自动扫描所有app下面的arya模块
    def ready(self):
    from django.utils.module_loading import autodiscover_modules
    autodiscover_modules('arya')
  2. 在项目的setting.py文件中配置,激活组件,使全局生效,此时Django启动时,在完全启动前,便会扫描所有app下面的arya.py文件并加载生效
    1
    2
    3
    4
    5
    INSTALLED_APPS = [
    ...
    'arya.apps.AryaConfig',
    ...
    ]
  3. 核心代码逻辑实现
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    # cat arya/seevice/v1.py

    from django.conf.urls import url
    from django.shortcuts import render, HttpResponse, redirect

    class AryaConfig(object):
    """
    每个models类的URL对应处理的View实现
    """

    def __init__(self, model_class, site):
    self.model_class = model_class
    # View对象
    self.site = site

    @property
    def urls(self):
    partterns = [
    url(r'^$', self.changelist_view),
    url(r'^add/', self.add_view),
    url(r'^(\d+)/change/$', self.change_view),
    url(r'^(\d+)/delete/$', self.delete_view)
    ]
    return partterns

    def changelist_view(self, request):
    """
    列表试图
    :param request:
    :return:
    """
    # return HttpResponse("列表页面")
    return render(request, 'arya/changelist.html')

    def add_view(self, request):
    """
    添加试图
    :param request:
    :return:
    """
    return HttpResponse("添加试图")

    def change_view(self, request):
    """
    修改试图
    :param request:
    :return:
    """
    return HttpResponse("修改试图")

    def delete_view(self, request):
    """
    删除试图
    :param request:
    :return:
    """
    return HttpResponse("删除试图")


    class AryaSite(object):
    """
    实现类似于admin.site.register()功能
    """

    # 存放所有的models类及对应处理UTRL的的view对象
    def __init__(self):
    self._registry = {}

    def register(self, class_name, config_class):
    """
    注册方法,封装对象
    self._registry = {
    module.UserInfo: obj1, # obj1 = AryaConfig(models.UserInfo,site),
    module.UserType: obj2, # obj2 = AryaConfig(models.UserType,site),
    }
    :param class_name: models类
    :param config_class: 对应的View类(AryaConfig)
    :return:
    """
    self._registry[class_name] = config_class(class_name, self)

    @property
    def urls(self):
    """
    处理子路由
    :return:
    """
    partterns = [
    url(r'^login/$', self.login),
    url(r'^logout/$', self.logout),
    ]
    # 循环self._registry属性里面的每一个元素,key为models类,value为URLS对应处理的类obj对象
    for model_class, arya_config_obj in self._registry.items():
    # 分别为app名称和models的类名称
    print("*" * 50)
    print(model_class._meta.app_label, model_class._meta.model_name)
    app_model_name_urls = r'^{0}/{1}/'.format(model_class._meta.app_label, model_class._meta.model_name)
    # arya_config_obj.urls self._registry字典中存放的values对象obj下面的urls方法
    pt = url(app_model_name_urls, (arya_config_obj.urls, None, None))
    partterns.append(pt)
    # 3元组
    return partterns, None, None

    def login(self):
    """
    登陆
    :return:
    """
    return redirect('login')

    def logout(self):
    """
    退出
    :return:
    """
    return redirect('login')


    # 实例化,利用单例模式
    site = AryaSite()

引入arya组件并使用

  1. 创建app,引入arya组件

    1
    2
    3
    创建多个app,并在每个app下面创建arya.py文件,用于实现CURD操作
    django startapp app01
    ...
  2. 在app01下面创建models模型

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    # cat app01/models.py

    from django.db import models

    class UserType(models.Model):
    """
    用户类型表
    """
    title = models.CharField(max_length=32, verbose_name="用户类型")

    class Meta:
    verbose_name_plural = "用户类型表"

    def __str__(self):
    return self.title


    class Role(models.Model):
    """
    角色表
    """
    caption = models.CharField(max_length=32, verbose_name="角色名")

    class Meta:
    verbose_name_plural = "角色表"

    def __str__(self):
    return self.caption


    class UserInfo(models.Model):
    """
    用户表
    """
    username = models.CharField(max_length=32, verbose_name="用户名")
    password = models.CharField(max_length=64, verbose_name="密码")
    email = models.CharField(max_length=32, verbose_name="邮箱")
    ut = models.ForeignKey(to="UserType", blank=True, verbose_name="用户类型")
    roles = models.ManyToManyField(to="Role", blank=True, verbose_name="所属角色")

    class Meta:
    verbose_name_plural = "用户表"

    def __str__(self):
    return self.username
  3. 生成表结构并制作部分展示数据,可以直接在表中添加,也可以通过admin后台进行添加

  4. 在app01中的arya.py文件中注册models类并自定义实现展示UI

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    # cat app01/arya.py

    from arya.service import v1
    from . import models


    # 自定义属性控制UI展示
    class UserInfoConfig(v1.AryaConfig):
    """
    自定义用户信息UI
    """
    list_display = ['username', 'ut', 'roles', 'email']


    class UserTypeConfig(v1.AryaConfig):
    """
    自定义用户类型UI
    """
    list_display = ['title']


    class RoleConfig(v1.AryaConfig):
    """
    自定义角色UI
    """
    list_display = ['caption']

    # 注册models
    v1.site.register(models.UserInfo, UserInfoConfig)
    v1.site.register(models.UserType, UserTypeConfig)
    v1.site.register(models.Role, RoleConfig)
  5. 在项目中应用arya组件,使URL生效

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #cat project_name/urls.py

    from django.contrib import admin
    from django.conf.urls import url
    from arya.service import v1
    from . import views

    # url的第二个参数返回类型为([],None,None)的元组,源码见include方法
    # 此处的login和logut也可以使用arya/service/v1.py文件中定义的路由,示例为自己实现
    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # 此处会生成对应的多个URL子路由
    url(r'^arya/', v1.site.urls),
    url(r'^index/$', views.index, name="index"),
    url(r'^login/$', views.login, name="login"),
    url(r'^logout/$', views.logout, name="logout"),
    ]
  6. 路由试图处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    # cat project_name/views.py

    from django.shortcuts import render, redirect
    from app01 import models

    def login(request):
    """
    登陆
    :param request:
    :return:
    """
    if request.method == "GET":
    return render(request, 'login.html')
    else:
    username = request.POST.get('username')
    password = request.POST.get('password')
    obj = models.UserInfo.objects.filter(username=username, password=password).first()
    if obj:
    request.session['userinfo'] = {'username': obj.username, 'is_login': True}
    return redirect('index')
    return render(request, 'login.html', {'msg': '用户名或密码错误'})


    def logout(request):
    """
    退出
    :param request:
    :return:
    """
    if request.method == "GET":
    return redirect('login')


    def index(request):
    """
    首页
    :param request:
    :return:
    """
    if request.method == "GET":
    return render(request, 'index.html')
  7. 项目URL
    此时在项目的路由中已经存在对应增删改查的4个URL,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # app_nmae 应用名
    # models_name models类名小写
    arya/app_name/models_name/
    arya/app_name/models_name/add/
    arya/app_name/models_name/(\d+)/change/
    arya/app_name/models_name/(\d+)/delete/
    # 在该示例中为:
    http://127.0.0.1:8000/arya/app01/userinfo/
    http://127.0.0.1:8000/arya/app01/userinfo/add/
    http://127.0.0.1:8000/arya/app01/userinfo/()
    ....
  8. 扩展
    在每个app下面的arya.py文件中继承arya组件中的service.v1.AryaConfig类,自己内部实现处理URL的view以及定制化操作


总结

至此,自定义实现的类似Django admin组件基本开发完成,能够满足models模型数据的CURD操作。在Django中作为app引入CustAdmin组件,然后在settings文件中注册后就
可以生效使用了,engoy it!

github

本文标题:Django基于Admin原理实现的CURD组件

文章作者:shuke

发布时间:2020年04月20日 - 14:04

最后更新:2020年04月20日 - 14:04

原始链接:https://shuke163.github.io/2020/04/20/Django%E5%9F%BA%E4%BA%8EAdmin%E5%8E%9F%E7%90%86%E5%AE%9E%E7%8E%B0%E7%9A%84CURD%E7%BB%84%E4%BB%B6/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢您的阅读-------------

本文标题:Django基于Admin原理实现的CURD组件

文章作者:shuke

发布时间:2020年04月20日 - 14:04

最后更新:2020年04月20日 - 14:04

原始链接:https://shuke163.github.io/2020/04/20/Django%E5%9F%BA%E4%BA%8EAdmin%E5%8E%9F%E7%90%86%E5%AE%9E%E7%8E%B0%E7%9A%84CURD%E7%BB%84%E4%BB%B6/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%