Django框架详解

Django作为Python web开发的重量级框架,想要从事Django开发的必须了解Django框架的使用,这篇文章供大家学习参考,共同交流

1. Web应用程序处理流程

  • 前端客户端

    • 浏览器
    • APP
    • ajax
    • 爬虫程序 --http–>
  • 后端服务器程序(接收请求报文,解析请求报文)

    • Gunicorn

      • Gunicorn(绿色独角兽)是一个Python WSGI的HTTP服务器

      • 方式一:在flask项目下启动

        # 第一个app指的是app.py文件,第二个指的是flask应用的名字;
        gunicorn -w 4 -b 0.0.0.0:8000 app:app
        # 参数详解
        # -w INT, --workers INT:用于处理工作进程的数量,为正整数,默认为1;
        # -b ADDRESS: ADDRESS,ip加端口,绑定运行的主机;
        
      • 以配置文件的形式

        # gunicorn.conf文件
        
        # 并行工作进程数
        workers = 4
        # 指定每个工作者的线程数
        threads = 2
        # 监听内网端口5000
        bind = '127.0.0.1:5000'
        # 设置守护进程,将进程交给supervisor管理
        daemon = 'false'
        # 工作模式协程
        worker_class = 'gevent'
        # 设置最大并发量
        worker_connections = 2000
        # 设置进程文件目录
        pidfile = '/var/run/gunicorn.pid'
        # 设置访问日志和错误信息日志路径
        accesslog = '/var/log/gunicorn_acess.log'
        errorlog = '/var/log/gunicorn_error.log'
        # 设置日志记录水平
        loglevel = 'warning'
        
      • 启动unicorn

        gunicorn -c gunicorn.conf app:app
        # -c CONFIG: CONFIG,配置文件的路径,通过配置文件启动;生产环境使用;
        
    • uwisg --WSGI–>

  • 框架程序

    • Django
    • Flask
    • 框架流程
      • http请求对象
      • 中间层处理
      • 视图函数,业务处理(数据库,模板,表单)
      • 中间层处理
      • 响应对象
  • 构造响应报文,返回响应报文

  • 前端客户端

2. Web程序框架的意义

  • 用于搭建Web应用程序
  • 免去不同Web应用相同代码部分的重复编写,只需关心Web应用核心的业务逻辑实现

3. Web应用程序的本质

  • 接收并解析HTTP请求,获取具体的请求信息
  • 处理本次HTTP请求,即完成本次请求的业务逻辑处理
  • 构造并返回处理结果——HTTP响应

4. Web框架

  • 如何搭建工程程序
    • 工程的组建
    • 工程的配置
    • 路由定义
    • 视图函数定义
  • 如何获取请求数据(操作request对象)
  • 如何构造响应数据(构造response对象)
  • 如何使用中间层
  • 框架提供的其他功能组件的使用
    • 数据库
    • 模板
    • admin

5.特点

  • Django,是用python语言写的开源web开发框架,并遵循MVC(其核心思想是分工,解耦,让不同代码之间降低耦合,增强代码的可扩展性和可移植性,实现先后兼容)设计

    对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。

    • 重量级框架
      • 提供项目工程管理的自动化脚本工具
    django-admin startproject xxx
    Python manage.py startapp xxx
    
    • 数据库ORM支持(对象关系映射,英语:Object Relational Mapping)
    • 模板
    • 表单
    • Admin管理站点
    • 文件管理
      • 认证权限
      • session机制
      • 缓存
    • Django的MVT
      • M全拼为Model,与MVC中的M功能相同,负责和数据库交互,进行数据处理。
      • V全拼为View,与MVC中的C功能相同,接收请求,进行业务处理,返回应答。
      • T全拼为Template,与MVC中的V功能相同,负责封装构造要返回的html。

6.项目工程

  • 创建
Django-admin startproject 项目名称
  • 运行
python manage.py runserver ip:端口
  • 创建子应用模块
Python manage.py startapp 子应用名称

7.配置文件

    1. BASE_DIR

当前工程的根目录,Django会依此来定位工程内的相关文件,我们也可以使用该参数来构造文件路径。

    1. DEBUG

调试模式,创建工程后初始值为True,即默认工作在调试模式下。

​ 作用:Django程序出现异常时,向前端显示详细的错误追踪信息,

注意:部署线上运行的Django不要运行在调式模式下,会影响用户体验,记得修改DEBUG=False。

    1. 本地语言与时区

初始化的工程默认语言和时区为英语和UTC标准时区

LANGUAGE_CODE = 'en-us'  # 语言
TIME_ZONE = 'UTC'  # 时区

将语言和时区修改为中国大陆信息

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'

8.静态文件

项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Django中提供了一种解析的方式配置静态文件路径。静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。

为了提供静态文件,需要配置两个参数:

  • STATICFILES_DIRS=[ ] 存放查找静态文件的目录 接收的是list

  • STATIC_URL 访问静态文件的URL前缀

    注意

    • Django 仅在调试模式下(DEBUG=True)能对外提供静态文件。
    • 当DEBUG=False工作在生产模式时,Django不再对外提供静态文件,需要是用collectstatic命令来收集静态文件并交由其他静态文件服务器来提供。

9. 路由命名与reverse反解析(逆向)

  • 路由命名
    在定义路由的时候,可以为路由命名,方便查找特定视图的具体路径信息。

    1) 在使用include函数定义路由时,可以使用namespace参数定义路由的命名空间,如

url(r'^users/', include('users.urls', namespace='users')),

命名空间表示,凡是users.urls中定义的路由,均属于namespace指明的users名下。

命名空间的作用:避免不同应用中的路由使用了相同的名字发生冲突,使用命名空间区别开。

2) 在定义普通路由时,可以使用name参数指明路由的名字,如

urlpatterns = [
    url(r'^index/$', views.index, name='index'),
    url(r'^say', views.say, name='say'),
]
  • reverse反解析
    使用reverse函数,可以根据路由名称,返回具体的路径,如:
from django.urls import reverse  # 注意导包路径

def index(request):
    return HttpResponse("hello the world!")

def say(request):
    url = reverse('users:index')  # 返回 /users/index/
    return HttpResponse('say')

对于未指明namespace的,reverse(路由name)
对于指明namespace的,reverse(命名空间namespace:路由name)

10.Request

  • 利用HTTP协议向服务器传参有几种途径?

    • 提取URL的特定部分,如/weather/beijing/2018,可以在服务器端的路由中用正则表达式截取;
    • 查询字符串(query string),形如key1=value1&key2=value2;
    • 请求体(body)中发送的数据,比如表单数据、json、xml;
    • 在http报文的头(header)中。
  • Django中的QueryDict对象

    定义在django.http.QueryDict

    HttpRequest对象的属性GET、POST都是QueryDict类型的对象

    与python字典不同,QueryDict类型的对象用来处理同一个键带有多个值的情况

​ 1)方法get():根据键获取值

如果一个键同时拥有多个值将获取最后一个值

如果键不存在则返回None值,可以设置默认值进行后续处理

dict.get('键',默认值)
可简写为
dict['键']

​ 2) 方法getlist():根据键获取值,值以列表返回,可以获取指定键的所有值

如果键不存在则返回空列表[],可以设置默认值进行后续处理

dict.getlist('键',默认值)
  • 查询字符串Query String

获取请求路径中的查询字符串参数(形如?k1=v1&k2=v2),可以通过request.GET属性获取,返回QueryDict对象。

# /qs/?a=1&b=2&a=3

def qs(request):
    a = request.GET.get('a')
    b = request.GET.get('b')
    alist = request.GET.getlist('a')
    print(a)  # 3
    print(b)  # 2
    print(alist)  # ['1', '3']
    return HttpResponse('OK')

重要:查询字符串不区分请求方式,即假使客户端进行POST方式的请求,依然可以通过request.GET获取请求中的查询字符串数据。

  • 请求体

请求体数据格式不固定,可以是表单类型字符串,可以是JSON字符串,可以是XML字符串,应区别对待。

可以发送请求体数据的请求方式有POSTPUTPATCHDELETE

Django默认开启了CSRF防护,会对上述请求方式进行CSRF防护验证,在测试时可以关闭CSRF防护机制,方法为在settings.py文件中注释掉CSRF中间件,如:

​ 1) 表单类型 Form Data

前端发送的表单类型的请求体数据,可以通过request.POST属性获取,返回QueryDict对象。

def get_body(request):
    a = request.POST.get('a')
    b = request.POST.get('b')
    alist = request.POST.getlist('a')
    print(a)
    print(b)
    print(alist)
    return HttpResponse('OK')

重要:request.POST只能用来获取POST方式的请求体表单数据。

​ 2)非表单类型 Non-Form Data

非表单类型的请求体数据,Django无法自动解析,可以通过request.body属性获取最原始的请求体数据,自己按照请求体格式(JSON、XML等)进行解析。request.body返回bytes类型。

例如要获取请求体中的如下JSON数据

{"a": 1, "b": 2}

可以进行如下方法操作:

import json

def get_body_json(request):
    json_str = request.body
    json_str = json_str.decode()  # python3.6 无需执行此步
    req_data = json.loads(json_str)
    print(req_data['a'])
    print(req_data['b'])
    return HttpResponse('OK')
  • 请求头

可以通过request.META属性获取请求头headers中的数据,request.META为字典类型

具体使用如:

def get_headers(request):
    print(request.META['CONTENT_TYPE'])
    return HttpResponse('OK')
  • 其他常用HttpRequest对象属性
    • method:一个字符串,表示请求使用的HTTP方法,常用值包括:‘GET’、‘POST’。
    • user:请求的用户对象。
    • path:一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
    • encoding:一个字符串,表示提交的数据的编码方式。
      • 如果为None则表示使用浏览器的默认设置,一般为utf-8。
      • 这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值。
    • FILES:一个类似于字典的对象,包含所有的上传文件。

11. HttpResponse

#第一种方式设置响应对象
HttpResponse(content=响应体, content_type=响应体数据类型, status=状态码)
content_type='Application/json' / 'image/jpg'

#第二种方式设置响应对象
response = HttpResponse()
response.content='data'
response.status_code = 200/300/400/500
  • 响应头设置
response = HttpResponse()
response['food1'] = 'hotdrynoodles'  # 自定义响应头food1, 值为hotdrynoodles
  • JsonResponse
    • 帮助我们将数据转换为json字符串
    • 设置响应头Content-Typeapplication/json
from django.http import JsonResponse

def demo_view(request):
    return JsonResponse({'city': 'beijing', 'subject': 'python'},status=xxxx)
  • redirect重定向
from django.shortcuts import redirect

def demo_view(request):
    return redirect(reverse('users:say'))

12.cookie

Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份、进行session跟踪而储存在用户本地终端上的数据(通常经过加密)。Cookie是由服务器端生成,发送给User-Agent(一般是浏览器),浏览器会将Cookie的key/value保存到某个目录下的文本文件内,下次请求同一网站时就发送该Cookie给服务器(前提是浏览器设置为启用cookie)。Cookies最典型记住用户名。Cookie是存储在浏览器中的一段纯文本信息

Cookie的特点
  • Cookie以键值对Key-Value形势进行信息的存储。
  • Cookie基于域名安全,不同域名的Cookie是不能互相访问的
设置Cookie

可以通过HttpResponse对象中的set_cookie方法来设置cookie。

HttpResponse.set_cookie(cookie名, value=cookie值, max_age=cookie有效期)
  • max_age 单位为秒,默认为None。如果是临时cookie,可将max_age设置为None。
读取Cookie

可以通过HttpRequest对象的COOKIES属性来读取本次请求携带的cookie值。request.COOKIES为字典类型

13.session

  • session的作用

Session 的作用就是它在 Web服务器上保持用户的状态信息供在任何时间从任何设备上的页面进行访问。因为浏览器不需要存储任何这种信息,所以可以使用任何浏览器,即使是像 Pad 或手机这样的浏览器设备。保持会话状态!

  • 特点
  1. 依赖cookies
  2. 存储敏感、重要的信息
  3. 支持更多字节
  4. Session共享问题
  • 与cookie的区别

    1)cookie以文本文件格式存储在浏览器中,而session存储在服务端它

    2)cookie的存储限制了数据量,只允许4KB,而session是无限量的

    3)我们可以轻松访问cookie值但是我们无法轻松访问会话值,因此它更安全

    4)设置cookie时间可以使cookie过期。使用session-destory(),我们将会销毁会话。

  • session操作

    • 以键值对的格式创建session。

        request.session['键']=
    • 根据键读取值。

        request.session.get('键',默认值) #如果value为none,则返回默认值
      
    • 删除所有session的值

        request.session.clear()
      
    • 清除session数据,在存储中删除session的所有数据。

      request.session.flush()
      
    • 删除session中的指定键及值,在存储中只删除某个键及对应的值。

      del request.session['键']
      
    • 设置session的有效期

      request.session.set_expiry(value)
      

Django类视图的说明详解

  • 为什么需要类视图

    • 以函数的方式定义的视图称为函数视图,函数视图便于理解。但是遇到一个视图对应的路径提供了多种不同HTTP请求方式的支持时,便需要在一个函数中编写不同的业务逻辑,代码可读性与复用性都不佳。
    • 类视图的好处:
      • 代码可读性好
      • 类视图相对于函数视图有更高的复用性, 如果其他地方需要用到某个类视图的某个特定逻辑,直接继承该类视图即可
  • 类视图使用装饰器

    • 在url中添加装饰器(此方法会为类视图的所有方法添加装饰器)

    • 在类视图中添加装饰器

      • 在类视图中使用为函数视图准备的装饰器时,不能直接添加装饰器,需要使用method_decorator将其转换为适用于类视图方法的装饰器。

      • method_decorator装饰器使用name参数指明被装饰的方法

      • # 为全部请求方法添加装饰器
        @method_decorator(my_decorator, name='dispatch')
        
      • # 为特定请求方法添加装饰器
        @method_decorator(my_decorator, name='get')
        
      • @method_decorator(my_decorator)  # 为get方法添加了装饰器,其他方法同理
        def get(self, request):
            pass
        
  • 类视图mixin扩展类

    • 使用面向对象多继承的特性,可以通过定义父类(作为扩展类),在父类中定义想要向类视图补充的方法,类视图继承这些扩展父类,便可实现代码复用
中间件
  • Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理过程,修改Django的输入或输出.

  • 定义一个中间件工厂函数,然后返回一个可以被调用的中间件。

  • 中间件工厂函数需要接收一个可以调用的get_response对象。

  • 返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象。

    def simple_middleware(get_response):
        # 此处编写的代码仅在Django第一次配置和初始化的时候执行一次。
    
        def middleware(request):
            # 此处编写的代码会在每个请求处理视图前被调用。
    
            response = get_response(request)
    
            # 此处编写的代码会在每个请求处理视图之后被调用。
    
            return response
    
        return middleware
    
  • 多个中间件的执行流程

    • 在请求视图被处理,中间件由上至下依次执行
    • 在请求视图被处理,中间件由下至上依次执行
模板
  • 模板变量

    • 模板变量可以使python的内建类型,也可以是对象。

      • {{变量}}
        
  • 模板语句

  • for循环

  •     {% for item in 列表 %}
    循环逻辑
        {{forloop.counter}}表示当前是第几次循环,从1开始
    {%empty%} 列表为空或不存在时执行此逻辑
        {% endfor %}  # 结尾语句不能丢
    
if条件
      {% if ... %}
    逻辑1
      {% elif ... %}
      逻辑2
      {% else %}
      逻辑3
      {% endif %}    # 结尾语句不能丢
  • 运算符

    • 运算符左右两侧不能紧挨变量或常量,必须有空格。

    • {% if a == 1 %}  # 正确
      {% if a==1 %}  # 错误
      
过滤器
  • 语法如下:

    • 使用管道符号|来应用过滤器,用于进行计算、转换操作,可以使用在变量、标签中。
    • 如果过滤器需要参数,则使用冒号:传递参数。
    • 变量|过滤器:参数
  • 自带过滤器

    • safe,禁用转义,告诉模板这个变量是安全的,可以解释执行

    • length,长度,返回字符串包含字符的个数,或列表、元组、字典的元素个数。

    • default,默认值,如果变量不存在时则返回默认值。

      • data|default:'默认值'
        
自定义过滤器
  • 1: 在自己的app里建一个templatetags包,在包里创建一个后面要在HTML文件引用的py文件,

  • 2:在filter_age.py文件中,先导入from django import template ,

    • #实例化对象
      register = template.Library()
      #创建一个template能认识的函数,对创建的每一个过滤器,都要用加上装饰器
      @register.fitler 
      def multi_age(x):
          return x * x
      
  • 在HTML中的引用

    • 在文件开头导入

      • {% load filter_age %}
        
    • 使用过滤器

      • {{ age | multi_age }}
        
注释
  • 单行注释语法如下:
{#...#}
  • 多行注释使用comment标签,语法如下:
{% comment %}
...
{% endcomment %}
数据库
  • ORM框架

    O是object,也就类对象的意思,R是relation,翻译成中文是关系,也就是关系数据库中数据表的意思,M是mapping,是映射的意思。在ORM框架中,它帮我们把类和数据表进行了一个映射,可以让我们通过类和类对象就能操作它所对应的表格中的数据。ORM框架还有一个功能,它可以根据我们设计的类自动帮我们生成数据库中的表格,省去了我们自己建表的过程。

  • 使用django进行数据库开发的步骤如下:

    1. 配置数据库连接信息

      1. 使用MySQL数据库首先需要安装驱动程序

        pip install PyMySQL
        
      2. 在Django的工程同名子目录的__init__.py文件中添加如下语句

        from pymysql import install_as_MySQLdb
        
        install_as_MySQLdb()
        

        作用是让Django的ORM能以mysqldb的方式来调用PyMySQL。

      3. 修改DATABASES配置信息

        #DATABASES = {
        #   'default': {
        #      'ENGINE': 'django.db.backends.sqlite3',
        #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        #}
        #}
        DATABASES = {
            'default': {
                'ENGINE': 'django.db.backends.mysql',
                'HOST': '127.0.0.1',  # 数据库主机
                'PORT': 3306,  # 数据库端口
                'USER': 'root',  # 数据库用户名
                'PASSWORD': 'mysql',  # 数据库用户密码
                'NAME': 'django_demo'  # 数据库名字
            }
        }
        
      4. 在MySQL中创建数据库

        create database django_demo default charset=utf8;
        
    2. 在models.py中定义模型类

      1) 数据库表名

      模型类如果未指明表名,Django默认以 小写app应用名_小写模型类名 为数据库表名。

      可通过db_table 指明数据库表名。

      2) 关于主键

      django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。

      默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。

      3) 属性命名限制

      • 不能是python的保留关键字。

      • 不允许使用连续的下划线,这是由django的查询方式决定的。

      • 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:

        属性=models.字段类型(选项)
        

      4)字段类型

      类型说明
      AutoField自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性
      BooleanField布尔字段,值为True或False
      NullBooleanField支持Null、True、False三种值
      CharField字符串,参数max_length表示最大字符个数
      TextField大文本字段,一般超过4000个字符时使用
      IntegerField整数
      DecimalField十进制浮点数, 参数max_digits表示总位数, 参数decimal_places表示小数位数
      FloatField浮点数
      DateField日期, 参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False; 参数auto_now_add和auto_now是相互排斥的,组合将会发生错误
      TimeField时间,参数同DateField
      DateTimeField日期时间,参数同DateField
      FileField上传文件字段
      ImageField继承于FileField,对上传的内容进行校验,确保是有效的图片

      5) 选项

      选项说明
      null如果为True,表示允许为空,默认值是False
      blank如果为True,则该字段允许为空白,默认值是False
      db_column字段的名称,如果未指定,则使用属性的名称
      db_index若值为True, 则在表中会为此字段创建索引,默认值是False
      default默认
      primary_key若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用
      unique如果为True, 这个字段在表中必须有唯一值,默认值是False

      null是数据库范畴的概念,blank是表单验证范畴的

      6) 外键

      在设置外键时,需要通过on_delete选项指明主表删除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量:

      • CASCADE 级联,删除主表数据时连通一起删除外键表中数据

      • PROTECT 保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据

      • SET_NULL 设置为NULL,仅在该字段null=True允许为null时可用

      • SET_DEFAULT 设置为默认值,仅在该字段设置了默认值时可用

      • SET() 设置为特定值或者调用特定方法,如

        from django.conf import settings
        from django.contrib.auth import get_user_model
        from django.db import models
        
        def get_sentinel_user():
            return get_user_model().objects.get_or_create(username='deleted')[0]
        
        class MyModel(models.Model):
            user = models.ForeignKey(
                settings.AUTH_USER_MODEL,
                on_delete=models.SET(get_sentinel_user),
            )
        
      • DO_NOTHING 不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常

    3. 迁移

      1)生成迁移文件

      python manage.py makemigrations
      

      2)同步到数据库中

      python manage.py migrate
      
  • 数据库操作

  • 查看数据库日志

    • 查看mysql数据库日志可以查看对数据库的操作记录。 mysql日志文件默认没有产生,需要做如下配置:

      sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
      # 添加一下代码
      general_log_file = /var/log/mysql/mysql.log
      general_log = 1
      
      # 重启服务
      sudo service mysql restart
      

      使用如下命令打开mysql日志文件。

      tail -f /var/log/mysql/mysql.log  # 可以实时查看数据库的日志内容
      # 如提示需要sudo权限,执行
      # sudo tail -f /var/log/mysql/mysql.log
      
  • 查询集QuerySet

    • QuerySet是Django提供的强大的数据库接口(API),正是因为通过它,我们可以使用filter, exclude, get等方法进行数据库查询,而不需要使用原始的SQL语言与数据库进行交互。从数据库中查询出来的结果一般是一个集合,这个集合叫就做 queryset查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合。

      当调用如下过滤器方法时,Django会返回查询集(而不是简单的列表):

      • all():返回所有数据。
      • filter():返回满足条件的数据。
      • exclude():返回满足条件之外的数据。
      • order_by():对结果进行排序。
    • 特性

      • 惰性执行
        • 创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用
      • 缓存
        • 当你遍历查询出来的queryset时,所有匹配的记录会从数据库获取。这些结果会载入内存并保存在queryset内置的cache中。这样如果你再次遍历或读取这个queryset时,Django就不需要重复查询了,这样也可以减少对数据库的查询
  • 管理器manager

    • 管理器是Django的模型进行数据库操作的接口,Django应用的每个模型类都拥有至少一个管理器。我们在通过模型类的objects属性提供的方法操作数据库时,即是在使用一个管理器对象objects。当没有为模型类定义管理器时,Django会为每一个模型类生成一个名为objects的管理器,它是models.Manager类的对象
Admin站点
  • 使用Django的管理模块,需要按照如下步骤操作:
    1. 管理界面本地化
    2. 创建管理员
    3. 注册模型类
    4. 自定义管理页面
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值