Featured image of post 学习《Django完整初学者指南》的笔记3

学习《Django完整初学者指南》的笔记3

引言

这一节深入学习两个基本概念:URLsForm,建立 templates 并编写大量的单元测试。

首先,现修改下模型文件,增加一些内容:

boards/models.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
class Topic(models.Model):
  	# 省略部分...
    # 为Topic增加最新更新时间字段,并设置 `auto_now=True` 表示每次save都会更新此字段为当前时间
    # 原文这里使用的是 `auto_now_add=True` 表示仅在新添加时更新此字段为当前时间
    last_updated = models.DateTimeField(auto_now=True)
    
class Post(models.Model):
  	# 省略部分...
    # 为Post添加更新时的用户,可以为 null,当设置 related_name='+' 时,不支持反向查询
    updated_by = models.ForeignKey(User, null=True, related_name='+')
                                 

当一个模型有两个相同模型的外键时,需要明确指定 related_name ,因为默认的 related_name 为 小写的模型名_set ,当有两个时就无法区分,必须明确指定 related_name 。例如:本项目Post 与 User 有两个外键分别为:created_by 和 updated_by, 默认的related_name都是 post_set ,此时需要明确为两个外键明确指定不同的 related_name

模型修改了需要重新生成数据迁移文件和数据库

1
2
$ python manage.py makemigrations
$ python manage.py migrate

URLs

首先,我们要实现一个列举所有Topic的页面,先在urls.py里加入路由

myproject/urls.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# myproject/urls.py

from django.contrib import admin
from django.urls import path

from boards import views

urlpatterns = [
  	# 教程中使用的Django版本是1.11,我整理时Django 2.2已经是长期支持版了, 
    # 老版本的路有使用 url 通过正则表达式进行匹配解析,
    # 如:url(r'^$', views.home, name='home') ,
    # 2.0以后使用 path 更加简洁,如果使用正则表达式实现更复杂的解析,
    # 可以使用 re_path 效果和老版本的 url 一致。
    path("", views.home, name="home"),
    path("boards/<int:pk>/", views.board_topics, name="board_topics"),
    path("admin/", admin.site.urls),
]

一个项目可以在各个 apps 中设置多个 urls.py ,但Django需要一个特殊的 urls.py 作为起点,默认在项目根目录下的 urls.py 就是这个特殊的URLconf, 可以在项目配置文件 settings.py 中配置

1
ROOT_URLCONF = 'myproject.urls' # 在创建项目时这个配置Django就帮你自动生成了,可以自定义修改

在URL 配置文件中,path函数使浏览器的 url 和 Django的 view视图对应,例如上面的path("", views.home, name="home"), 就是将网站的跟路径和views.home视图对应起来,当访问 http://127.0.0.1:8000/ (当部署后可能是你配置的域名和端口) 时就由 views.home 视图解析,函数具体参数为:

1
2
3
4
5
6
7
8
9
# 实时上Django 2.2 中 path 和 re_path 都指向 _path 函数,是 _path 函数的一个引用,只是 Pattern 不同,一个路由匹配模式(RoutePattern),一个是正则匹配模式(RegexPattern)
def _path(route, view, kwargs=None, name=None, Pattern=None):
  ...

# partial 是python functools 中的高阶函数,作用是为一个函数指定默认的变量,再取一个别名,以下语句类似于:
# def path(route, view, kiew, kwargs=None, name=None, Pattern=RoutePattern):
# 	_path(route, view, kwargs, name, Pattern)
path = partial(_path, Pattern=RoutePattern)
re_path = partial(_path, Pattern=RegexPattern)
  • route:一个 URL 规则匹配字符串,这里并不会区分 GETPOST方式,匹配规则将会根据 _path 函数的 Pattern 参数区分。
  • view: 一个处理URL请求的视图函数,它也接受一个 django.conf.urls.include 函数,用于指定一个外部的 urls.py 配置文件来分级处理子路由,这样可以很好的将一类功能的路由放在一起,路由分级也较为合理。例如:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# myproject/urls.py
from django.urls import path

urlpatterns = [
  	# 路由 http://127.0.0.1:8000/boards/ 下的所有请求都由另一个的 urls.py 来处理
    path('boards/', include('boards.urls'))
]

# boards/urls.py
from django.urls import path
from boards import views

urlpatterns = [
  	# 对应路由 http://127.0.0.1:8000/boards/ 
    path('', views.board_list(), name='board_list'), 
  	# 对应路由 http://127.0.0.1:8000/boards/new/
  	path('new/', views.board_new(), name='board_new'),
]
  • kwargs:通常给前面的 view 函数指定默认的参数,一般用于可重用视图, 我们不会经常用到。
  • name: 给 URL 指定一个唯一标示名,它是较为重要的功能,可以在编程时通过这个名字转换为对应的URL,而不是通过硬编码在程序的其它地方写入URL

URLs 基本用法

1
2
3
4
5
6
7
from django.urls import path
from boards import views

urlpatterns = [
    path('', views.home, name='home'),
    path('about/', views.about, name='about'),
]

可以创建更深的URL结构:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.urls import path
from boards import views

urlpatterns = [
    path('', views.home, name='home'),
    path('about/', views.about, name='about'),
    path('about/company/', views.about_company, name='about_company'),
    path('about/author/', views.about_author, name='about_author'),
    path('about/author/vitor/', views.about_vitor, name='about_vitor'),
    path('about/author/erica/', views.about_erica, name='about_erica'),
    path('privacy/', views.privacy_policy, name='privacy_policy'),
]

对应的 view 函数可能是这样的结构:

1
2
3
4
5
6
7
8
def about(request):
    # do something...
    return render(request, 'about.html')

def about_company(request):
    # do something else...
    # return some data along with the view...
    return render(request, 'about_company.html', {'company_name': 'Simple Complex'})

URLs高级用法

可以通过前文提到的 RoutePatternRegexPattern 灵活动态匹配 URLs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
from django.urls import path
from boards import views

urlpatterns = [
  	# 匹配 boards/12/12/1
  	# year、month、day 都是整数就可以
    path('boards/<int:year>/<int:month>/<int:day>/', views.boards_archive, name='boards_archive1'),
  	# 匹配 boards/2012/12/13
  	# year、month、day 必须是包含 0-9 的数字字符串, 
    # year必须是4位,month和day必须是2位
    # 会组成一个字典传入 view 函数,类似:
    # {"year": 2012, "month": 12, "day": 13}
    re_path(r'^boards/(?P<year>[0-9]{4})/(?P<month>[0-9]{2}/(?P<day>[0-9]{2}))/$', views.boards_archive, name='boards_archive2'),
  	# 如果正则中不加入 ?P<paramter> 则会将参数生成一个元组按照位置将参数传入
  	# 类似: (2012, 12, 13)
  	re_path(r'^boards/([0-9]{4})/([0-9]{2}/([0-9]{2}))/$', views.boards_archive, name='boards_archive2'),
]

以上两种方式 path 函数写路径更自然更简洁也更高效,re_path 函数方式匹配更精准,默认情况尽量使用 path 函数进行匹配,当 path 无法满足需求时,使用 re_path 函数匹配。

对应的 view 函数

1
2
3
4
5
6
7
# view 函数可以直接接受 url 匹配的变量
def board_topics(request, year, month, day):
    # do something...

# 或
def board_topics(request, *args, **kwargs):
  	# do something...

深入理解,举例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# urls.py
from django.contrib import admin
from django.urls import path, re_path

from boards import views

urlpatterns = [
    re_path(r"^test/(?P<id>\d+)/$", views.test, name="test1"),
    re_path(r"^test/(\w+)/(\w+)/$", views.test, name="test2"),
    path("test/<str:name>/", views.test, name="test3"),
]
1
2
3
4
5
# views.py
from django.shortcuts import HttpResponse

def test(request, *args, **kwargs):
    return HttpResponse(f"args:{args}<br>kwargs:{kwargs}")

url和view以及浏览器显示的内容:

urlview nameresponse
/test/123test1args:()
kwargs:{‘id’: ‘123’}
/test/abc/12atest2args:(‘abc’, ‘12a’)
kwargs:{}
/test/lee/test3args:()
kwargs:{’name’: ’lee’}
comments powered by Disqus
Built with Hugo
主题 StackJimmy 设计