Python入门自学进阶-Web框架——3Django的URL配置

Posted kaoa000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python入门自学进阶-Web框架——3Django的URL配置相关的知识,希望对你有一定的参考价值。

了解一下Django的配置文件settings.py:

"""
Django settings for MyPySite project.

Generated by 'django-admin startproject' using Django 3.2.11.

For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
import os
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-5oc(5qnia&=t80t#g5%+m-nhu)kmpf7jl6%u8n=&refp&ir0+w'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01.apps.App01Config',
]

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',
]

ROOT_URLCONF = 'MyPySite.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_APPLICATION = 'MyPySite.wsgi.application'


# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases

DATABASES = 
    'default': 
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    



# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators

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',
    ,
]


# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True


# Static files (CSS, javascript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/

STATIC_URL = '/static/'

# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

INSTALLED_APPS:是配置本项目中不同的应用,前面六个是Django的自己应用,最后一个'app01.apps.App01Config',是我们创建的应用
TEMPLATES:是模板的相关配置信息,最主要的就是他的路径,有了这个配置后,默认的模板都是保存在这个目录下的,如render()方法的第二个参数,就是写的模板的名称,这里只写相对路径就可以了,最终由Django合成绝对路径,如“userinfo.html”,实际上最后前面会加上D:\\MyPySite\\templates,最终是D:\\MyPySite\\templates\\userinfo.html。
STATIC_URL:是对静态文件夹的一个别名,这里说的静态文件夹,可以理解为存放网站图片、js脚本等的文件夹。
STATICFILES_DIRS:这是定义静态文件夹具体信息的配置项,默认settings配置文件中没有,其配置如下:STATICFILES_DIRS=(os.path.join(BASE_DIR,"statics_dir"),),这是与STATIC_URL配合使用的,将js脚本文件存放与statics_dir文件夹下,在模板中引入js脚本文件时,只需写/static/jquery.js就行了。设置别名的好处是,如果我们不想使用statics_dir这个文件夹,而将相关文件移到新文件夹中时,模板文件中的相关文件引用不需要修改位置信息。

 

 

 Django中urls.py,一般叫做路由系统。

系统怎么知道访问这个urls.py呢?在settings.py文件中有一个ROOT_URLCONF设置,设置的是在访问网址时通过哪一个url文件去匹配所请求的网址。我们的例子中设置如下:
ROOT_URLCONF = 'MyPySite.urls',即使用的是项目的urls模块,可以叫做根路由模块

对于1.x版本,使用的是url(),其后版使用path()或re_path(),re_path()相当于url(),可以在路径中使用正则。

模块不同:
django.urls path
django.conf.urls url

url(regex, view, kwargs=None, name=None)
path(route, view, kwargs=None, name=None)
re_path(regex, view, kwargs=None, name=None)

urlpatterns = [
   url(正则表达式, views视图函数,参数,别名),
]
urlpatterns = [
   re_path(正则表达式, views视图函数,参数,别名),
]

参数说明:

  • 一个正则表达式字符串
  • 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
  • 可选的要传递给视图函数的默认参数(字典形式)
  • 一个可选的name参数, 是url的别名,代表这个url,即匹配出来的url

django如何处理请求
当用户请求一个页面时,Django根据下面的逻辑执行操作:

(1)决定要使用的根URLconf模块(即urls.py文件)。通常,这是ROOT_URLCONF设置的值,但是如果传入的HttpRequest对象具有urlconf属性(由中间件设置),则其值将被用于代替ROOT_URLCONF设置。
(2)加载该模块并寻找可用的urlpatterns。 它是django.conf.urls.url()实例的一个列表。
(3)依次匹配每个URL模式,在与请求的URL相匹配的第一个模式停下来。(注意url在列表中的位置)
(4)导入并调用匹配行中给定的视图,该视图是一个简单的Python函数(被称为视图函数),或基于类的视图。 视图将获得如下参数:
    <1>一个HttpRequest 实例。
    <2>如果匹配的正则表达式返回了没有命名的组,那么正则表达式匹配的内容将作为位置参数提供给视图。
    <3>关键字参数由正则表达式匹配的命名组组成,但是可以被django.conf.urls.url()的可选参数kwargs覆盖。
    <4>如果没有匹配到正则表达式,或者过程中抛出异常,将调用一个适当的错误处理视图。

urlpatterns = [
    path('admin/', admin.site.urls),
    path('cur_time/',views.cur_time),
    path('userinfo/',views.userinfo),
    re_path(r'^art/2003/$',views.art2003),
    # 匹配以art/2003/开头和结尾的路由,严格匹配,如果去掉$,则任何以art/2003/开头的都能匹配,如art/2003/kko
    # 同理,如果去掉^,则任何以art/2003/结尾都能匹配,如ooo/ppp/art/2003/
    # re_path(r'^art/[0-9]4/$',views.year_art),
    # 以art/开头接4个数字接/,如art/2034/,但是如果写art/2003/,则先匹配的上是一条re_path,执行的是art2003,从上到下查找
    # re_path(r'^art/([0-9]4)/$',views.year_art1),
    # 加上括号,相当于在视图函数,即year_art参数增加了一个,有几个括号,视图函数就增加几个参数
    # 视图函数定义:year_art1(req,year),year参数接收()中的内容,最终转换为字符串类型
    # re_path(r'^art/([0-9]4)/([0-9]2)/$',views.year_art2),
    # 视图函数定义:year_art2(req,year,month),year参数接收第一个()中的内容,4位数字,month参数接收第二个()中的内容,2个数字,最终转换为字符串类型
    # 正则中小括号起分组功能,以上叫做无名分组,分组与参数对应,完全按照位置传参
    # re_path(r'^art/(?P<year>[0-9]4)/(?P<month>[0-9]2)/$',views.year_art2),
    # 这时传递的参数是有名字的,这就要求定义year_art2(req,year,month)参数名必须与尖括号<>中的名一致,否则出错
    # year_art2() got an unexpected keyword argument 'year'
    # url(r'^art/(?P<year>[0-9]4)/(?P<month>[0-9]2)/$',views.year_art2),
    # 上面的就叫做有名分组,视图函数中参数与分组名对应
    # re_path(r'^art/(?P<year>[0-9]4)/(?P<month>[0-9]2)/$',views.year_art3,"kkk":123,"foo":"bar"),
    # 视图函数year_art3需要四个参数,year,month,kkk,foo,且参数名称必须一致,这就是kwarg参数。
    # 如果后面定义的字典的key与正则分组的名相同,后面的会覆盖前面的。
from django.shortcuts import render,HttpResponse,redirect,reverse
from pathlib import Path
import os
import datetime
from app01 import models

# Create your views here.

def cur_time(request):
    base_p = Path(__file__).resolve().parent.parent
    templates = os.path.join(base_p, 'templates')
    print(templates)
    times = datetime.datetime.now()
    return render(request,"cur_time.html","abc":times)
    # 这里使用render来返回一个经过渲染的模板文件,这里就是cur_time.html
    # 这里cur_time.html是保存在templates目录中,可以直接写文件名
    # Django能够直接找到这个文件,是因为在settings.py配置文件中已经对相关目录进行了配置
    # 注意settings.py中TEMPLATES的设置,函数中的base_p和templates就是settings.py中的BASE_DIR和TEMPLATES
userlist = []
def userinfo(req):
    if req.method == "POST":
        u = req.POST.get("username",None)
        s = req.POST.get("sex", None)
        e = req.POST.get("email", None)
        # user = "username":username,"sex":sex,"email":email
        # userlist.append(user)
        models.UserInfo.objects.create(  #这就是将数据保存到数据库,即数据插入数据库
            username=u,
            sex=s,
            email=e
        )
    userlist = models.UserInfo.objects.all()  # 从数据库取所有数据
    return render(req,"userinfo.html","userlist":userlist)
    # else:
    #     return render(req,"userinfo.html")

def art2003(req):
    return HttpResponse("<h1>^xxx$</h1>")
def year_art(req):
    return HttpResponse("<h1>^xxx/[0-9]4/$</h1>")
def year_art1(req,year):
    return HttpResponse("<h1>"+year+"年</h1>")

def year_art2(req,year,month):
    return HttpResponse("<h1>"+year+"年"+month+"月</h1>")

def year_art3(req,year,month,kkk,foo):

    return HttpResponse(year + month + kkk + foo)

正则表达式中有分组,且为无名分组,怎对应view中函数参数要定义对应的位置参数,参数名任意,如果是命名分组,函数中的参数名要使用分组名,对于第三个参数,可以看做是命名分组,只不过其值不是从路径中获取的,是另外赋予的。

比较难理解的是name参数,是一个别名,代表当前的url:re_path(r'^art/01/$',views.art12,name="we1"),

这里对art/01/定义了一个别名we1,就可以在模板中使用:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action=% url "we1" % method="post">
    <input type="text">
    <input type="submit" value="submit">
</form>
</body>
</html>

注意% url "we1" %,是模板语言,意思就是寻找url中名为we1的路径,在urlpatterns中就找到了art/01/,进行替换,最终发送到浏览器的是action="/art/01/",使用别名的好处是前端,即模板,如这里的action,不需要考虑后台的变化,如后台的urlpatterns改变了:

re_path(r'^art01/2019/$',views.art12,name="we1"),

前台的action不需要修改,模板渲染时自动就替换修改了。

对URL含有分组的形式,如re_path(r'^art01/([0-9]4)/$',views.art12,name="we1"),直接如上面使用会出现错误:

 模板在渲染解析we1时缺少参数,没有找到不带参数的we1,修改url定义,修改view中的函数的定义,修改模板中url的使用,带上参数:

re_path(r'^art01/(?P<year>[0-9]4)/$',views.art12,name="we1")

def art12(req,year):
    if req.method == "POST":
        return HttpResponse("success!!!")
    return render(req,"test.html","va":year)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action='% url "we1" year=va %' method="post">
    <input type="text">
    <input type="submit" value="submit">
</form>
</body>
</html>

无名分组怎么将参数带入模板还没解决。

转换器(django2.0 以上默认使用的是path转换器)

函数 path() 具有四个参数,两个必须参数:route 和 view,两个可选参数:kwargs 和 name。即路由和视图是必填参数.与旧版本的参数主要区别就在于url()是要写正则表达式(regex)的路由,而path()是写的非正则路由(route)

urlpatterns = [
   path(非正则路由, views视图函数,参数,别名),
]

1、path()参数:route
    route 是一个匹配URL的准则(类似正则表达式)。当Django响应一个请求时,它会从urlpatterns的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。
    这些准则不会匹配GET和POST参数或域名。例如,URLconf在处理请求https://www.example.com/myapp/时,它会尝试匹配myapp/。处理请求https://www.example.com/myapp/?page=3 时,也只会尝试匹配 myapp/。
2、path()参数:view
    当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个HttpRequest对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。
3、path()参数:kwargs
    任意个关键字参数可以作为一个字典传递给目标视图函数。
4、path()参数:name
    为你的URL取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个URL模式。

route使用的是非正则表达式可以表示的普通路由路径。

注意:

(1)要捕获一段url中的值,需要使用尖括号,而不是之前的圆括号;
(2)可以转换捕获到的值为指定类型,比如例子中的int。默认情况下,捕获到的结果保存为字符串类型,不包含/这个特殊字符;
(3)匹配模式的最开头不需要添加/,因为默认情况下,每个url都带一个最前面的/。

尖括号中,前边是代表捕获的参数将转换成的类型,如str,即转换器,后面代表参数的名称,类似正则分组中的命名分组。

默认情况下,Django内置下面的路径转换器:

  • str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的;
  • int:匹配0和正整数,返回一个int类型
  • slug:可理解为注释。该转换器匹配任何ASCII字符以及连接符和下划线,比如’ building-your-1st-django-site‘;
  • uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象;
  • path:匹配任何非空字符串,重点是可以包含路径分隔符’/‘。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串。

比如要匹配一个视图中的函数路由,该函数有两个形参:

def peopleList(request,book_id)

第一个request是默认的,那么路径自动匹配该函数的第二个形参,匹配格式:<int:book_id>,并返回一个正整数或零值。

path('articles/2003/', views.special_case_2003),
    path('articles/<name1>/', views.special_case),
    # 这里设置了没有转换器的匹配项,默认匹配任何非空的字符,所以下面的articles/<int:year>/就不会被匹配了
    # 想捕获下面的数字,可以这两条对换位置
    path('articles/<int:year>/', views.year_archive),
    path('articles/<int:year>/<int:month>/', views.month_archive),
    path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail),
    # /articles/2005/03/ 将匹配第三条,并调用views.month_archive(request, year=2005, month=3);
    # /articles/2003/匹配第一条,并调用views.special_case_2003(request);
    # /articles/2003将一条都匹配不上,因为它最后少了一个斜杠,而列表中的所有模式中都以斜杠结尾;
    # /articles/2003/03/building-a-django-site/ 将匹配最后一个,并调用views.article_detail(request, year=2003, month=3, slug="building-a-django-site"

与re_path的比较及区别:

(1)将要匹配的确切URL受到更多限制。例如,年份10000将不再匹配,因为年份整数被限制为正好是四位数长。
(2)无论正则表达式进行哪种匹配,每个捕获的参数都将作为字符串发送到视图。
(3)当从使用re_path()切换为使用path(), re_path()反之亦然时,特别重要的是要注意视图参数的类型可能会发生变化,因此您可能需要调整视图。
(4)当命名的组与未命名的组两种样式混合使用时,任何未命名分组path('(\\d+)/',view)都会被忽略,只有命名分组path('(?P<year>\\d+)/',view)才会传递到视图函数。
(5)未命名分组将正则表达式匹配到的内容当作位置参数,命名分组将正则表达式匹配到的内容当作关键字参数

include()
在任何时候,urlpatterns都可以“include”其他URLconf模块。这本质上是一组位于其他url之下的“roots”。

from django.urls import include, path

urlpatterns = [
    path('community/', include('aggregator.urls')),
    path('contact/', include('contact.urls')),
]
每当Django遇到时include(),它都会截断直到该处匹配的URL的任何部分,并将剩余的字符串发送到包含的URLconf中以进行进一步处理。

另一种可能性是通过使用path()实例列表包括其他URL模块 。例如,考虑以下URLconf:

from django.urls import include, path

from apps.main import views as main_views
from credit import views as credit_views

extra_patterns = [
    path('reports/', credit_views.report),
    path('reports/<int:id>/', credit_views.report),
    path('charge/', credit_views.charge),
]

urlpatterns = [
    path('', main_views.homepage),
    path('help/', include('apps.help.urls')),
    path('credit/', include(extra_patterns)),
]
/credit/reports/将由credit_views.report()视图处理 。

测试:

urlpatterns = [
         path('admin/', admin.site.urls),
         path('app01/',include('app01.urls')),
]

在app01下建立urls.py

urlpatterns = [
    path('new/story/',views.app_story),

]
def app_story(req):
    return HttpResponse("应用urls测试")

以上是关于Python入门自学进阶-Web框架——3Django的URL配置的主要内容,如果未能解决你的问题,请参考以下文章

Python入门自学进阶-Web框架——20Django其他相关知识2

Python入门自学进阶-Web框架——2Django初识

Python入门自学进阶-Web框架——3Django的URL配置

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——21DjangoAdmin项目应用

Python入门自学进阶-Web框架——4HttpRequest和HttpResponse及模板