1、安装Django
$ pip install django==2.2.0
$ pip list
Package Version
---------------- -------
backcall 0.1.0
decorator 4.4.0
Django 2.2
$ python -m django --version
2.2
2、安装 mysqlclient$ pip3 install mysqlclient
如果报错,请参考:https://blog.51cto.com/qiangsh/2422115
1:用户打开一个网址的步骤
从用户角度分析一个框架(网站)都需要那些零件
经典的MVC(MTV)框架是怎么来的呢?
2:以此设计逻辑分析django框架
首先创建django项目,查看下项目的目录结构
使用 django-admin 来创建 devops 项目:
django-admin.py startproject devops
查看项目的目录结构:
$ tree devops/
├── devops
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── manage.py
目录说明:
2.1:访问URL——为啥访问 http://ip/admin就访问到了管理后台
# URL总入口
$ cat devops/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
# 访问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.html
500.html base.html change_list.html delete_confirmation.html includes object_history.html search_form.html
actions.html base_site.html change_list_object_tools.html delete_selected_confirmation.html index.html pagination.html submit_line.html
app_index.html change_form.html change_list_results.html edit_inline invalid_setup.html popup_response.html widgets
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 hello
qiangsh@Dream ~/D/P/5/D/devops> tree hello
hello
├── admin.py # 后台管理文件
├── apps.py # app命名文件
├── __init__.py # 初始化文件,有他表示这是一个包
├── migrations # 数据迁移文件
│ └── __init__.py
├── models.py # 模型文件
├── tests.py
├── urls.py # 定义路由,默认没有,自己加的
└── views.py # 逻辑处理,即控制器
2.1:全局配置文件中注册新创建的APP
$ cat devops/settings.py
INSTALLED_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 render
from django.http import HttpResponse,QueryDict
# 练习一
# def index(request):
# return HttpResponse("Hello World,Hello, Django
")
# 练习二
# 位置参数的接收方法---函数中的参数和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) #
print(type(request.POST)) #
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_path
from . import views
urlpatterns = [
# 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[0-9]{4})/(?P[0-9]{2})/', views.user, name='user'),
]
#在统一访问url入口将hello的url引入进来(注册子app的url)
$ cat devops/urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('hello/',include('hello.urls'),name="hello"),
]
# 总入口文件又多了一层路径,所以最终的访问路径为 http://ip:8000/hello//
#最终路径如下
$ 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.py
3 directories, 13 files
3、启动工程
python manage.py runserver 0.0.0.0:8000
小结
以上这个小栗子其实只用到了MTV中的View以及URL(url是view的指引,这两个会一起出现,统称为V),数据库和模板都没用上,故而体验不好,功能也简单,好歹是跑通了。接下来一个完整的项目。在此之前把V和URL的最佳实战知识学习下
hello的小栗子主要实现了用户发起请求,然后Django根据用户发起的url路径找到对应的处理函数,然后将内容简单返回而已。但现实中用户的请求可不是这么简单。用户都会有那些请求呢,大致可以分为两类读和写,读有带参数和不带参数两种场景,写肯定是带参数了
Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同
Django的MTV分别是值:
4、添加html文件
#添加模板目录 'DIRS': []
$ cat devops/settings.py
TEMPLATES = [
{
# 模板引擎,翻译给前端展示
'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 devops
qiangsh@Dream ~/devops> mkdir templates/
qiangsh@Dream ~/devops> cat index.html
python训练营
myname is {{ user.name }}, age is {{ user.age }}
5、浏览器访问
5.1:带参数的读——get
# 普通参数
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.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
{{title}}
{# 接受列表的第N个值,很low不推荐 #}
{{books.0}}
{{books.1}}
{{books.2}}
{{books.3}}
{# for循环标签 ,渲染books列表 #}
{% for book in books %}
{{book}}
{% endfor %}
{# 接受字典中的定义的值 #}
hello my name is {{people.name}}
my age is {{ people.age }} and I am {{ people.sex }}
{# if标签使用,判断user是否存在 #}
{% if people %}
name:{{people.name}}
{% else %}
用户不存在
{% endif %}
{# for循环输出字典里所有的key,value #}
{% for k,v in people.items %}
{{k}}-->{{v}}
{% endfor %}
{# 列表页展示 #}
{% for product in products %}
ID:{{product.pid}},Name:{{product.name}}
{% endfor %}
{# 列表页展示,表格输出 #}
{# 定义表格的表头 #}
{# 行 #}
ID {# 表头单元格 - 包含表头信息 #}
商品名
{% for product in products %}
{{product.pid}} {# 标准单元 - 包含数据 #}
{{product.name}}
{% endfor %}
3、浏览器访问
http://127.0.0.1:8000/hello/user/2019/08/
1、修改逻辑代码
$ cat hello/views.py
#添加模块
from django.http import HttpResponse, QueryDict, HttpResponseRedirect
from django.shortcuts import render
from 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
{% if data %}
{{ data }}
{% endif %}
3、路由
$ cat hello/urls.py
from django.urls import path,re_path
from . import views
app_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[0-9]{4})/(?P[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/