Django框架基础

摘要

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

  • django基础
  • 配置文件
  • 路由
  • Template
  • Model
  • Form
  • 中间件
  • Admin

Django

http提交数据的方式有"post","get","put","patch","delete","head","options","trace".

提交数据的时候,服务端依据method的不同会触发不同的视图函数.

  • 对于from表单来说,提交数据只有get和post两种方法
  • 另外的方法可以通过Ajax方法来提交

Django的请求生命周期

首先我们知道HTTP请求及服务端响应中传输的所有数据都是字符串,在Django中,当我们访问一个的url时,会通过路由匹配进入相应的html网页中.

  • 当用户在浏览器中输入url时,浏览器会生成请求头和请求体发给服务端,包含浏览器的动作(method)
  • 请求经过Django中的wsgi,再经过Django的中间件,最后url到过路由映射表,在路由中一条一条进行匹配,
    • 中间件:拦截一部分请求;比如验证session, 没有登录的请求一些页面,跳转至登录页,再到 urls,分发请求
    • 一旦其中一条匹配成功就执行对应的视图函数,后面的路由就不再继续匹配了.
  • 视图函数根据客户端的请求查询相应的数据和模板,返回给Django,然后Django把客户端想要的数据渲染到模板中,做为一个字符串返回给客户端.
    • 视图函数中,CBV(dispatch反射)和FBV的get请求
  • 客户端浏览器接收到返回的数据,经过解析后显示给用户.
    • 客户端发到服务端的url中还必须要包含所要请求的数据信息等内容

基本命令

  • 创建项目:django-admin startproject sitename
  • 启动:python manage.py runserver 0.0.0.0
  • 创建app:python manage.py startapp appname
  • 同步数据库:python manage.py syncdb
  • 生成数据库结构变动:python manage.py makemigrations
  • 执行数据库结构变动:python manage.py migrate
  • 创建管理员用户:python manage.py createsuperuser
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 创建项目
django-admin startproject sitename
# 创建app
python manage.py startapp base
# 启动服务
python manage.py runserver 0.0.0.0:8090

python manage.py flush
# 生成数据库结构变动
python manage.py makemigrations
# 执行数据库结构变动
python manage.py migrate
python manage.py makemigrations base
python manage.py migrate base
# 显示要执行的sql
python manage.py sqlall
# 只增加,不修改、删除
python manage.py syncdb

# i18n
python manage.py makemessages -t zh_hans
python manage.py makemessages -t zh_cn
python manage.py makemessages -l zh-hans
python manage.py makemessages -l en_us
python manage.py makemessages -l zh_hans -d djangojs
python manage.py makemessages -l zh_hans
python manage.py compilemessages
python manage.py shell
# 计划任务
python manage.py crontab show
python manage.py crontab add
python manage.py crontab remove

# 创建管理员用户
python manage.py createsuperuser

render、redirect、HttpResponse

1
2
3
4
5
6
from django.shortcuts import HttpResponse, render, redirect

return HttpResponse('ok')
return render(request,'index.html',{"name": "monicx", "hobby": ["reading", "blog"]})
return redirect('/index')
return render_to_response('Account/Login.html',data,context_instance=RequestContext(request))
  • render 模板渲染,可以接收三个参数,自动使用RequestContext
    • request
    • 待渲染的html模板文件
    • 保存具体数据的字典参数
  • redirect 接受一个URL参数,表示让浏览器跳转去指定的URL
  • HttpResponse 内部传入一个字符串然后发送给浏览器
  • render_to_response 与render类似,需要自己处理RequestContext
  • HttpRequest 表示来自某客户端的一个单独的HTTP请求。HttpRequest对象是Django自动创建的
  • reverse 反向解析url
  • resolve_url
  • HttpResponseRedirect

request

封装了所有用户请求信息request.environ

1
2
3
4
5
6
7
8
9
10
request.body
request.POST
request.FILES
request.GET
request.xxx.get
request.xxx.getlist
request.Meta
request.method
request.path_info
request.COOKIES

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import os
from django.urls import reverse_lazy

# 模板配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')],
# 开启加载app独立模板,寻找顺序:app的templates目录 -> 项目的templates目录
'APP_DIRS': True,
]

# 登录相关url配置
LOGIN_REDIRECT_URL = reverse_lazy('home:index')
LOGIN_URL = reverse_lazy("users:login")
LOGOUT_REDIRECT_URL = reverse_lazy("users:login")

# 数据库配置
# 由于Django内部连接MySQL时使用的是MySQLdb模块,而python3中还无此模块,所以需要使用pymysql来代替
# 如下设置放置的与project同名的配置的 __init__.py文件中,或者放在配置中的__init__.py中

# import pymysql
# pymysql.install_as_MySQLdb()
 
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# },
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'kseva',
'USER': 'root',
'PASSWORD': 'ufRRgdfu3ar1',
"HOST": '127.0.0.1',
"PORT": 33060,
"CHARSET": 'utf8',
"COLLATION": 'utf8_general_ci',
"TEST": {}
},
}



# i18n语言配置
LANGUAGE_CODE = 'en-us'
# LANGUAGE_CODE = 'zh-hans'

#TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_L10N = True

USE_TZ = True

LANGUAGES = (
('en', ('English')),
('zh-hans', ('中文简体')),
)

# i18n目录app目录配置
LOCALE_PATHS = [os.path.join(BASE_DIR, 'i18n'), ]

# 静态文件路径配置
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)

# 邮件配置
# EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
# EMAIL_USE_SSL = True
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = 'kissingworld@163.com'
EMAIL_HOST_PASSWORD = '6502570qq'
DEFAULT_FROM_EMAIL = '[kseva]'

# 用户删除目录配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

# 计划任务配置
CRONJOBS = [
('* * * * *', 'home.cron.test','>>/tmp/test.log')
]

# 验证码配置
CAPTCHA_IMAGE_SIZE = (80, 33)
CAPTCHA_FOREGROUND_COLOR = '#001100'
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_dots',)

# bootstrap3配置
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = {
'horizontal_label_class': 'col-md-2',
# Field class to use in horizontal forms
'horizontal_field_class': 'col-md-9',
# Set placeholder attributes to label if no placeholder is provided
'set_placeholder': True,
'success_css_class': '',
}

# restful配置
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
}

mac下i18n报错

CommandError: Can't find msguniq. Make sure you have GNU gettext tools 0.15 or newer installed.

1
2
brew install gettext 
brew link gettext --force

路由系统

单一路由对应

1
url(r'^index$', views.index),

基于正则的路由

1
2
url(r'^index/(\d*)', views.index),
url(r'^manage/(?P<name>\w*)/(?P<id>\d*)', views.manage),

添加额外的参数

1
url(r'^manage/(?P<name>\w*)', views.manage,{'id':333}),

为路由映射设置名称

1
2
url(r'^home', views.home, name='h1'),
url(r'^index/(\d*)', views.index, name='h2'),

设置名称之后,可以在不同的地方调用,如:

  • 模板中使用生成URL
1
{% url 'h2' 2012 %}
  • 使用request.path_info获取当前url
  • 函数中使用生成URL
1
reverse('h2', args=(2012,)) 路径:django.urls.reverse
  • Model中使用获取URL 自定义get_absolute_url() 方法
1
2
3
4
5
6
7
8
9
10
11
12
# x
url(r'/index/',name='index')
{% url 'index' %}
reverse('index')
# xx
url(r'/index/(\d+)/(\d+)',name='index')
{% url 'index' 1 10 %}
reverse('index',args=(1,2,))
# xxx
url(r'/index/(?P<nid>\d+)/(?P<uid>\d+)',name='index')
{% url 'index' nid=1 uid=10 %}
reverse('index',kwargs={"nid":1,"uid":2})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class NewType(models.Model):
caption = models.CharField(max_length=16)


def get_absolute_url(self):
"""
为每个对象生成一个URL
应用:在对象列表中生成查看详细的URL,使用此方法即可!!!
:return:
"""
# return '/%s/%s' % (self._meta.db_table, self.id)
# 或
from django.urls import reverse
return reverse('NewType.Detail', kwargs={'nid': self.id})

获取请求匹配成功的URL信息:request.resolver_match

根据app对路由规则进行分类(路由分发)

1
url(r'^web/',include('web.urls')),

命名空间

project.urls.py

1
2
3
4
5
6
from django.conf.urls import url,include

urlpatterns = [
url(r'^a/', include('app01.urls', namespace='author-polls')),
url(r'^b/', include('app01.urls', namespace='publisher-polls')),
]

app01.urls.py

1
2
3
4
5
6
7
from django.conf.urls import url
from app01 import views

app_name = 'app01'
urlpatterns = [
url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]

app01.views.py

1
2
3
def detail(request, pk):
print(request.resolver_match)
return HttpResponse(pk)

以上定义带命名空间的url之后,使用name生成URL时候,应该如下:

1
2
v = reverse('app01:detail', kwargs={'pk':11})
{% url 'app01:detail' pk=12 pp=99 %}

django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给对一个的view中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。

通过反射机制,为django开发一套动态的路由系统Demo: 点击下载

path、re_path、url区别

1
2
3
4
5
6
# 路由配置,支持<int:a><str:link>转换器
from django.urls import path
# 支持正则匹配
from django.urls import re_path
# 是re_path的别名
from django.conf.urls import url

路由配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

# 主配置文件中的路由配置
from django.contrib import admin
from django.urls import path, include
from django.views.i18n import JavaScriptCatalog
from rest_framework.authtoken.views import obtain_auth_token
from home.views import IndexView

# 根据app对路由规则进行分类
# include配置会去对应的app的urls中加载app自己的路由(路由分发)
# 此处的路由是app路由的路由前缀
urlpatterns = [
path('', IndexView.as_view()),
path('admin/', admin.site.urls),
# namespace设置命名空间,include做路由分发
path('base/', include('base.urls', namespace='base')),
path('configs/', include('configs.urls')),
# 验证码配置
path('captcha/', include('captcha.urls')),
# js中i18n配置
path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
# restful-api路由配置
path('api-token-auth/', obtain_auth_token),
]


# base
from django.urls import path
from .views.menu import MenuView

# 对app路由设置命名空间
app_name = "base"
urlpatterns = [
# 对子路由设置映射名称{% url 'base:menu' %}
path('menu/', MenuView.as_view(), name="menu"),
]

from django.urls import path, re_path
from .views import *

app_name = "configs"
urlpatterns = [
path('', AppView.as_view(), name="index"),
# 正则路由配置
re_path('^app/$', AppView.as_view(), name="app"),
re_path('^app/create/$', AppCreateView.as_view(), name="create"),
re_path('^app/(?P<pk>[0-9a-zA-Z\-]+)/edit/$', AppEditView.as_view(), name="edit"),
re_path('^app/(?P<pk>[0-9a-zA-Z\-]+)/config/$', ConfigView.as_view(), name="config"),
]

Template

Django框架Template

Model

Django框架Model

Form

Django框架Form

View

FBV和CBV解析以及常用的View解析

Django框架View

中间件

在django中,中间件middleware其实就是一个类,在请求到来和结束后,django会根据自己的规则在合适的时机执行中间件中相应的方法。

在django项目的settings模块中,有一个 MIDDLEWARE_CLASSES 变量,其中每一个元素就是一个中间件。

1
2
3
4
5
6
7
8
9
10
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',
'django.middleware.locale.LocaleMiddleware',
]

与mange.py在同一目录下的文件夹 wupeiqi/middleware下的auth.py文件中的Authentication类

中间件中可以定义以下方法,分别是:

  • process_request(self,request)请求最开始进来的时候执行
  • process_view(self, request, callback, callback_args, callback_kwargs)view执行之前执行
  • process_template_response(self,request,response)view中的函数返回的对象中有一个render方法的时候执行
  • process_exception(self, request, exception)view执行报错之后执行
  • process_response(self, request, response)返回response之后执行
  • Django1.10版本之后:process_request以及process_view如果返回了response(HttpResponse)则不会执行view,会直接调用同级别的process_response然后一级一级返回
  • Django1.10版本之前:会调用最下层process_response然后一级一级返回

以上方法的返回值可以是None和HttpResonse对象,如果是None,则继续按照django定义的规则向下执行,如果是HttpResonse对象,则直接将该对象返回给用户。

创建中间件类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.utils.deprecation import MiddlewareMixin

class Foo:
def render(self):
return HttpResponse('OK')

class MyMiddleware(MiddlewareMixin):

def process_request(self,request):
pass

def process_view(self, request, callback, callback_args, callback_kwargs):
i =1
pass

def process_exception(self, request, exception):
pass

def process_template_response(self, request, response):
return Foo()

def process_response(self, request, response):
return response

注册中间件

1
2
3
4
MIDDLEWARE_CLASSES = (
...
'mypro.middleware.auth.RequestExeute',
)

Admin

Django框架Admin

扩展

自定义上传

1
2
3
4
5
6
7
8
def upload_file(request):
if request.method == "POST":
obj = request.FILES.get('fafafa')
f = open(obj.name, 'wb')
for chunk in obj.chunks():
f.write(chunk)
f.close()
return render(request, 'file.html')

Form上传文件实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class FileForm(forms.Form):
ExcelFile = forms.FileField()

from django.db import models

class UploadFile(models.Model):
userid = models.CharField(max_length = 30)
file = models.FileField(upload_to = './upload/')
date = models.DateTimeField(auto_now_add=True)

def UploadFile(request):
uf = AssetForm.FileForm(request.POST,request.FILES)
if uf.is_valid():
upload = models.UploadFile()
upload.userid = 1
upload.file = uf.cleaned_data['ExcelFile']
upload.save()

print upload.file

应用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# radio select单选
request.POST.get('x')
# checkbox select多选multiple
request.POST.getlist('x')

# input(type=file)上传文件
<form action="/login" method="POST" enctype="multipart/form-data">
<input type="file" name="x">
</form>
obj = request.FILES.get('x')
f = open(obj.name,mode='wb')
for i in obj.chunks():
f.write(i)
f.close()