python之第一个Django项目
## URL 是 Uniform Resource Locator 的简写,简称统一资源定位符,一个 URL 一般由以下几部分组成:
scheme://host:port/path/?query-string=xxx#anchor
* scheme 代表的是访问的协议,一般为 http 或者 https 以及 ftp 等。
* host 主机名或域名,比如:www.baidu.com。
* port 端口号,http 协议默认端口号为80,https 默认为443。
* path 查找路径,比如:https://home.cnblogs.com/u/lanten2020 中的 u/lanten2020。
* query-string 查询字符串,比如:https://www.jianshu.com/search?q=python&page=1&type=note 中的 q=python、page=1 和 type=note 都是查询字符串。
* anchor 锚点,只是前端用来做页面定位的功能,后台一般都不用管。
注意:URL 中所有字符都是 ASCII 字符集,如果出现非 ASCII字符,比如中文,浏览器会先进行编码再进行传输。
## 创建第一个 django 项目:
1、通过命令行方式创建:
> 首先要进入到安装了 django 工具的虚拟环境,然后在虚拟环境中执行下面命令:
''' django-admin startproject [项目名称] '''
> 进入到刚才创建的项目目录下,通过下面命令启动项目:
''' python manage.py runserver '''
> 验证项目是否启动成功,在浏览器中输入下面地址:
''' http://127.0.0.1:8000/ '''
如何返回页面是正常的,表示 django 项目正常启动。
2、通过 pycharm 方式创建:
> 点击 pycharm 左上角的“File”-“New Project...”,然后在“New Project”窗口中选择“Django”,并在右侧的填写项目路径、项目名称,选择 python 虚拟环境,然后点击“Create”如下图所示:
> 项目创建完成后,点击有上角的绿色三角符号启动项目,如下图所示:
> 验证项目是否启动成功,在浏览器中输入下面地址:
''' http://127.0.0.1:8000/ '''
如何返回页面是正常的,表示 django 项目正常启动。
## 项目结构分析:
* manage.py 整个项目的主函数文件,以后和项目的交互基本上都是基于这个文件,输入:python manage.py help 可以查看函数包含的子命令。
* settings.py 保存项目所有的配置信息。
* urls.py 用来做 url 与视图函数映射的,每当有一个新的请求,就会从这个文件中找到匹配的视图函数。
* wsig.py 专门用来做部署的,不需要去修改。
## Django 推荐的项目规范:
按照功能或者模块进行分层,分成一个个的 app,所有和某个模块相关的视图都写在对应的 app 的 views.py 文件中,并且模型和其他模块也类似。然后 django 已经提供一个比较方便创建 app 的命令:"python manage.py startapp [app名称]"。
## DEBUG 模式:
1、如果开启了 DEBUG 模式,那么以后修改了 Django 项目的代码,然后按下 "ctrl+s",Django 就会自动的帮我们重启项目。
2、如果开启了 DEBUG 模式,在 Django 项目出现 bug 时,浏览器中会打印相信的错误信息。
3、在生产环境中,禁止开启 DEBUG 模式,不然会存在很多的安全隐患。
4、如果将 DEBUG 设置为 False,那么必须要设置 ALLOWED_HOSTS。
## ALLOWED_HOSTS:
这个变量是用来限定别人只能通过此变量中设置的 IP 地址或者域名进行访问。
## 视图函数:
1、视图函数的第一个参数必须是 request,这个参数不能缺少。
2、视图函数的返回值必须是 "django.http.response.HttpResponseBase" 的子类的对象。
## URL 映射:
1、因为在 "settings.py" 文件中的 "ROOT_URLCONF" 变量指定了 "urls.py" 文件,因此 django 会自动去 "urls.py" 查找指定的视图函数映射。
2、在 "urls.py" 文件中所有的映射都应放在 "urlpatterns" 变量中。
3、所有映射都是使用 "path" 函数或者是 "re_path" 函数进行包装。
## URL 中添加参数,传给视图函数:
1、采用在 url 中使用变量的方式:在 "path" 函数的第一个参数中添加 "<参数名>" 方式可以给视图函数传递参数,在视图函数中要有一个和参数名一样的变量名与之对应,否则将提示无法查找到此参数。其中 url 中可以传递多个参数。代码展示如下:
""" python viems.py def book_detail(request, book_name): text = "您正在查找的书籍名称为:%s" % book_name return HttpResponse(text) """
2、采用查询字符串方式:在访问地址中的 "?xxx=xxx" 的形式来传递参数给视图函数,视图函数不需要添加任何变量,只需要通过 request.GET['id'] 或者 request.GET.get('id') 即可获取传递的参数。所有以 "?xxx=xxx" 形式都属于 get 请求,因为 'GET' 请求的类型是一个类似于字典的数据类型,所有获取的值跟获取字典的方式一致。代码展示如下:
"""python viems.py def author_detail(request): author_id = request.GET.get('id') # id 为用户访问浏览器是传递参数的键名,id=1 author_name = request.GET['name'] text = "您所查询作者的ID为:%s,作者为:%s" % (author_id, author_name) return HttpResponse(text) """
## url 参数的转换器(django内置5中类型的转换器):
1、str:匹配除了斜杆 "/" 的所有字符串,例如:a1$sa。代码展示如下:
"""python urls.py path("book/convertor/<str:convertor_id>/", convertor_detail) """
2、int:匹配一个或者多个阿拉伯数字,例如:123976。代码展示如下:
"""python urls.py path("book/convertor/<int:convertor_id>/", convertor_detail) """
3、path:匹配除了的所有字符串,例如:a%s-d/@!1s39。代码展示如下:
"""python urls.py path("book/convertor/<path:convertor_id>/", convertor_detail) """
4、uuid:匹配满足"uuid.uuid4()"函数返回的字符串类型格式,例如:b30c8b8a-752b-4a45-8e78-9f81a648853f。代码展示如下:
"""python urls.py path("book/convertor/<uuid:convertor_id>/", convertor_detail) """
5、slug:匹配一个或者多个英文中的横杠"-",下划线"_",字符串或者阿拉伯数字,例如:-sa_3-asd_a12。代码展示如下:
"""python urls.py path("book/convertor/<slug:convertor_id>/", convertor_detail) """
注意:如果内置转换器参数值中有"#"和"?",浏览器会自动的在"#"和"?"前面加上"/","/"和它后面的所有值都不会被匹配。内置转换器类:from django.urls import converters
## urls 分层模块使用:
当项目变得越来越大,如果所有的 url 都写在同一个 "urls.py" 中,将会不方便进行管理,因此可以在每个 app 自己的 url 放置到自己的 app 中进行管理,通常是在 app 项目中创建一个 "urls.py" 文件进行统一存储所有这个 app 的 url,然后在进行如下相关配置:
1、在主项目的 "urls.py" 文件中通过 include() 函数将 app 的 url 导入进来,代码如下:
"""python urls.py from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('book/', include('book.urls')) # 通过 include() 函数将 book 这个 app 中的所有 url 设置导入进来 ] """
2、在 app 的 "urls.py" 中,需要定义一个 "urlpatterns" 变量,并将所有的 URL 匹配放到这个变量中,代码如下:
"""python urls.py from django.urls import path from .views import * urlpatterns = [ # 访问图书首页 path("", book), # 通过 URL 传递参数给视图函数,变量名必须和视图函数的一致 path("book_detail/<book_name>/", book_detail), ] """
3、最后在 app 中编写 url 设置的视图函数代码。
## url 命名:
在开发过程中 url 是经常变化的,如果将其写死的话,将会对后期代码维护带来得多的不便,因此可以给 url 取个名字。只需在 "urls.py" 文件中的 path() 函数中添加一个 "name='xxxx'" 的参数即可,以后在需要使用 url 时候可以通过该 name 进行反转来获取 url 信息,即使修改了 url 前缀,仍可以正常的找到该 url 地址。示例代码如下:
"""python urls.py urlpatterns = [ path("", views.index, name="index"), # 前端首页 path("signin/", views.login, name="login"), # 前端登录首页 ] python views.py from django.shortcuts import redirect,reverse def index(request): # 如果用户直接访问首页,没有进行登陆的话,将跳转到登陆页面, ?usernam=xxxx username = request.GET.get("username") if username: return HttpResponse("前端首页") else: login_url = reverse("login") # reverse() 函数可以根据给定的 url 名称反转成 url 匹配的路径 print(login_url) return redirect(login_url) # redirect() 函数用来重定向 url """
## 应用命名空间:
在多个 app 项目之间,存在同名的 url 是正常的事情,此时为了避免在反转 url 时候程序产生混淆,可以使用在每个 app 自己的 "urls.py" 文件中定义一个 "app_name='xxx'" 应用命名空间变量来区分。示例代码如下:
"""python url.py app_name = "front" # app 应用命名空间,用于让 django 可以区分不同的 app urlpatterns = [ path("", views.index, name="index"), # 前端首页 path("signin/", views.login, name="login"), # 前端登录首页 ] """
以后在进行 url 反转时候,直接使用 "app_name:url_name" 的方式进行反转即可。示例代码如下:
"""python views.py from django.shortcuts import redirect,reverse def index(request): # 如果用户直接访问首页,没有进行登陆的话,将跳转到登陆页面, ?usernam=xxxx username = request.GET.get("username") if username: return HttpResponse("前端首页") else: login_url = reverse("front:login") # reverse() 函数可以根据给定的 url 名称反转成 url 匹配的路径 print(login_url) return redirect(login_url) # redirect() 函数用来重定向 url """
## 应用命名空间和实例命名空间区别:
一个 app 可以同时创建多个实例,并且多个 url 也可以映射到同一个 app 中,因此只是通过应用命名空间来进行 url 的反转的话,将会产生 url 的混淆,此时可以通过给 url 中 include() 函数添加一个 "namespace" 实例命名空间变量来区分当前访问的具体是哪个实例,示例代码如下:
"""python url_name_demo/urls.py urlpatterns = [ # path('admin/', admin.site.urls), path("", include("front.urls"), name="front"), # name 参数给 url 命令,后续调用时,可以直接通过 name 值来匹配。 path("cms1/", include("cms.urls", namespace="cms1"), name="cms"), # namespace 参数是实例命名空间,通过实例命名空间可以区分不同前缀的url转跳到同一个视图函数。 path("cms2/", include("cms.urls", namespace="cms2"), name="cms"), ] """
然后在需要进行反转时候,就可以通过该实例命名空间来指定具体的 url,示例代码如下:
"""python cms/views.py def index(request): username = request.GET.get("username") if username: return HttpResponse("CMS首页") else: # 通过 request 的 GET 方法可以获取当然 URL 设置的实例命名空间 current_namespace = request.resolver_match.namespace print(current_namespace) return redirect(reverse("%s:login" % current_namespace)) """
注意:在指定一个 "namespace" 实例命名空间时,必须要在对应的 app 中指定 "app_name" 应用命名空间,否则项目启动将报错。
## include 函数详解:
""" 1. include(module, namespace=None),示例代码如下: """ python path("book/", include("book.urls"), name="book") """ 2. include((pattern_list, app_namespace), namespace=None),示例代码如下: """ python path("book/", include(("book.urls", "book"), name="book")) """ 3. include(pattern_list),示例代码如下: """ python from movie import views path("movie/", include([ path("", views.index), path("list/", views.movie_list), ])) """ * module: 子 url 的模块字符串。 * namespace=None: 实例命名空间。 * pattern_list: 必须是一个可迭代的 path 或者 re_path 清单。 * app_namespace: 应用命名空间。 """
## re_path 函数详解:
re_path 函数和 path 函数的作用都是一样的,区别在于 re_path 可以在 url 中使用正则表达式来匹配用户传递的参数值是否合规。在写正则表达式时候建议使用原生字符串的方式,即以 'r' 开头的字符串。 并且在正则表达式中将需要获取的参数用括号 () 括起来,然后通过 ?P 后面跟需要获取的参数变量名称,后续在视图函数通过这个变量获取用户输入 URL 的值。示例代码如下:
"""python article/url.py from django.urls import re_path from . import views urlpatterns = [ re_path(r"^$", views.article, name="index"), # 正则表达式中将需要获取的参数用括号 () 括起来,?P 后面跟需要获取的参数变量名称,后续视图函数通过这个变量获取用户输入 URL 的值。 re_path(r"^list/(?P<year>\d{4})-(?P<month>\d{2})/$", views.article_list, name="article_list"), ] """ """python article/views.py from django.http import HttpResponse def article(request): return HttpResponse("文章首页。") def article_list(request, year, month): text = "当前文章的日期是:%s-%s" % (year, month) return HttpResponse(text) """
## reverse 函数详解:
1、在进行 url 反转时,可以通过在 reverse 函数后面手动拼接字符串的方式给 url 传递查询字符串参数。示例代码如下:
"""python viems.py from django.http import HttpResponse from django.shortcuts import redirect, reverse def index(request): username = request.GET.get("username") if username: return HttpResponse("前端首页") else: # 在进行 url 反转时,通过在 reverse 函数后面手动拼接字符串的方式给 url 传递查询字符串参数 login_url = reverse("login") + "?next=%2F" return redirect(login_url) def login(request): return HttpResponse("登陆首页") """
2、在进行 url 反转时,可以通过给 reverse 函数添加关键字参数变量的方式传递指定的参数到需要反转的 url 中,示例代码如下:
"""python viems.py from django.http import HttpResponse from django.shortcuts import redirect, reverse def index(request): username = request.GET.get("username") if username: return HttpResponse("前端首页") else: # 通过给 reverse 函数添加关键字参数变量的方式传递指定的参数到需要反转的 url 中 article_url = reverse("article", kwargs={'article_id': 1, 'page_id': 5}) return redirect(article_url) def article_detail(request, article_id, page_id): text = "文章ID为:%s,页数ID为:%s" % (article_id, page_id) return HttpResponse(text) """
## 自定义 URL(PATH) 转换器:
需求:实现一个获取文章列表的 demo,用户可以根据 "/article/list/文章列表分类/" 的方式来获取文章列表。其中文章列表分类是采用 "list1+list2+list3..." 的方式拼接的,当只有一个列表分类时,不需要加号。需求示例如下:
""" # 第一种:获取 python 分类下的文章 /article/list/python/ # 第二种:获取 python 和 django 分类下的文章 /article/list/python+django/ # 第三种:获取 python 和 django 还有 flask 分类下的文章 /article/list/python+django+flask/ 以此类推... """
实现方式一:通过在 "urls.py" 文件中给 re_path 函数添加正则表达式来匹配用户传递的 url 参数,然后将参数传给视图函数的变量即可。示例代码如下:
"""python article/urls.py from django.urls import path, re_path from . import views urlpatterns = { path("", views.article, name="index"), # \w: [0-9a-zA-Z_] re_path(r"list/(?P<categories>\w+|(\w+\+\w+)+)/", views.article_list, name="article_list"), } """
实现方式二:通过自定义 url 转换器,然后在 url 中配置该转换器,就可以将用户传入的参数获取了。示例代码如下:
"""pyrhon article/converters.py from django.urls import register_converter class CategoryConverter(object): regex = '\w+|(\w+\+\w+)+' # 当程序调用转换器时,会执行 to_python 函数 def to_python(self, value): # 将 "python+django+java" 转换成 "['python', 'django', 'java']" result = value.split("+") return result # 当程序进行 url 反转时,会执行 to_url 函数 def to_url(self, value): # 判断 value 是否为 list 类型 if isinstance(value, list): # 将 "['python', 'django', 'java']" 反转成 "python+django+java" result = "+".join(value) return result else: # 当 value 类型不为 list 时,抛出异常 raise RuntimeError("反转的 URL 的分类参数必须为列表类型!") # 将自定义的转换器注册到代码中 register_converter(CategoryConverter, "category") """ """python article/urls.py from django.urls import path, re_path from . import views urlpatterns = { path("", views.article, name="index"), path("list/<category:categories>/", views.article_list, name="article_list"), } """
建议:为方便管理将自定义的转换器单独存放在一个名为 "converters.py" 中,然后在 "__init__.py" 添加一行 "from . import converters" 代码,就可以在启动程序时候自动将自定义转换器注册到代码中。
## url 映射时指定默认参数:
在视图函数中给变量指定一个值即可。示例代码如下:
"""python views.py from django.http import HttpResponse books_list = [ "三国演义", "我们仨", "水浒传" ] def books(request, page=0): return HttpResponse(books_list[page]) """ """python urls.py from django.urls import path from . import views urlpatterns = [ path("", views.books, name="index"), path("books/<int:page>/", views.books, name="books"), ] """