准备环境

1、安装Django

$ pip install django==2.2.0$ pip listPackage Version---------------- -------backcall 0.1.0 decorator 4.4.0 Django 2.2 $ python -m django --version2.2

2、安装 mysqlclient
$ pip3 install mysqlclient

如果报错,请参考:https://blog.51cto.com/qiangsh/2422115

web框架的设计逻辑

1:用户打开一个网址的步骤

从用户角度分析一个框架(网站)都需要那些零件

经典的MVC(MTV)框架是怎么来的呢?

用户输入网址是哪里定义的呢?——URL用户两种访问模式读(get)/写(post),谁来处理?——view(control)view处理的需要数据在哪存着的呢?——modelview处理完毕,用户请求看到的页面是谁渲染的呢?——template(view)

2:以此设计逻辑分析django框架

首先创建django项目,查看下项目的目录结构

使用 django-admin 来创建 devops 项目:

django-admin.py startproject devops

查看项目的目录结构:

$ tree devops/├── devops │   ├── __init__.py│   ├── settings.py│   ├── urls.py│   └── wsgi.py├── manage.py

目录说明:

devops: 项目的容器。manage.py: 一个实用的命令行工具,可让你以各种方式与该 Django 项目进行交互。devops/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。devops/settings.py: 该 Django 项目的设置/配置。devops/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站"目录"。devops/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行你的项目。

2.1:访问URL——为啥访问 http://ip/admin就访问到了管理后台

# URL总入口$ cat devops/urls.py from django.contrib import adminfrom django.urls import pathurlpatterns = [ # 访问admin的请求交给了admin.site.urls处理,而admin是django自带的APP path('admin/', admin.site.urls), ]# devops项目总配置文件$ cat devops/settings.py……# Django自带的APP都已经在这里注册好了,我们自己定义的app也得在这里注册INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'hello.apps.HelloConfig', # 添加此行]……# 这些django自动的APP在哪呢?$ ls ~/env3/lib/python3.6/site-packages/django/contrib/admin admindocs auth contenttypes flatpages gis humanize __init__.py messages postgres __pycache__ redirects sessions sitemaps sites staticfiles syndication# 找到admin这个APP的url$ vim ~/env3/lib/python3.6/site-packages/django/contrib/admin/sites.py…… def urls(self): return self.get_urls(), 'admin', self.name……

2.2:用户发出的请求是数来处理的呢

URL已经将用户请求带到了admin的内部。用户访问admin后台其实提交了两个操作,第一是get请求admin页面,admin给了一个列表框,要求填写用户名密码;第二个是post请求是把用户密码提交给admin,那么这两个请求是谁来处理的呢——view

# admin app的view目录下编写应对用户请求的各种逻辑,源码可自行查看$ ls ~/env3/lib/python3.6/site-packages/django/contrib/admin/views/autocomplete.py decorators.py __init__.py main.py

2.3:数据存在哪里?数据表结构哪里定义的呢?
vim ~/env3/lib/python3.6/site-packages/django/contrib/admin/models.py

2.4:用户看的页面各种样式在哪里定义的呢?

$ ls ~/env3/lib/python3.6/site-packages/django/contrib/admin/templates/admin/404.html auth change_form_object_tools.html date_hierarchy.html filter.html login.html prepopulated_fields_js.html500.html base.html change_list.html delete_confirmation.html includes object_history.html search_form.htmlactions.html base_site.html change_list_object_tools.html delete_selected_confirmation.html index.html pagination.html submit_line.htmlapp_index.html change_form.html change_list_results.html edit_inline invalid_setup.html popup_response.html widgetsDjango起步

1、创建工程
django-admin.py startproject devops

2、创建一个APP(应用)

Django框架的组织架构:一个项目(Project)下可以有多个应用(APP),每个应用下面都五脏俱全的MTV模块(就是以py结尾的文件),每个模块各司其职。

qiangsh@Dream ~/D/P/5/Django_day1> cd devops/qiangsh@Dream ~/D/P/5/D/devops> python manage.py startapp helloqiangsh@Dream ~/D/P/5/D/devops> tree hellohello├── admin.py # 后台管理文件├── apps.py # app命名文件├── __init__.py # 初始化文件,有他表示这是一个包├── migrations # 数据迁移文件│ └── __init__.py├── models.py # 模型文件├── tests.py├── urls.py # 定义路由,默认没有,自己加的└── views.py # 逻辑处理,即控制器

2.1:全局配置文件中注册新创建的APP

$ cat devops/settings.pyINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'hello.apps.HelloConfig', # 添加此行]

注释下面一行,解决权限问题

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',]

2.2:编写处理逻辑代码(控制器)

$ cat hello/views.py# Create your views here.from django.shortcuts import renderfrom django.http import HttpResponse,QueryDict# 练习一# def index(request):# return HttpResponse("<p>Hello World,Hello, Django</p>")# 练习二# 位置参数的接收方法---函数中的参数和URL中的位置一一对应(严重依赖参数顺序且代码可读性不好,不推荐)def index(request,year=2018,month=8): # 普通参数的接受方法 # 方法一、设置默认值的方式获取数据更优雅 year = request.GET.get("year","2019") # 方法二、直接获取数据,没有传值会报错,不建议使用 month = request.GET["month"] return HttpResponse("year is %s,month is %s" %(year,month))# 关键字传参数(?<参数名>参数类型)——视图中直接通过参数名获取值(最常用)def index2(request,**kwargs): # 请求参数接收,默认为GET请求,通过method判断POST请求 if request.method == "POST": print(request.scheme) # http #print(request.method) #POST print(request.body) #b'year=2019&month=11' #print(type(request.body)) print(QueryDict(request.body).dict()) #{'year': '2018', 'month': '08'} #print(type(QueryDict(request.body).dict())) print(request.POST) #<QueryDict: {'year': ['2018'], 'month': ['08']}> print(type(request.POST)) #<class 'django.http.request.QueryDict'> data = request.POST year = data.get('year',2018) month = data.get('month',8) else: print(request) print(request.method) print(request.META) print(request.body) print(kwargs) year = kwargs.get('year',2018) month = kwargs.get('month',8) return HttpResponse("year is %s,month is %s" %(year,month))def user(request,**kwargs): if request.method == "POST": pass else: user = {'name':'qsh','age':'18'} return render(request,'index.html',{'user':user})

2.3:编写提供给用户访问的路由URL

URL的设计比较优雅的方式:APP自己定义自己的url,然后在全局统一入口的url文件中引入即可

#设计自己的url, 用户访问/hello就会把请求发给views模块中的index方法

$ cat hello/urls.py # 系统没有,需要自己建立from django.urls import path,re_pathfrom . import viewsurlpatterns = [ # 2.3.1:普通传参url基本和无参数一样 # 请求方式 http://127.0.0.1:8000/hello/hello/?year=2019&month=10 path('index/', views.index, name='index'), path('hello/',views.index2, name='index'), # URL中每个位置数值和view中定义的参数顺序一一对应(代码可读性不好,不推荐) # 2.3.2:位置匹配 # 请求方式 http://127.0.0.1:8000/hello/hello/2019/08/ re_path('hello/([0-9]{4})/([0-9]{2})/', views.index2, name='index2'), # 2.3.3:关键字匹配(最优雅) (?<参数名>参数类型)??视图中直接通过参数名获取值(最常用) re_path('user/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/', views.user, name='user'),]

#在统一访问url入口将hello的url引入进来(注册子app的url)

$ cat devops/urls.pyfrom django.contrib import adminfrom django.urls import path,includeurlpatterns = [ path('admin/', admin.site.urls), path('hello/',include('hello.urls'),name="hello"),]# 总入口文件又多了一层路径,所以最终的访问路径为 http://ip:8000/hello/<app_url>/

#最终路径如下

$ tree.├── devops # devops工程自动的全局app│ ├── __init__.py│ ├── settings.py│ ├── urls.py # 全局路由入口 │ └── wsgi.py├── hello # 自己创建的app│ ├── admin.py│ ├── apps.py│ ├── __init__.py│ ├── migrations│ │ └── __init__.py│ ├── models.py│ ├── tests.py│ ├── urls.py # 每个app自定义的路由入口,需要注册│ └── views.py└── manage.py3 directories, 13 files

3、启动工程

python manage.py runserver 0.0.0.0:8000

小结

以上这个小栗子其实只用到了MTV中的View以及URL(url是view的指引,这两个会一起出现,统称为V),数据库和模板都没用上,故而体验不好,功能也简单,好歹是跑通了。接下来一个完整的项目。在此之前把V和URL的最佳实战知识学习下

MTV之视图(URL&&View)

hello的小栗子主要实现了用户发起请求,然后Django根据用户发起的url路径找到对应的处理函数,然后将内容简单返回而已。但现实中用户的请求可不是这么简单。用户都会有那些请求呢,大致可以分为两类读和写,读有带参数和不带参数两种场景,写肯定是带参数了
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同

Django的MTV分别是值:

M 代表模型(Model):负责业务对象和数据库的关系映射(ORM)。T 代表模板 (Template):负责如何把页面展示给用户(html)。V 代表视图(View):负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和
Template,MTV的响应模式如下所示:Web服务器(中间件)收到一个http请求Django在URLconf里查找对应的视图(View)函数来处理http请求视图函数调用相应的数据模型来存取数据、调用相应的模板向用户展示页面视图函数处理结束后返回一个http的响应给Web服务器Web服务器将响应发送给客户端

4、添加html文件
#添加模板目录 'DIRS': []

$ cat devops/settings.pyTEMPLATES = [ { # 模板引擎,翻译给前端展示 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 模板目录,当前目录下创建templates 'DIRS': [BASE_DIR+"/templates"], # 如果统一目录没有,就在app自己目录查找 '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', ], }, },]#配置解释* BACKEND 是一个指向实现了Django模板后端API的模板引擎类的带点的Python路径。内置的后有django.template.backends.django.DjangoTemplates 和 django.template.backends.jinja2.Jinja2.两个模板差不多* DIRS 定义了一个目录列表,模板引擎按列表顺序搜索这些目录以查找模板源文件。默认会先找templates目录* APP_DIRS 告诉模板引擎是否应该进入每个已安装的应用中查找模板。每种模板引擎后端都定义了一个惯用的名称作为应用内部存放模板的子目录名

qiangsh@Dream ~> cd devopsqiangsh@Dream ~/devops> mkdir templates/qiangsh@Dream ~/devops> cat index.html<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /><title>python训练营</title><head><body >myname is {{ user.name }}, age is {{ user.age }}</body></html>

5、浏览器访问
5.1:带参数的读——get

用户请求带参数的url

# 普通参数http://127.0.0.1:8000/hello/index/?year=2019&month=10

# 位置参数http://127.0.0.1:8000/hello/hello/2018/01/

5.2:带参数的写——post

#shell终端,模拟django表单POST提交数据curl -X POST http://127.0.0.1:8000/hello/hello/ -d 'year=2019&month=11'


http://127.0.0.1:8000/hello/user/2019/08/

6、QueryDict

通过上面演示我们知道无论GET/POST请求,接受参数的数据类型都是QueryDict。QueryDict到底做了什么事情呢

在HttpRequest 对象中,GET 和POST 属性是django.http.QueryDict 的实例,它是一个自定义的类似字典的类,用来处理同一个键带有多个值。无论使用GET,POST方式,他们最终都是通过QueryDict方法对传入的参数进行处理

# QueryDict常用方法>>> QueryDict('a=1&a=2&c=3') # 对用户请求的数据处理<QueryDict: {'a': ['1', '2'], 'c': ['3']}>>>> QueryDict.get(key, default=None) # 获取数据>>> q = QueryDict('a=1&a=2&a=3')>>> q.lists()[('a', ['1', '2', '3'])]>>> q = QueryDict('a=1&b=3&c=5')>>> q.dict(){'a': '1','b':'3','c':'5'}表格练习

1、修改逻辑代码

$ cat hello/views.py def user(request,**kwargs): if request.method == "POST": pass else: user = {'name':'qsh','age':'18'} title = "devops" books = ['python','java','php','web'] people = {'name':'qsh','age':18,'sex':'male'} products = [{'pid': 1, 'name': 'iphone'}, {'pid': 2, 'name': 'computer'}, {'pid': 3, 'name': 'TV'}] return render(request,'index.html',{'title':title,'books':books,'people':people,'user':user,'products':products})

2、修改html页面

$ cat templates/index.html<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" /><title>{{title}}</title><head><body > {# 接受列表的第N个值,很low不推荐 #}<li>{{books.0}}</li> <li>{{books.1}}</li><li>{{books.2}}</li><li>{{books.3}}</li>{# for循环标签 ,渲染books列表 #}{% for book in books %}<li>{{book}}</li>{% endfor %}{# 接受字典中的定义的值 #}<div >hello my name is {{people.name}} </br> my age is {{ people.age }} and I am {{ people.sex }}</div>{# if标签使用,判断user是否存在 #}{% if people %} <li>name:{{people.name}}</li>{% else %} 用户不存在{% endif %}{# for循环输出字典里所有的key,value #}{% for k,v in people.items %}<li>{{k}}-->{{v}}</li>{% endfor %}{# 列表页展示 #}{% for product in products %} <li>ID:{{product.pid}},Name:{{product.name}}</li>{% endfor %}{# 列表页展示,表格输出 #}<table border="1"><thead> {# 定义表格的表头 #} <tr> {# 行 #} <th>ID</th> {# 表头单元格 - 包含表头信息 #} <th>商品名</th> </tr></thead><tbody> {% for product in products %} <tr> <td> {{product.pid}} </td> {# 标准单元 - 包含数据 #} <td> {{product.name}} </td> </tr> {% endfor %}</tbody></table></body></html>

3、浏览器访问
http://127.0.0.1:8000/hello/user/2019/08/

表单练习

1、修改逻辑代码

$ cat hello/views.py#添加模块from django.http import HttpResponse, QueryDict, HttpResponseRedirectfrom django.shortcuts import renderfrom django.urls import reverse#新建登录函数def login(request, **kwargs): data = "" if request.method == "POST": print(request.POST) print(QueryDict(request.body).dict()) username = request.POST.get('username','qsh') passwd = request.POST.get('password','123456') if username == "admin" and passwd == "123456": # data = "welcome you %s" % username return HttpResponseRedirect(reverse("hello:user")) # return HttpResponseRedirect("/hello/hello/") else: data = "your passwd or username is wrong,plaeace again" return render(request, 'login.html', {'data':data})

2、创建登录html

$ cat templates/login.html<html><body> <!--登陆表单--> <form action="{% url 'hello:login' %}" method="post"> <!--用户名--> <input name="username" type="text" placeholder="用户名"> </br> <!--密码--> <input name="password" type="password" placeholder="密码"> </br> <button type="submit">登录</button> </form> {% if data %} <h2>{{ data }}</h2> {% endif %}</body></html>

3、路由

$ cat hello/urls.pyfrom django.urls import path,re_pathfrom . import viewsapp_name = 'hello'urlpatterns = [ path('index/', views.index, name='index'), path('hello/',views.index2, name='hello'), path('login/',views.login, name='login'), path('index2/',views.user, name='user'), re_path('hello/([0-9]{4})/([0-9]{2})/', views.index2, name='index2'), re_path('user/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/', views.user, name='user'),]

4、浏览器访问
http://127.0.0.1:8000/hello/login/
用户名密码错误,效果如下。

输入正确 跳转http://127.0.0.1:8000/hello/index2/

页面美化

模板template如何接收View的各种数据类型并渲染已经完成,但页面还是不够美丽,就得引出前端内容了——Bootstrap(HTML/CSS/Jquery)

学习网站:
https://v3.bootcss.com/
https://v3.bootcss.com/css/