一、Django构建项目
1.1 创建项目
命令: django-admin startproject 项目名
django-admin startproject django_demo01
其中 mysite 是你的项目名称
这时会在指定的项目文件夹中,生成一个 mysite 文件夹,目录结构如下:
django_demo01/
manage.py
django_demo01/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
这些目录和文件的说明如下:
- manage.py 一个让你用各种方式管理 Django 项目的命令行工具。
- init.py 一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包
- settings.py 项目的配置文件,有关于数据库、编码、时区等
- urls.py 项目的 url路由配置,即url路由与其函数的对应配置
- wsgi.py :作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。
- asgi.py :作为你的项目的运行在 ASGI 兼容的 Web 服务器上的入口
1.2 启动项目
在该cmd目录下执行如下命令,可以启动项目:
python manage.py runserver
该命令后续可以增加参数,如:
python manage.py runserver 8081
python manage.py runserver 127.0.0.1:8888
在浏览器中访问提示的服务器网址:Starting development server at http://127.0.0.1:8000/ 中的
1.3 创建Django应用
一个项目由很多个应用组成的,每一个应用完成一个功能模块。
创建应用的命令如下: python mange.py startapp
应用名称
cmd窗口中,进入最外层的mysite,在这个目录下,我们可以输入如下命令,创建一个新的子应用:
python manage.py startapp app01
创建完成后的文件目录结构如下:
─django_demo01
│ db.sqlite3
│ manage.py
│
├─app01
│ │ admin.py
│ │ apps.py
│ │ models.py
│ │ tests.py
│ │ views.py
│ │ __init__.py
│ │
│ └─migrations
│ __init__.py
│
└─django_demo01
│ asgi.py
│ settings.py
│ urls.py
│ wsgi.py
│ __init__.py
│
└─__pycache__
settings.cpython-38.pyc
urls.cpython-38.pyc
wsgi.cpython-38.pyc
__init__.cpython-38.pyc
- init.py 是一个空文件,表示当前目录news可以当作一个python包使用。
- tests.py 文件用于开发测试用例,在实际开发中会有专门的测试人员,这个事情不需要我们来
做。 - models.py 文件跟数据库操作相关。
- views.py 文件跟接收浏览器请求,进行处理,返回页面相关。
- admin.py 文件跟网站的后台管理相关。
- migrations 数据库迁移文件。
1.4 应用注册
应用创建成功后,需要注册才可以使用,也就是建立应用和项目之间的关联,在项目 /settings.py 中
INSTALLED_APPS下添加应用的名称就可以完成注册。
初始项目的INSTALLED_APPS如下图:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
)
1.5 服务说明
能够打开一个django
的欢迎页面,是因为启动了django自带的一个简易web服务器,它是一个用纯 Python 写的轻量级的 Web 服务器。仅仅用于开发环境,不要在生产环境使用。
在这个服务器中,在需要的情况下会对每一次的访问请求重新载入一遍 Python 代码。所以你不需要为了让修改的代码生效而频繁的重新启动服务器。然而,一些动作,比如添加新文件,将不会触发自动重新加载,这时你得自己手动重启服务器。
二、Djnago请求
2.1 请求流程
2.2 urls.py配置
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('firstRequest/', views.FirstRequest),
]
2.3 views.py
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def FirstRequest(request):
return HttpResponse("第一个Django请求成功")
2.4 启动服务
python manage.py runserver
2.5 浏览器发送请求
其中在views.py文件中,每一个请求方法需要有一个request参数,通过该参数可以获取请求相应信息。可以使用dir(request)查看详细。
[
'COOKIES', 'FILES', 'GET', 'META', 'POST', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '__weakref__', '_current_scheme_host', '_encoding', '_get_full_path', '_get_post', '_get_raw_host', '_get_scheme', '_initialize_handlers', '_load_post_and_files'
, '_mark_post_parse_error', '_messages', '_read_started', '_set_content_type_params', '_set_post', '_stream', '_upload_handlers', 'accepted_types', 'accepts', 'body', 'build_absolute_uri', 'close', 'content_params', 'content_type', 'csrf_processing_done', 'encoding', 'environ', 'get_full_path', 'get_full_path_info', 'get_host', 'get_port', 'get_signed_cookie', 'headers', 'is_secure', 'method', 'parse_file_upload', 'path', 'path_info', 'read', 'readline', 'readlines', 'resolver_match', 'scheme', 'session', 'upload_handlers', 'user'
]
2.6 开发第一个登录表单
- urls.py配置
from django.contrib import admin
from django.urls import path
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('firstRequest/', views.FirstRequest),
path('login/', views.login),
]
- views.py添加业务方法
def login(request):
# 返回页面
html = '''
<html>
<body>
<form method="post">
用户名:<input name = "username" type="text"></input></br>
密码:<input name = "password" type = "password"></input></br>
<input type="submit" value="登录"></input>
</form>
</body>
</html>
'''
# return render(request, 'login.html')
return HttpResponse(html)
- 浏览器发送请求测试
三、Django配置
3.1 settings介绍
django项目创建后,在主应用中,会有一个settings.py文件,这个就是该项目的配置文件
- settings文件包含Django安装的所有配置
- settings文件是一个包含模块级变量的python模块,所以该模块本身必须符合python规则,并且可以使用python的语法
- settings中的所有配置项的key必须全部大写
- settings中每一个配置项都有默认值,默认配置内容在django/conf/global_settings.py中可以查看到,项目中不需要导入该模块,django框架会自动获取
- settings中可以添加自定义的配置项
可以在global_settings
中进行查看
from django.conf import global_settings
3.2 应用settings文件
- manage.py启动
默认在manage.py中配置:
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "firstdjango.settings")
- 手动指定配置文件位置
cmd命令启动如下:
python manage.py runserver 0.0.0.0:8000 --settings=firstdjango.settings
- 服务器部署启动
在wsgi.py中配置:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "firstdjango.settings")
3.3 常用配置项解析(重点)
import os
"""
当前文件所在文件夹的上一级目录的绝对路径
切记2个 os.path.dirname
"""
#BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = Path(__file__).resolve().parent.parent
# E:\Code_Learn\Python全栈工程师\Django\django_demo01
"""
用于加密session,一个随机的字符串
这样生成:
from django.core.management import utils
utils.get_random_secret_key()
"""
SECRET_KEY = 'django-insecure-yibecivjsju-iym&5fc!6x9)z$8!u=d7bwnpk6^tfythk)gzc)'
# 调试模式,可以看到错误的所有相信信息,部署时一定要修改为False
DEBUG = True
"""
允许访问的域名设置
开发环境不用理会
运行环境,配置 DEBUG = False后,
如果允许所有域名访问,则设置 ALLOW_HOSTS = ['*']
如果指定某些域名可以访问,则设置 ALLOW_HOSTS = ['*.baidu.com']
"""
ALLOWED_HOSTS = []
"""
应用的配置,
如:'app01.apps.App01Config'
如果没有 App01Config,那么可以配置为 'app01'
"""
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles', # 只有 DEBUG = Ture 才有效
'app01' # 子应用必须配置,否则不起作用
]
"""
中间层配置
自己编写的 中间层 需要配置在最后
譬如:
mymidlle.md.TestMiddleware
"""
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',
]
# 配置基础的urls
ROOT_URLCONF = 'django_demo01.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配置
WSGI_APPLICATION = 'django_demo01.wsgi.application'
"""
数据库配置
mysql在python3的使用,需要在 __init__.py 中加入以下代码:
import pymysql
pymysql.install_as_MySQLdb()
"""
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'django_test1',
'USER': 'root',
'PASSWORD': '123456',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
"""
用户密码验证
"""
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',
},
]
# 语言选择 , zh-Hans 为中文
LANGUAGE_CODE = 'en-us'
# 时区 Asia/Shanghai 是中国时区
TIME_ZONE = 'UTC'
# 国际化
USE_I18N = True
# 本地化
USE_L10N = True
# 使用时区,配套TIME_ZONE使用,必须设置为 False
USE_TZ = False
"""
静态文件的路径,默认是 static
如果在各自项目的static目录以外,还有目录存放静态文件,需要添加如下属性:
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "common_static1"),
'/var/www/static/',
)
"""
STATIC_URL = '/static/'
3.4 程序中获取settings中的配置项
如果在项目代码中需要获取settings中的配置项,这样获取:
# 切记不要导入具体的settings模块的路径,会形成高耦合
# 这样的方式是不可取的:from mysite import settings
from django.conf import settings
d = settings.DEBUG
四、URL调度器(重点)
4.1 工作原理
django通过urlconf来映射视图函数,只区分路径,不区分http方法
- Django确定要使用的根URLconf模块,一般是在settings中的ROOT_URLCONF设置的值,但是如果传入 HttpRequest 对象具有一个urlconf 属性(由中间件设置),则其值将用于代替 ROOT_URLCONF设置。
- Django加载该URLconf模块并查找变量 urlpatterns,它是一个列表django.urls.path() 和 / 或django.urls.re_path()实例。
- Django按顺序遍历每个URL模式,并停在与请求的URL匹配的第一个URL模式,需要特别注意编写的顺序
- 一旦某个URL模式匹配,Django就会导入并调用给定的视图,该视图是一个简单的Python函数(或基于类的视图方法)。该视图通过以下参数传递:
- 一个HttpRequest实例。
- 如果匹配的URL模式没有返回任何命名组,则来自正则表达式的匹配作为位置参数提供。
- 关键字参数由路径表达式匹配的任何命名部分组成,并由可选的kwargs参数传给 django.urls.path()或django.urls.re_path()。
- 如果没有URL模式匹配,或者在此过程中的任何点发生异常,Django将调用适当的错误处理视图
4.2 简单示例
- 在子应用下创建urls.py,内容如下
urlpatterns = [
]
- 在项目的应用中添加子应用urls.py配置
from django.contrib import admin
from django.urls import path, include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('firstRequest/', views.FirstRequest),
path('login/', views.login),
path('app01/', include('app01.urls'))
]
- 配置子应用urls.py
from django.contrib import admin
from django.urls import path, include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('firstRequest/', views.FirstRequest),
path('login/', views.login),
path('app01/', include('app01.urls'))
]
- 浏览器进行访问
http://127.0.0.1:8000/app01/login/
4.3 路由中的参数匹配
配置项目的urls.py
from django.contrib import admin
from django.urls import path, include
from app01 import views
urlpatterns = [
path('admin/', admin.site.urls),
path('firstRequest/', views.FirstRequest),
path('login/', views.login),
path('app01/', include('app01.urls'))
]
配置app01
的urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path('login/', views.login),
path('articles/2024/', views.articles_2024),
path('articles/<int:year>/', views.articles_year),
path('articles/<int:year>/<int:month>/', views.articles_year_month),
path('articles/<int:year>/<int:month>/<slug:slug>/', views.articles_year_month_slug),
path('articles/<str:str>/', views.str_test),
path('articles/<path:path>/', views.path_test),
path('articles/<uuid:uuid>/', views.uuid_test)
]
app01
的views.py
# 创建articles 2024处理函数
def articles_2024(request):
return HttpResponse('articles 2024')
def articles_year(request, year):
return HttpResponse(f'article_year, year:{year}')
def articles_year_month(request, year, month):
return HttpResponse(f'article_year_month, year:{year},month:{month}')
def articles_year_month_slug(request, year, month, slug):
return HttpResponse(f'article_year_month_slug, year:{year}, month:{month}, slug:{slug}')
def str_test(request, str):
return HttpResponse(f'str_test: {str}')
def path_test(request, path):
return HttpResponse(f'path_test: {path}')
def uuid_test(request, uuid):
return HttpResponse(f'uuid_test: {uuid}')
测试:
按照从上往下的顺序依次测试和匹配。
注意:测试uuid要注释掉str
笔记:
-
从URL中捕获值,请使用尖括号
-
捕获的值可以选择包含转换器类型。例如,用于 <int:name>捕获,前面的int指整数参数,name是参数的名称
-
没有必要添加一个前导斜杠,因为每个URL都有,例如,使用articles而不是/articles。
-
示例请求说明:
- /articles/2005/03/ 匹配列表中的第三个条目。Django会调用这个函数,views.month_archive(request, year=2005, month=3)
- /articles/2003/*会匹配列表中的第一个模式,而不是第二个模式,因为模式是按顺序测试的,而第一个模式是第一个要传递的测试。看看利用匹配顺序插入像这样的特殊情况。在这里,Django会调用这个函数 views.special_case_2003(request)
- /articles/2003*不匹配任何这些模式,因为每种模式都要求URL以斜线结尾,不过在浏览器访问时,会自动添加 / 。
- /articles/2003/03/building-a-django-site/ 将匹配最终模式。Django会调用这个函数 。views.article_detail(request, year=2003, month=3, slug=“building-a-django-site”)
路径转换器
- str:匹配任何非空字符串,不包括路径分隔符’/'。如果转换器不包含在表达式中,这是默认值。
- int:匹配零或任何正整数。返回一个int。
- slug:匹配由ASCII字母或数字组成的字符串,以及横线和下划线字符。例如, building-your-1st-django_site可以匹配,django_@site是不可以匹配的。
- uuid:匹配格式化的UUID。为防止多个URL映射到同一页面,必须包含破折号,并且字母必须是小写。例如,075194d3-6885-417e-a8a8-6c931e272f00。返回一个 UUID实例。
- path:匹配任何非空字符串,包括路径分隔符 ‘/’,可以匹配完整的URL路径,而不仅仅是URL路径的一部分str,使用时要谨慎,因为可能造成后续的所有url匹配都失效。
UUID的生成:
#获取uuid
import uuid
print(uuid.uuid1())
4.4 自定义路径转换器
转换器是一个包含以下内容的类:
- 一个regex类属性,作为一个re匹配字符串。
- to_python(self, value)方法,它处理匹配的字符串转换成要传递到视图函数的类型。
- to_url(self, value)方法,用于处理将Python类型转换为URL中使用的字符串。
定义方式如下:
- 新建一个converters.py文件,在文件中定义一个FourDigitYearConverter类:
class FourDigitYearConverter(object):
regex = '[0-9]{4}'
def to_python(self, value):
return int(value)
def to_url(self, value):
return '%04d' % value
- 使用register_converter()方法在URLconf中注册自定义转换器类 :
from django.urls import path, include
from . import views
from .converters import FourDigitYearConverter
from django.urls import register_converter
register_converter(FourDigitYearConverter, 'yyyy')
urlpatterns = [
# 自定义路径转换器path
path('articles/<yyyy:year>/', views.articles_year)
]
4.5 使用正则表达式
使用正则表达式匹配路径,请使用 re_path()而不是path()
在Python正则表达式中,命名正则表达式组的语法是(?P<name>pattern),其中name是组的名称,并且 pattern是一些要匹配的模式
示例:
from django.urls import path, re_path
from . import views
# url() 是 re_path 的别名,不推荐使用
urlpatterns = [
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.articles_year),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.articles_year_month()),
re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.articles_year_month_slug)
]
注意事项:
- 匹配的URL会受到一些限制。例如,年份10000将不再匹配,因为年份整数限制为四位数字
- 无论正则表达式匹配什么类型,每个捕获的参数都以字符串的形式发送到视图
- 除了命名的组语法,例如(?P<year>[0-9]{4}),也可以使用较短的未命名组,例如([0-9]{4}),但是不建议这样使用,会引起未知的匹配
嵌套参数:
from django.urls import re_path
urlpatterns = [
# 不推荐, 匹配 blog/page-3/
re_path(r'^blog/(page-(\d+)/)?$', blog_articles),
# 推荐 ,匹配:comments/page-2/ 路径到 comments(request, page_numer)
re_path(r'^comments/(?:page-(?P<page_number>\d+)/)?$', comments),
]
def comments(request, page_number):
return HttpResponse(f'comments:page_number:{page_number}')
4.6 使用默认值
URLConf中:
from django.urls import path
from . import views
urlpatterns = [
path('blog/', views.do_page),
path('blog/<int:num>/', views.do_page),
]
views中:
def do_page(request, num=1):
print(num)
return HttpResponse(f'do_page:num:{num}')
默认值
4.7 错误处理
- handler400- 状态码400
- handler403- 状态码403
- handler404- 状态码404
- handler500- 状态码500
- 在 settings中修改配置:
DEBUG = False
ALLOWED_HOSTS = ['*']
- 在主应用的urls中配置:
# app01是子应用
handler404 = "app01.views.page_not_found"
- 在app01应用的views中添加函数page_not_found:
def page_not_found(request, exception):
return HttpResponse('自定义的404错误页面')
- 浏览器测试访问,找不到匹配的路由
4.8 引用其他URL调度器
- 多个 patterns
from django.urls import include, path
extra_patterns = [
path('blog/', views.do_page),
path('blog/<int:num>/', views.do_page),
]
urlpatterns = [
path('', include(extra_patterns))
]
- 使用include消除重复前缀
from django.urls import path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/history/', views.history),
path('<page_slug>-<page_id>/edit/', views.edit),
path('<page_slug>-<page_id>/discuss/', views.discuss),
path('<page_slug>-<page_id>/permissions/', views.permissions),
]
# 修改为:
from django.urls import include, path
from . import views
urlpatterns = [
path('<page_slug>-<page_id>/', include([
path('history/', views.history),
path('edit/', views.edit),
path('discuss/', views.discuss),
path('permissions/', views.permissions),
])),
]
举例子
from django.urls import path, include, re_path
from . import views
extra_patterns = [
# 使用默认值
path('blog/', views.do_page),
path('blog/<int:num>/', views.do_page),
]
urlpatterns = [
path('articles/', include([
path('2024/', views.articles_2024),
path('<int:year>/', views.articles_year),
path('<int:year>/<int:month>/', views.articles_year_month),
]))
]
- 传递捕获的参数
在主urls中配置:
urlpatterns = [
path('admin/', admin.site.urls),
# 这里捕获username参数,类型为字符串
path('<username>/app01/', include('app01.urls'))
]
对应的 app01应用下的urls中配置:
urlpatterns = [
path('arg_test/', views.arg_test),
]
对应的app01应用下的views中编写函数:
def arg_test(request, username):
# 编写对应的业务逻辑
return HttpResponse(f'username {username}')
4.9 额外的参数
from django.urls import path
from . import views
urlpatterns = [
# 会传递给 views.param_test(request, user, password)
path('param_test/', views.param_test, {'user': 'root', 'password': '1234'}),
]
app01.views.py
def param_test(request, user, password):
return HttpResponse(f'user {user}, password {password}')
4.10 URL反向解析
url调度器除了从用户发起请求,到匹配对应的view,还能在python程序中调用进行匹配,通过 path或re_path 中 的name属性进行解析
- 在模板中,使用url模板标签
- 在Python代码中(主要是views),使用 reverse() 函数
- 在模型实例中,使用 get_absolute_url() 方法
示例:
app01
的urls
中配置:
from django.urls import path
from . import views
urlpatterns = [
path('reverse/', views.reverse_test),
path('articles/<int:year>/', views.year_archive, name='news-year-archive')
]
views.py
def reverse_test(request):
return HttpResponseRedirect(reverse('news-year-archive', args=(6666,)))
def year_archive(request, year):
return HttpResponse(f"year_archive: year:{year}")
测试:
2 在模板中测试
urls.py
from django.urls import path, include, re_path
from . import views
urlpatterns = [
path('do_html/', views.do_html),
]
- 模板中代码
<!--模板中: -->
<a href="{% url 'news-year-archive' 2004 %}">url反向解析测试</a>
- views.py跳转到页面
def year_archive(request, year):
return HttpResponse(f"year_archive: year:{year}")
def do_html(request):
return render(request, 'redirect_test.html')
4.11 命名空间
主要用于配合第 10 点 url反向解析 使用,多个不同的urls文件中可能配置同名的 name,那么为了进行区分,给不同的urls进行不同的命名,切记同一个项目下命名空间不能重复!
通过在 url调度器的模块中,定义 app_name = ‘app01’ 来命名
from django.urls import path
from . import views
# 定义,一般命名空间和子应用名相同,便于记忆
app_name = "app01"
urlpatterns = [
path('reverse/', views.reverse_test),
path('do_html/', views.do_html),
...
]
# 调用,一旦有了命名空间,调用时就必须使用app01: 前缀
def reverse_test(request):
return HttpResponseRedirect(reverse('app01:news-year-archive', args=(6666,)))
<a href="{% url 'app01:news-year-archive' 2004 %}">url反向解析测试</a>
命名空间可以进行嵌套:
# 在 urls 中配置如下:
from django.urls import path
from . import views
# 定义命名空间,一般命名空间名和子应用名相同,便于记忆
app_name = 'app01'
extra_patterns = (
[
path('app_name/', views.app_name, name='app_name'),
],
# 此处就是嵌套的命名空间
'extra'
)
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('extra/', include(extra_patterns)),
...
]
# 在模板中使用:
<a href="{% url 'app01:extra:app_name' %}">点击链接</a>