本节内容:

1、admin组件使用
2、admin源码解析

学习组件的三部曲:

1、学习如何使用组件
2、阅读源码
3、写一个类似的组件

一、admin组件使用

admin是一个Django封装好的app而已

Django 提供了基于 web 的管理工具。

Django 自动管理工具是 django.contrib 的一部分。

1、admin配置的增删改查的url:

查:http://127.0.0.1:8000/admin/app01/publish/
增:http://127.0.0.1:8000/admin/app01/publish/add/
改:http://127.0.0.1:8000/admin/app01/publish/1/change/
删:http://127.0.0.1:8000/admin/app01/publish/1/delete/

url这里通过不同的app名称来具体找对应的表

2、使用前的工作

1、配置settings.py中的INSTALLED_APPS路径

这里配置好,Django才找得到你的APP
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01" # 写法一:
    'app01.apps.App01Config'  # 写法二
]
django.contrib是一套庞大的功能集,它是Django基本代码的组成部分。

2、激活管理工具

通常我们在生成项目时会在 urls.py 中自动设置好,
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    ]

3、使用管理工具

启动开发服务器,然后在浏览器中访问 http://127.0.0.1:8000/admin/,得到登陆界面,
你可以通过命令 python manage.py createsuperuser 在Terminal控制台下来创建超级用户。

4、先在数据库中创建好数据表的结构

本节实例中的models.py文件
from django.db import models

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

# Create your models here.
class Book(models.Model):
    title = models.CharField( max_length=32)
    pub_date=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publish=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE,null=True)
    authors=models.ManyToManyField("Author",db_table="book2authors") # 创建关系表
    def __str__(self):
        return self.title

class Publish(models.Model):
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)
    email=models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Author(models.Model):
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    #books=models.ManyToManyField("Book")
    ad=models.OneToOneField("AuthorDetail",null=True,on_delete=models.CASCADE)
    def __str__(self):
        return self.name
class AuthorDetail(models.Model):
    birthday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField( max_length=64)
    # author=models.OneToOneField("Author",on_delete=models.CASCADE)
    def __str__(self):
        return str(self.telephone)

3、admin的定制

完成了上面的操作,就可以开始我们的使用admin组件了。
为了让 admin 界面管理某个数据模型,我们需要先注册该数据模型到 admin

1、两种注册方法

在admin.py中只需要讲Mode中的某个类注册,即可在Admin中实现增删改查的功能,如:

admin.site.register(models.UserInfo)
但是,这种方式比较简单,


如果想要进行更多的定制操作,需要利用ModelAdmin进行操作,如:

方式一:
    class UserAdmin(admin.ModelAdmin):
        list_display = ('user', 'pwd',)

    admin.site.register(models.UserInfo, UserAdmin) # 第一个参数可以是列表


方式二:
    @admin.register(models.UserInfo)     # 第一个参数可以是列表
    class UserAdmin(admin.ModelAdmin):
        list_display = ('user', 'pwd',)

2、admin具体的定制方法实例

本节实例中的对应app01程序admin.py文件代码
from django.contrib import admin

# Register your models here.
from .models import Book
from .models import Publish
from .models import Author
from .models import AuthorDetail


print("app01 admin.py>>>>")  # 启动项目的时候会自动加载admin.py文件

class PublishConfig(admin.ModelAdmin):
    list_display=["name","city","email"]

class BookConfig(admin.ModelAdmin):

    def show_authors(self,obj): # <QuerySet [<Author: 横斜>]> 拿到Authors对象(queryset对象集合,因为可能有多个作者)
        print(obj.authors.all())  # 这里obj是实例化的book对象
        return ",".join([obj.name for obj in obj.authors.all()]) # 用join来分隔作者,列表推导式拿到每一个作者的名字

    list_display =  ["title","price","publish","pub_date","show_authors"] # 多对一没问题还可以正常些字段就显示,多对多就要用方法来显示
    list_display_links = ["price","publish"]  # 设置点击可以进入编辑的锚点字段(类似于a标签跳转编辑)。

    list_filter = ["publish","authors"] # 筛选框,一般根据多对多,多对一的字段进行筛选
    search_fields = ["title","price"]  # 搜索框的内容根据在列表中字段进行搜索

    list_editable = ["title",] # 直接在显示页面可以编辑列表中的字段
    # change_list_template="mylist.html" # 显示自己的页面,这样根本就没有用到admin提供的,没什么用
    ordering = ["-price"] # 根据该条件进行排序显示,-price根据价格降序排列

    # 批量修改的功能三步走
    def patch_init(self,request,queryset): # 第一步定义方法
        queryset.update(price=100)

    patch_init.short_description = "批量初始化" # 第二:起名,在action中显示该操作的名字

    actions = [patch_init,]  # 第三:把这个功能添加进action里,点击就会执行该方法



admin.site.register(Book,BookConfig) # register是site对象的一个方法,拿到的是一个字典
admin.site.register(Publish,PublishConfig)
admin.site.register(Author) # 简单的注册,不能进行更多的定制操作
admin.site.register(AuthorDetail)

print("app01:>>>>>",admin.site._registry) # 拿到该对象下的字典存放着创建的表,大家共享的单例模式下的字典

二、admin源码解析

在看源码解析之前,首先我们要知道单例模式是什么???

1、单例模式

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。
如果在程序运行期间,有很多地方都需要使用配置文件的内容,
也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,
而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。

事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

1、在python中,实现单例模式的四种方法

1、使用模块
2、使用 __new__
3、使用装饰器(decorator)
4、使用元类(metaclass)

2、常用的两种实现单例模式的方法new和使用模块

(1)使用 __new__
为了使类只能出现一个实例,我们可以使用 __new__ 来控制实例的创建过程,代码如下:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kw):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(cls, *args, **kw)
        return cls._instance

class MyClass(Singleton):
    a = 1

在上面的代码中,我们将类的实例和一个类变量 _instance 关联起来,
如果 cls._instance 为 None 则创建实例,否则直接返回 cls._instance。

执行情况如下:


>>> one = MyClass()
>>> two = MyClass()
>>> one == two
True
>>> one is two
True
>>> id(one), id(two)
(4303862608, 4303862608)


(2)使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,
当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。
因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。

如果我们真的想要一个单例类,可以考虑这样做:

# mysingleton.py
class My_Singleton(object):
    def foo(self):
        pass

my_singleton = My_Singleton()
将上面的代码保存在文件 mysingleton.py 中,然后这样使用:

from mysingleton import my_singleton

my_singleton.foo()

2、admin执行流程(三步走)

1、启动所有app下的admin.py文件

启动Django项目的时候就会,循环加载执行所有已经注册的app中的admin.py文件
# 源码中会自动执行该方法:
def autodiscover():
    autodiscover_modules('admin', register_to=site)

2、注册模型类

admin.site: AdminSite的单例对象
admin.site.register(Book,BookConfig) # 注册
admin.site.register(Author)
class ModelAdmin():
      pass

class AdminSite():  # 所有的app共用这个单例对象,

    def __init__():
        self._registry = {}  # model_class class -> admin_class instance 模型类为键,配置类的实例对象为值


    def register(self, model_or_iterable, admin_class=None): # 这个方法主要干了两件事,
        admin_class = admin_class or ModelAdmin   # 看你参数是否有传配置类,有就用你传的,没有就默认的
        self._registry[model] = admin_class(model, self)  # 往这个单例对象的实例变量,添加一个键值对
                                                        # model_class class -> admin_class instance

3、基于二级分发设计url路由

语法:
path('shuying/',([ ],None,None)),

补充知识点:
Book._meta.model_name  # 拿到当前表的小写表名 'book'
Book._meta.app_label  # 拿到当前表所在的app名称 'app01'
一二级分发的简单示例url.py文件
# 这里就没有分开解耦了直接写在同一个页面方便查看
from django.contrib import admin
from django.urls import path

from django.shortcuts import HttpResponse

def test01(request):
    return HttpResponse("test01>>>>>>>")
def test02(request):
    return HttpResponse("test02>>>>>>>")
def test03(request):
    return HttpResponse("test03>>>>>>>")
def test04(request):
    return HttpResponse("test04>>>>>>>")
def test05(request):
    return HttpResponse("test05>>>>>>>")

urlpatterns = [
    # # 一级分发
    # path("index/",([
    #            path('test01/',test01), # 语法是:固定嵌套
    #            path('test02/',test02),
    #                ],None,None)),
    #
    # # 二级分发
    # path("index/",([
    #            path('name/',([
    #                path('alex/',test01),
    #                path('egon/',test02)
    #            ],None,None)), # 语法是:固定嵌套
    #            path('shop/',([
    #                path('apple/',test03),
    #                path('huawei/',test04),
    #                path('chang/',test05)
    #            ],None,None)),
    #                ],None,None)),
]
本节示例二级分发的应用
from django.contrib import admin
from django.urls import path,re_path

from django.shortcuts import HttpResponse

 # 二级分发应用

def add_view(request):
    return HttpResponse("add_view...")
def list_view(request):
    return HttpResponse("list_view...")
def change_view(request,id):
    return HttpResponse("change_view...")
def delete_view(request,id):
    return HttpResponse("delete_view...")


 # 二级分发
def get_urls2():
    temp=[
        path('',list_view),
        path('add/',add_view),
        re_path('(\d+)/change/',change_view), # 这里写对应url下执行的视图函数
        re_path('(\d+)/delete/',delete_view)
    ]
    return temp # 记得返回一个列表

# 一级分发
def get_urls():
    print("查看对象>>>>>>>",admin.site._registry)  #{Book:BookCongfigObj,Publish:PublishConfigObj,.....}
                                                  # 拿到site对象中的_register字典
    temp = []
    for model,config_obj in admin.site._registry.items():
        model_name = model._meta.model_name  # 拿到该对象的小写表名
        app_label = model._meta.app_label  # 查找当前表格对象的在哪个app下,拿到该APP名称
        temp.append(path('%s/%s/' %(app_label,model_name),(get_urls2(),None,None)))
    return temp

urlpatterns = [
    # path('admin/', admin.site.urls),

    # 二级分发应用
    path('shuying/',(get_urls(),None,None)),
]


0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *