Part17

本秂侑毒 提交于 2020-04-28 07:01:43

本节内容

  1. Web框架本质
  2. Django 基础
  3. Django 进阶

一、Web框架本质

对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端。
WEB框架本质是一个socket
#########WEB框架(代码文件结构)
MVC
    Model        View        Controller
    数据库       模板文件    业务处理

MTV
    Model        Template    View
    数据库        模板文件    业务处理

############## WEB:MVC、MTV

1、本质

import socket

#WEB框架本质-->socket

def handle_request(client):
    buf = client.recv(1024)
    client.send(b"HTTP/1.1 200 OK\r\n\r\n")
    client.send(b"Hello, Seven")


def main():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8000))
    sock.listen(5)

    while True:
        connection, address = sock.accept()
        handle_request(connection)
        connection.close()

if __name__ == '__main__':
    main()

2、自定义Web框架

WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。 Python标准库提供的独立WSGI服务器称为wsgiref(封装好的socket)。
通过Python标准库提供的wsgiref模块开发一个自己的Web框架。
 1 from wsgiref.simple_server import make_server
 2 from Controller import accout
 3 
 4 URL_DICT = {
 5         '/index': accout.handle_index,
 6         '/data': accout.handle_data,
 7     } #可以做正则匹配,匹配一类的请求内容
 8 
 9 def RunServer(environ, start_response):
10     # environ 客户端发来的所有数据
11     # start_response 封装要返回给用户的数据,响应头状态
12     start_response('200 OK', [('Content-Type', 'text/html')])
13     current_url = environ['PATH_INFO']
14 
15     # 返回的内容
16     func = None
17     if current_url in URL_DICT:
18         func = URL_DICT[current_url]
19         if func:
20             return func()
21         else:
22             return ['<h1>404</h1>'.encode('utf-8'), ]
23     else:
24         return ['<h1>404</h1>'.encode('utf-8'), ]
25 
26     # if current_url == '/index':
27     #     return handle_index()
28     # elif current_url == '/data':
29     #     return handle_data()
30     # else:
31     #     return ['<h1>404</h1>'.encode('utf-8'), ]
32 
33     # return ['<h1>Hello, web!</h1>'.encode('utf-8'),]
34 
35 if __name__ == '__main__':
36     httpd = make_server('', 8000, RunServer)
37     print("Serving HTTP on port 8000...")
38     httpd.serve_forever()
WSGI

二、Django基础

Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。
Django生存周期:

  1 Django:用Python开发Web站点提供最齐全的Web框架,基于MTV的框架
  2 
  3 pip3 install django
  4 
  5 
  6 C:\Python35\Scripts
  7 
  8 1、创建Django工程(可以使用Pycharm创建)
  9     django-admin startproject 【工程名称】        -->  会在当前目录下生成Django工程目录
 10     如:django-admin startproject mysite
 11         默认创建如下内容
 12         mysite   
 13             - mysite        # 对整个程序进行配置
 14                 - init
 15                 - settings  # 配置文件
 16                 - url       # URL对应关系
 17                 - wsgi      # 遵循WSIG规范,uwsgi + nginx(Django内部没有包含socket)
 18                 
 19             - manage.py     # 管理Django程序:
 20                 - python manage.py 
 21                 - python manage.py startapp xx
 22                 - python manage.py makemigrations
 23                 - python manage.py migrate
 24 
 25     PS:    WSGI    (Web Server Gateway Interface)
 26         是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。python标准库提供的独立WSGI服务器称为wsgiref。
 27     
 28     
 29 2、运行Django功能(可使用Pycharm运行,添加Djangoserver)
 30     python manage.py runserver 127.0.0.1:8001
 31 
 32     ------------------
 33     chouti
 34         - chouti
 35             - 配置
 36         - 主站 app
 37         - 后台管理 app
 38     ------------------
 39 
 40 
 41 3、创建app(进入Django工程根目录,执行如下命令)
 42     python manage.py startapp cmdb
 43     python manage.py startapp openstack
 44     python manage.py startapp xxoo....
 45     
 46     业务处理代码放入app中(views)
 47     app:
 48         migrations     修改表结构记录
 49         admin          Django为我们提供的后台管理
 50         apps           配置当前app
 51         models         ORM,写指定的类  通过命令可以创建数据库结构
 52         tests          单元测试
 53         views          业务代码
 54 
 55 4、配置(project.settings.py)
 56 
 57     a、配置模板的路径
 58     
 59         TEMPLATES = [
 60                 {
 61                     'BACKEND': 'django.template.backends.django.DjangoTemplates',
 62                     
 63                     'DIRS': [os.path.join(BASE_DIR, 'templates')],
 64                     
 65                     'APP_DIRS': True,
 66                     'OPTIONS': {
 67                         'context_processors': [
 68                             'django.template.context_processors.debug',
 69                             'django.template.context_processors.request',
 70                             'django.contrib.auth.context_processors.auth',
 71                             'django.contrib.messages.context_processors.messages',
 72                         ],
 73                     },
 74                 },
 75             ]
 76             
 77     b、配置静态目录(不要忘记逗号)
 78         static        -->  静态文件目录名字使用static
 79     
 80         STATICFILES_DIRS = (
 81             os.path.join(BASE_DIR, 'static'),
 82         )
 83 
 84         <link rel="stylesheet" href="/static/commons.css" />
 85         
 86         
 87     c、CSRF verification failed. Request aborted 报错解决(settings文件内注释csrf)
 88     
 89         MIDDLEWARE = [
 90         'django.middleware.security.SecurityMiddleware',
 91         'django.contrib.sessions.middleware.SessionMiddleware',
 92         'django.middleware.common.CommonMiddleware',
 93         #'django.middleware.csrf.CsrfViewMiddleware',
 94         'django.contrib.auth.middleware.AuthenticationMiddleware',
 95         'django.contrib.messages.middleware.MessageMiddleware',
 96         'django.middleware.clickjacking.XFrameOptionsMiddleware',
 97         ]
 98         
 99 5、定义路由规则
100     url.py
101     
102         "login" --> 函数名
103         
104         
105 6、定义视图函数
106     app下views.py
107         
108         def func(request):
109             # request.method   GET / POST        -- 用户提交方式
110             
111             # http://127.0.0.1:8009/home?nid=123&name=alex
112             # request.GET.get('',None)   # 获取请求发来的而数据
113             
114             # request.POST.get('',None)
115                             
116             # return HttpResponse("字符串")        --    返回字符串
117             # return render(request, "HTML模板的路径")    -- 返回给用户模板
118             # return redirect('/只能填URL')        -- 跳转,只能填'/URL'
119             
120 7、模板渲染
121     特殊的模板语言
122     
123     a、{{ 变量名 }}
124         
125         视图函数:
126         def func(request):
127             return render(request, "index.html", {'current_user': "alex"})
128 
129             
130         模板:index.html
131         -----------
132         <html>
133         ..
134             <body>
135                 <div>{{current_user}}</div>
136             </body>
137         
138         </html>
139         
140         ====> 最后生成的字符串
141         
142         <html>
143         ..
144             <body>
145                 <div>alex</div>
146             </body>
147         
148         </html>
149         ------------
150         
151     b、For循环
152         def func(request):
153             return render(request, "index.html", {'current_user': "alex", 'user_list': ['alex','eric']})
154 
155             
156         index.html
157         ------------
158         <html>
159         ..
160             <body>
161                 <div>{{current_user}}</div>
162                 
163                 <ul>
164                     {% for row in user_list %}
165                     
166                         {% if row == "alex" %}
167                             <li>{{ row }}</li>
168                         {% endif %}
169                         
170                     {% endfor %}
171                 </ul>
172                 
173             </body>
174         
175         </html>
176         ------------
177         
178         
179     c、索引################# 
180         def func(request):
181             return render(request, "index.html", {
182                         'current_user': "alex", 
183                         'user_list': ['alex','eric'], 
184                         'user_dict': {'k1': 'v1', 'k2': 'v2'}})
185 
186             
187         index.html
188         
189         <html>
190         ..
191             <body>
192                 <div>{{current_user}}</div>
193                 
194                 <a> {{ user_list.1 }} </a>        #模板语言中根据索引获取列表内容
195                 <a> {{ user_dict.k1 }} </a>        #模板语言中根据索引获取字典内容
196                 <a> {{ user_dict.k2 }} </a>
197                 
198             </body>
199         
200         </html>
201     
202     
203     d、条件 ###################
204     
205         def func(request):
206             return render(request, "index.html", {
207                         'current_user': "alex", 
208                         "age": 18,
209                         'user_list': ['alex','eric'], 
210                         'user_dict': {'k1': 'v1', 'k2': 'v2'}})
211 
212             
213         index.html
214         
215         <html>
216         ..
217             <body>
218                 <div>{{current_user}}</div>
219                 
220                 <a> {{ user_list.1 }} </a>
221                 <a> {{ user_dict.k1 }} </a>
222                 <a> {{ user_dict.k2 }} </a>
223                 
224                 {% if age %}
225                     <a>有年龄</a>
226                     {% if age > 16 %}
227                         <a>老男人</a>
228                     {% else %}
229                         <a>小鲜肉</a>
230                     {% endif %}
231                 {% else %}
232                     <a>无年龄</a>
233                 {% endif %}
234             </body>
235         
236         </html>
Django笔记

1、创建Django工程

可以使用Django在命令行中创建工程,也可以使用Pycharm创建(本质上自动执行了创建命令)
 1 django-admin startproject 【工程名称】
 2 如:django-admin startproject mysite
 3     默认创建如下内容
 4     - mysite      # 工程
 5         - mysite           # 对整个程序进行配置
 6             - __init__.py
 7             - settings.py  # 配置文件
 8             - urls.py      # URL对应关系
 9             - wsgi.py      # 遵循WSIG规范,wsgi + nginx(Django内部没有包含socket)
10             
11         - manage.py     # 管理Django程序:
12             - python manage.py 
13             - python manage.py startapp xx
14             - python manage.py makemigrations
15             - python manage.py migrate
16 
17 PS:   WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。python标准库提供的独立WSGI服务器称为wsgiref。
View Code

2、运行Django功能

可以在命令行中启动,也可以使用Pycharm创建Django server来运行(在Pycharm中创建的Django工程默认添加了Django server)
 1 python manage.py runserver 127.0.0.1:8001
 2 
 3 ------------------
 4 chouti
 5     - chouti
 6         - 配置
 7     - 主站 app
 8     - 后台管理 app
 9 ------------------
10 
11 PS:Python和Django要加环境变量
在命令行中启动

3、创建app

进入Django工程根目录,执行如下命令
 1 python manage.py startapp cmdb
 2 python manage.py startapp openstack
 3 python manage.py startapp xxoo....
 4 
 5 业务处理代码放入app中(views)
 6 app:
 7     migrations     修改表结构记录
 8     admin          Django为我们提供的后台管理
 9     apps           配置当前app
10     models         ORM,写指定的类  通过命令可以创建数据库结构
11     tests          单元测试
12     views          业务代码
View Code

4、配置文件

"""
Django settings for s14django project.

Generated by 'django-admin startproject' using Django 1.10.4.

For more information on this file, see
https://docs.djangoproject.com/en/1.10/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.10/ref/settings/
"""

import os

# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'hut(bs^o50n^opj8yneen_sh)m0uvu#sdzr%&eq1x)5p*(tqcz'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'cmdb',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    #'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 's14django.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 's14django.wsgi.application'


# Database
# https://docs.djangoproject.com/en/1.10/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}


# Password validation
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.10/howto/static-files/

STATIC_URL = '/static/'

# 配置好static静态文件目录,不配置用户访问页面静态文件访问不到
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

# APPEND_SLASH=True
settings.py
1) 配置HTML模板的路径
TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')],
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
View Code
2) 配置静态目录
static        -->  静态文件目录名字使用static

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

<link rel="stylesheet" href="/static/commons.css" />
CSS、JS、图片等静态文件目录
3) CSRF verification failed. Request aborted 报错解决
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
#'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
settings内注释csrf
4) 数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
默认sqllite
DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME':'dbname',
    'USER': 'root',
    'PASSWORD': 'xxx',
    'HOST': '',
    'PORT': '',
    }
}

# 注意:
# 由于Django内部连接MySQL时使用的是MySQLdb模块,而Python3中还无此模块,所以需要使用pymysql模块来代替。
  
# 在project下与其同名文件夹下的 __init__.py文件中做如下设置
  
import pymysql
pymysql.install_as_MySQLdb()
MySQL配置

5、路由规则

定义url与业务处理函数(视图函数)之间的映射关系。
 1 URL拼接传递参数有两种方式:    
 2     - 在url后加"?key=value"拼接,可以在视图函数中通过get()获取,如:
 3         html文件
 4             <a class="detail" href="/index?nid=1">详细信息</a>    #id是内置方法,尽量不要使用id做参数
 5         ---
 6         urls.py文件
 7             url(r'^index/', views.index),    #一个url对应函数
 8             url(r'^home/', views.Home.as_view()),    #一个url对应类,根据method通过反射来执行对应方法
 9         
10             
11     - 在url后加"分隔符value"拼接,在urls.py中通过正则进行匹配(常用方法),如:
12         html文件
13             <a class="detail" href="/detail-2-9.html">详细信息</a>
14         ---    
15         urls.py文件
16             a、一类url对应函数,"正则匹配"的数据(\d+)交给视图函数(视图函数参数有位置要求) 
17                 url(r'^detail-(\d+)-(\d+).html', views.detail),    
18             
19                 def func(request, nid, uid):    #nid即正则匹配的数据(\d+)
20                     pass        
21                 def func(request, *args):
22                     args = (2,9)
23                 def func(request, *args, **kwargs):
24                     args = (2,9)
25                     
26                     
27             b、"正则匹配"的数据"分组"(?P<nid>\d+)交给视图函数(视图函数参数没有位置要求) 
28                 url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)
29                 
30                 def func(request, nid, uid):
31                     pass
32                     
33                 def funct(request, **kwargs):
34                     kwargs = {'nid': 1, 'uid': 3}
35                     
36                 def func(request, *args, **kwargs):
37                     args = (2,9)
38    
39    
40     - PS:
41         def detail(request, *args,**kwargs):
42             pass
路由系统笔记
 1 """s14django URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.contrib import admin
18 from cmdb import views
19 
20 urlpatterns = [
21     url(r'^admin/', admin.site.urls),
22     # url(r'^h.html/', views.home),
23     url(r'^login', views.login),
24     url(r'^home', views.home),
25 ]
urls.py
1) 单一路由对应
from django.conf.urls import url

urlpatterns = [
    url(r'^index/', views.index),    #一个url对应函数
    url(r'^home/', views.Home.as_view()),    #一个url对应类,根据method通过反射来执行对应方法
]
urls.py
from django.views import View
class Home(View):
    #方法名必须小写,支持的方法见源码
    def dispatch(self, request, *args, **kwargs):
        # 调用父类中的dispatch,相当于装饰器功能
        print('before')
        result = super(Home,self).dispatch(request, *args, **kwargs)
        print('after')
        return result

    def get(self,request):
        print(request.method)
        return render(request, 'home.html')

    def post(self,request):
        print(request.method,'POST')
        return render(request, 'home.html')
views.py(一个url对应一个类)
2) 基于正则的路由
 1 html文件
 2     <a class="detail" href="/detail-2-9.html">详细信息</a>
 3 ---    
 4 urls.py文件
 5     a、一类url对应函数,"正则匹配"的数据(\d+)交给视图函数(视图函数参数有位置要求) 
 6         url(r'^detail-(\d+)-(\d+).html', views.detail),    
 7     
 8         def func(request, nid, uid):    #nid即正则匹配的数据(\d+)
 9             pass        
10         def func(request, *args):
11             args = (2,9)
12         def func(request, *args, **kwargs):
13             args = (2,9)
14             
15             
16     b、"正则匹配"的数据"分组"(?P<nid>\d+)交给视图函数(视图函数参数没有位置要求) 
17         url(r'^detail-(?P<nid>\d+)-(?P<uid>\d+).html', views.detail)
18         
19         def func(request, nid, uid):
20             pass
21             
22         def funct(request, **kwargs):
23             kwargs = {'nid': 1, 'uid': 3}
24             
25         def func(request, *args, **kwargs):
26             args = (2,9)
View Code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        body{
            margin: 0;
        }
        .menu{
            display: block;
            padding: 5px;

        }
    </style>
</head>
<body>
    <div style="height: 48px;background-color: black;color: white">
        红红火火哈哈
    </div>
    <div>
        <div style="position: absolute;top:48px;bottom: 0;left: 0;width: 200px;background-color: brown;">
            <a class="menu" href="/cmdb/user_info/">用户管理</a>
            <a class="menu" href="/cmdb/user_group/">用户组管理</a>
        </div>
        <div style="position:absolute;top:48px;left: 210px;bottom: 0;right: 0;overflow: auto">
            <h3>添加用户</h3>
            <form method="POST" action="/cmdb/user_info/">
                <input type="text" name="user" />
                <input type="text" name="pwd" />
                <select name="group_id">
                    {% for item in group_list %}
                        <option value="{{ item.uid }}">{{ item.caption }}</option>
                    {% endfor %}
                </select>
                <input type="submit" value="添加"/>
            </form>
            <h3>用户列表</h3>
            <ul>
                {% for row in user_list %}
                    <li>
                        <a href="/cmdb/userdetail-{{ row.id }}/">{{ row.username }}</a> |
                        <span> {{ row.user_group.caption }} </span>
                        <a href="/cmdb/userdel-{{ row.id }}/">删除</a> |
                        <a href="/cmdb/useredit-{{ row.id }}/">编辑</a>
                    </li>
                {% endfor %}
            </ul>
        </div>
    </div>
</body>
</html>
示例HTML
3) 多级路由(include),根据app对路由规则进行分类
#project/urls.py
from django.conf.urls import url,include
from django.contrib import admin

urlpatterns = [
    url(r'^cmdb/', include("app01.urls")),
    url(r'^monitor/', include("app02.urls")),
]


#app01/urls.py
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^login/', views.login),
]


#app02/urls.py
from django.conf.urls import url,include
from django.contrib import admin
from app02 import views

urlpatterns = [
    url(r'^login/', views.login),
]
View Code
4) 默认值

 

5) 命名空间

 

6、视图函数

处理用户请求函数,app目录下的views.py
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#上传文件form做处理enctype="multipart/form-data"#}
    <form action="/login/" method="POST" enctype="multipart/form-data">
        <p>
            <input type="text" name="user" placeholder="用户名" />
        </p>
        <p>
            <input type="password" name="pwd" placeholder="密码" />
        </p>
        <p>
            男:<input type="radio"  name="gender" value="1"/>
            女:<input type="radio" name="gender" value="2"/>
        </p>
        <p>
            男:<input type="checkbox"  name="favor" value="11"/>
            女:<input type="checkbox" name="favor" value="22"/>
        </p>
        <p>
            <select name="city" multiple>
                <option value="sh">上海</option>
                <option value="bj">北京</option>
                <option value="tj">天津</option>
            </select>
        </p>
        <p>
            <input type="file" name="fafafa"/>
        </p>

        <input type="submit" value="提交"/>
    </form>
</body>
</html>
示例HTML
1) 基本用法(一个url对应一个函数,一个url对应类的参照5-1)
 1 app下views.py
 2     
 3     def func(request):
 4         # request.method   GET / POST        -- 用户提交方式
 5         
 6         # http://127.0.0.1:8009/home?nid=123&name=alex
 7         # request.GET.get('',None)   # 获取请求发来的而数据
 8         
 9         # request.POST.get('',None)
10                         
11         # return HttpResponse("字符串")        --    返回字符串
12         # return render(request, "HTML模板的路径")    -- 返回给用户模板
13         # return redirect('/只能填URL')        -- 跳转,只能填'/URL'
View Code
 1 from django.shortcuts import render
 2 
 3 # Create your views here.
 4 from django.shortcuts import HttpResponse
 5 from django.shortcuts import render
 6 from django.shortcuts import redirect
 7 
 8 def login(request):
 9     # 包含用户提交的所有信息
10     # print(request.method)  # 获取用户提交方法
11     error_msg = ""
12     #如果请求提交的方法是POST(用户提交的数据)
13     if request.method == "POST":
14         # 获取用户通过POST提交过来的数据(用户提交的数据类似于字典)
15         user = request.POST.get('user',None)    #get('user',None)获取user内容,默认为None
16         pwd = request.POST.get('pwd',None)
17         if user == 'root' and pwd == "123":
18             # 去跳转到
19             return redirect('/login')   #redirect重定向、跳转
20         else:
21             # 用户密码不配
22             error_msg = "用户名或密码错误"
23     #打开页面并返回给用户,settings文件中配置了html文件的路径TEMPLATES('DIRS': [os.path.join(BASE_DIR, 'templates')])
24     return render(request,'login.html', {'error_msg': error_msg})   #render找到模板返回给用户
25 
26 USER_LIST = [
27     {'id': 1, 'username': 'alex', 'email': 'asdfasdf', "gender": ''},
28     {'id': 2, 'username': 'eriuc', 'email': 'asdfasdf', "gender": ''},
29     {"id": 3,'username': 'seven', 'email': 'asdfasdf', "gender": ''},
30 ]
31 
32 def home(request):
33     print(request.GET.get('nid'))
34 
35     if request.method == "POST":
36         # 获取用户提交的数据 POST请求中
37         u = request.POST.get('username')
38         e = request.POST.get('email')
39         g = request.POST.get('gender')
40         temp = {'username': u, 'email': e, "gender": g}
41         USER_LIST.append(temp)
42     return render(request, 'test/home.html', {'user_list':  USER_LIST})
43 
44 
45 # def login(request):
46 #     # string = """
47 #     # <form>
48 #     #     <input type='text' />
49 #     # </form>
50 #     #
51 #     # """
52 #     # f = open('templates/login.html', 'r', encoding='utf-8')
53 #     # data = f.read()
54 #     # f.close()
55 #     # return HttpResponse(data)
56 #     return render(request,'login.html')
57 
58 # def home(request):
59 #     return HttpResponse('<h1>CMDB</h1>')
60 
61 # 主机管理
62 # 防火墙
63 # 。。。
views.py
2) 获取用户请求数据
request.GET.get()
request.POST.get()
request.FILES.get()
PS:
    GET:http请求默认使用的method,获取数据                
    POST:可以与GET区分,用来提交数据
3) 获取多选的内容(checkbox等)
request.POST.getlist()
4) 上传文件
import os
from django.shortcuts import render,redirect
from django.core.files.uploadedfile import InMemoryUploadedFile

def login(request):
    if request.method == "GET":
        return render(request, 'login.html')

    elif request.method == "POST":
        # 上传文件,form标签做特殊设置
        obj = request.FILES.get('fafafa')
        print(obj,type(obj),obj.name)
        #文件路径拼接
        file_path = os.path.join('upload', obj.name)
        # 打开文件
        f = open(file_path, mode="wb")
        # obj.chunks()上传文件的块数据
        for i in obj.chunks():
            f.write(i)
        f.close()
     
        return render(request, 'login.html')
    else:
        # PUT,DELETE,HEAD,OPTION...
        return redirect('/index/')
    
views.py
5) FBV&CBV
FBV --> function base view
    url.py
        /index/ -> 函数名
    view.py
        def 函数(request):
            ...
            
CBV --> class base view
    url.py
        /index/ -> 类
        urlpatterns = [
            url(r'^admin/', admin.site.urls),
            url(r'^home/', views.Home.as_view()),    #根据get或post提交方式在类中执行对应的方法
        ]
    view.py
        from django.views import View
        class Home(View):
            def dispatch(self, request, *args, **kwargs):
                # 调用父类中的dispatch
                print('before')
                result = super(Home,self).dispatch(request, *args, **kwargs)
                print('after')
                return result
                
            def get(self,request):
                print(request.method)
                return render(request, 'home.html')
                
            def post(self,request):
                print(request.method,'POST')
                return render(request, 'home.html')
        
        PS:类的方法名一定要小写,源代码中定义好了方法名
====》
建议:两者都用
View Code
6) 装饰器

 

7、模板

templates目录下html
1) 模板的执行
模版的创建过程,其实就是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户。
2) 模板语言
变量:
        {{ item }}
For循环:
        {% for item in item_list %}  {{ item }}  {% endfor %}
            forloop.counter
            forloop.first
            forloop.last 
if语句:        
        {% if ordered_warranty %}  {% else %} {% endif %}
母板:
        {% block title %}{% endblock %}
子板:
        {% extends "base.html" %}
        {% block title %}{% endblock %}    
帮助方法:
        {{ item.event_start|date:"Y-m-d H:i:s"}}
        {{ bio|truncatewords:"30" }}
        {{ my_list|first|upper }}
        {{ name|lower }}
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6     <link rel="stylesheet" href="/static/commons.css" />
 7     <style>
 8         label{
 9             width: 80px;
10             text-align: right;
11             display: inline-block;
12         }
13     </style>
14 </head>
15 <body>
16     {#提交数据给login#}
17     <form action="/login" method="post">
18         <p>
19             {#点击label后input内出现光标#}
20             <label for="username">用户名:</label>
21             <input id="username" name="user" type="text" />
22         </p>
23         <p>
24             <label for="password">密码:</label>
25             <input id="password" name="pwd" type="password" />
26             <input type="submit" value="提交" />
27             {# Django会特殊处理 {{ error_msg }} #}
28             <span style="color: red;">{{ error_msg }}</span>
29         </p>
30     </form>
31     <script src="/static/jquery.min.js"></script>
32 </body>
33 </html>
变量
 1 <!DOCTYPE html>
 2 <html lang="en">
 3 <head>
 4     <meta charset="UTF-8">
 5     <title>Title</title>
 6 </head>
 7 <body style="margin: 0">
 8     <div style="height: 48px;background-color: #dddddd"></div>
 9     <div>
10         <form action="/home" method="post">
11             <input type="text" name="username" placeholder="用户名" />
12             <input type="text" name="email"  placeholder="邮箱"/>
13             <input type="text" name="gender"  placeholder="性别"/>
14             <input type="submit" value="添加" />
15         </form>
16     </div>
17     <div>
18         <table>
19             {# Django模板语言for循环 #}
20             {% for row in user_list %}
21                 <tr>
22                     <td>{{ row.username }}</td>
23                     <td>{{ row.gender }}</td>
24                     <td>{{ row.email }}</td>
25                     <td>
26                         <a href="/detail?nid={{ row.id }}">查看详细</a> |
27                         <a class="del" href="#" row-id="{{ row.id }}">删除</a>
28                     </td>
29                 </tr>
30             {% endfor %}
31 
32         </table>
33     </div>
34     <div>
35         <form action="/del_host" method="post">
36             <input style="display: none" id="nid" type="text" name="nid" />
37             <p>
38                 <input type="submit" />
39                 <input type="botton" />
40             </p>
41         </form>
42     </div>
43     <script>
44         $('.del').click(function(){
45             var row_id = $(this).attr('row-id');
46             //赋值
47             $('#nid').val(row_id);
48         })
49     </script>
50 </body>
51 </html>
For循环
 1 def func(request):
 2     return render(request, "index.html", {
 3                 'current_user': "alex", 
 4                 'user_list': ['alex','eric'], 
 5                 'user_dict': {'k1': 'v1', 'k2': 'v2'}})
 6 
 7     
 8 index.html
 9 
10 <html>
11 ..
12     <body>
13         <div>{{current_user}}</div>
14         
15         <a> {{ user_list.1 }} </a>        #模板语言中根据索引获取列表内容
16         <a> {{ user_dict.k1 }} </a>        #模板语言中根据索引获取字典内容
17         <a> {{ user_dict.k2 }} </a>
18         
19     </body>
20 
21 </html>
数据索引
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    {#根据索引(key)直接获取值#}
    {{ user_dict.k1 }}
    <ul>
        {#循环的key#}
        {% for k in user_dict.keys %}
            <li>{{ k }}</li>
        {% endfor %}
    </ul>
    <ul>
        {#循环的value#}
        {% for val in user_dict.values %}
            <li>{{ val }}</li>
        {% endfor %}
    </ul>
    <ul>
        {#循环的key和value#}
        {% for k,row in user_dict.items %}
            <li>{{ k }}-{{ row }}</li>
        {% endfor %}
    </ul>
</body>
</html>
循环字典
 1 def func(request):
 2     return render(request, "index.html", {
 3                 'current_user': "alex", 
 4                 "age": 18,
 5                 'user_list': ['alex','eric'], 
 6                 'user_dict': {'k1': 'v1', 'k2': 'v2'}})
 7 
 8     
 9 index.html    
10 <html>
11 ..
12     <body>
13         <div>{{current_user}}</div>
14         
15         <a> {{ user_list.1 }} </a>
16         <a> {{ user_dict.k1 }} </a>
17         <a> {{ user_dict.k2 }} </a>
18         
19         {% if age %}
20             <a>有年龄</a>
21             {% if age > 16 %}
22                 <a>老男人</a>
23             {% else %}
24                 <a>小鲜肉</a>
25             {% endif %}
26         {% else %}
27             <a>无年龄</a>
28         {% endif %}
29     </body>
30 
31 </html>
if条件语句
3) 自定义simple_tag

 

8、中间件

 

9、admin

 

三、Django进阶

Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。

1、Model

django使用ORM方式处理数据库相关操作,ORM即,关系对象映射(Object Relational Mapping)。
  PHP:activerecord
  Java:Hibernate 
  C#:Entity Framework
django中遵循 Code Frist 的原则,即,根据代码中定义的类来自动生成数据库表。注意原生SQL与ORM的对应关系。
1) 创建表
*基本结构
a. 先写类
    from django.db import models

    # app01_userinfo
    class UserInfo(models.Model):
        # id列,自增,主键
        # 用户名列,字符串类型,指定长度
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=64)
    
b. 注册APP
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01',    #settings中添加app名,Django才给app生成数据库
    ]
    
c. 执行命令(同步结构改动到数据库)
    #记录对models.py的改动(表结构变化),并在migrations下生成记录文件
        python manage.py  makemigrations    
    #将改动应用到数据库
        python manage.py  migrate
    注意:缩小列长度要谨慎,超出长度数据会丢失    
    
    
d. ********** 注意 ***********
    Django默认使用MySQLdb模块链接MySQL
    主动修改为pymysql,在project同名文件夹下的__init__文件中添加如下代码即可:
        import pymysql
        pymysql.install_as_MySQLdb()
创建表基本过程
# Create your models here.
# app01_userinfo

from django.db import models

class UserGroup(models.Model):
    uid = models.AutoField(primary_key=True)
    caption = models.CharField(max_length=32,unique=True)
    ctime = models.DateTimeField(auto_now_add=True, null=True)
    uptime = models.DateTimeField(auto_now=True, null=True)

class UserInfo(models.Model):   #必须继承models.Model
    # id列,自增,主键 (Djiango默认创建)
    # 用户名列,字符串类型CharField,指定长度max_length
    # 字符串、数字、时间、二进制
    username = models.CharField(max_length=32,blank=True,verbose_name='用户名')
    password = models.CharField(max_length=60, help_text='pwd')
    email = models.CharField(max_length=60)
    test = models.EmailField(max_length=19,null=True,error_messages={'invalid': '请输入密码'})
    # user_group_id 数字
    user_group = models.ForeignKey("UserGroup",to_field='uid',on_delete=models.CASCADE) # (uid,catption,ctime,uptimew)
    user_type_choices = (
        (1, '超级用户'),
        (2, '普通用户'),
        (3, '普普通用户'),
    )
    user_type_id = models.IntegerField(choices=user_type_choices,default=1)
    
    #不同的字符串类型(...Field),给Django的admin表单认证使用的
    # test = models.URLField(max_length=19,null=True)
    # test = models.GenericIPAddressField()
    # gender = models.CharField(max_length=60, null=True)
models.py
字符串********
    不同的字符串类型,只是给Django的admin表单校验使用的(如自动校验EmailField是否是邮箱格式)
    CharField(Field)
        - 字符类型
        - 必须提供max_length参数, max_length表示字符长度

    TextField(Field)
        - 文本类型

    EmailField(CharField):
        - 字符串类型,Django Admin以及ModelForm中提供验证机制

    IPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制(新版本中废弃,使用GenericIPAddressField)

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both"

    URLField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证 URL

    SlugField(CharField)
        - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

    CommaSeparatedIntegerField(CharField)
        - 字符串类型,格式必须为逗号分割的数字

    UUIDField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证

    FilePathField(Field)
        - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
        - 参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

    FileField(Field)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage

    ImageField(FileField)
        - 字符串,路径保存在数据库,文件上传到指定目录
        - 参数:
            upload_to = ""      上传文件的保存路径
            storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
            width_field=None,   上传图片的高度保存的数据库字段名(字符串)
            height_field=None   上传图片的宽度保存的数据库字段名(字符串)

数字********
    AutoField(Field)
        - int自增列,必须填入参数 primary_key=True

    BigAutoField(AutoField)
        - bigint自增列,必须填入参数 primary_key=True
        
        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        ---
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

    SmallIntegerField(IntegerField):
        - 小整数 -32768 ~ 32767

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

    PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正整数 0 ~ 2147483647

    BigIntegerField(IntegerField):
        - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807

    自定义无符号整数字段
        class UnsignedIntegerField(models.IntegerField):
            def db_type(self, connection):
                return 'integer UNSIGNED'

        PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
            'AutoField': 'integer AUTO_INCREMENT',
            'BigAutoField': 'bigint AUTO_INCREMENT',
            'BinaryField': 'longblob',
            'BooleanField': 'bool',
            'CharField': 'varchar(%(max_length)s)',
            'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
            'DateField': 'date',
            'DateTimeField': 'datetime',
            'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
            'DurationField': 'bigint',
            'FileField': 'varchar(%(max_length)s)',
            'FilePathField': 'varchar(%(max_length)s)',
            'FloatField': 'double precision',
            'IntegerField': 'integer',
            'BigIntegerField': 'bigint',
            'IPAddressField': 'char(15)',
            'GenericIPAddressField': 'char(39)',
            'NullBooleanField': 'bool',
            'OneToOneField': 'integer',
            'PositiveIntegerField': 'integer UNSIGNED',
            'PositiveSmallIntegerField': 'smallint UNSIGNED',
            'SlugField': 'varchar(%(max_length)s)',
            'SmallIntegerField': 'smallint',
            'TextField': 'longtext',
            'TimeField': 'time',
            'UUIDField': 'char(32)',

    BooleanField(Field)
        - 布尔值类型

    NullBooleanField(Field):
        - 可以为空的布尔值
        
    DurationField(Field)
        - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

    FloatField(Field)
        - 浮点型

    DecimalField(Field)
        - 10进制小数
        - 参数:
            max_digits,小数总长度
            decimal_places,小数位长度

二进制********                
    BinaryField(Field)
        - 二进制类型

日期、时间********    
    DateTimeField(DateField)
        - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

    DateField(DateTimeCheckMixin, Field)
        - 日期格式      YYYY-MM-DD

    TimeField(DateTimeCheckMixin, Field)
        - 时间格式      HH:MM[:ss[.uuuuuu]]
字段类型
auto_now_add        创建时,自动生成时间
auto_now               更新时,自动更新为当前时间
    # obj = UserGroup.objects.filter(id=1).update(caption='CEO')    #自动时间更新不生效
    # obj = UserGroup.objects.filter(id=1).first()    #生效
    # obj.caption = "CEO"
    # obj.save()

default             数据库中字段的默认值    
null                数据库中字段是否可以为空
db_column           数据库中字段的列名
db_tablespace
primary_key         数据库中字段是否为主键
db_index            数据库中字段是否可以建立索引
unique              数据库中字段是否可以建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

verbose_name        Admin中字段名显示自定义的名字
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的输入提示信息(显示在该行下边)
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                    注:因为数据存在内存中,如果修改要重启程序才会生效
                    如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                    字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
                    如:{'null': "不能为空.", 'invalid': '格式错误'}

validators          自定义错误验证(列表类型),从而定制想要的验证规则
                    from django.core.validators import RegexValidator
                    from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
                    MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
                    如:
                        test = models.CharField(
                            max_length=32,
                            error_messages={
                                'c1': '优先错信息1',
                                'c2': '优先错信息2',
                                'c3': '优先错信息3',
                            },
                            validators=[
                                RegexValidator(regex='root_\d+', message='错误了', code='c1'),
                                RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
                                EmailValidator(message='又错误了', code='c3'), ]
                        )
字段的参数
class UserInfo(models.Model):
        nid = models.AutoField(primary_key=True)
        username = models.CharField(max_length=32)
        class Meta:
            # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
            db_table = "table_name"

            # 联合索引
            index_together = [
                ("pub_date", "deadline"),
            ]

            # 联合唯一索引
            unique_together = (("driver", "restaurant"),)

            # admin中显示的表名称
            verbose_name

            # verbose_name加s
            verbose_name_plural
        
#更多:https://docs.djangoproject.com/en/1.10/ref/models/options/
元信息(models.py)
1.触发Model中的验证和错误提示有两种方式:
        a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息
        b. 调用Model对象的 clean_fields 方法,如:
            # models.py
            class UserInfo(models.Model):
                nid = models.AutoField(primary_key=True)
                username = models.CharField(max_length=32)

                email = models.EmailField(error_messages={'invalid': '格式错了.'})

            # views.py
            def index(request):
                obj = models.UserInfo(username='11234', email='uu')
                try:
                    print(obj.clean_fields())
                except Exception as e:
                    print(e)
                return HttpResponse('ok')

           # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。

    2.Admin中修改错误提示
        # admin.py
        from django.contrib import admin
        from model_club import models
        from django import forms


        class UserInfoForm(forms.ModelForm):
            username = forms.CharField(error_messages={'required': '用户名不能为空.'})
            email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'})
            age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'})

            class Meta:
                model = models.UserInfo
                # fields = ('username',)
                fields = "__all__"


        class UserInfoAdmin(admin.ModelAdmin):
            form = UserInfoForm


        admin.site.register(models.UserInfo, UserInfoAdmin)
拓展知识

 

*连表结构

 

2) 操作表
*基本操作
根据类对数据库表中的数据进行各种操作

    一对多:
    
        a. 创建外键(一个对象)
        
        b. 数据库中实际存储 --> 外键字段_id
        
        c.    根据"外键字段_id"添加数据
            models.tb.object.create(name='root', user_group_id=1)
            
        d.  显示用户组信息
            
            userlist = models.tb.object.all()
            for row in userlist:
                row.id
                row.user_group_id
                row.user_group.caption

 

*进阶操作

 

*其他操作

 

*连表操作

 

*扩展

 

2、Form

 

3、跨站请求伪造

 

4、Cookie

 

5、Session

 

6、分页

 

7、缓存

 

8、序列化

 

9、信号

 

参考:

Web框架:http://www.cnblogs.com/wupeiqi/articles/5237672.html
Django【基础篇】:http://www.cnblogs.com/wupeiqi/articles/5237704.html
Django【进阶篇】:http://www.cnblogs.com/wupeiqi/articles/5246483.html
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!