● urlpatterns = [
path('',index),
...
]
这里的''留空,相对应的地址就是:127.0.0.1:8000
● 'python包'和'目录(文件夹)'的区别:'python包'里面多了'init.py'
● 所谓的'django模型',就是数据库中的一张表
● 重启项目快捷方式---'ctrl+s';批量注释的快捷键:'ctrl+/'
● from django.http import HttpResponse:鼠标移动到HttpResponse,ctrl+B可以查看源码(点击'structure'可以查看更多源码结构)
● 使用'<>'来接收用户输入的url:
示例:
def front_user(request,user_id): # 注意这里的user_id类型是str,而不是int
text='your user_id is {}'.format(user_id)
return HttpResponse(text)
urlpatterns=[
...
path('user/
]
访问:http://127.0.0.1:8000/front/user/1,返回: your user_id is 1
● 查询字符串:以'?'开头,例如百度的'?wd=python',而其实质,就是get请求
在django操作中,url无需做多余的配置,view这么写:
对查询字符串进行判断,实现页面跳转的demo
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
def cms_index(request):
query_string=request.GET.get('username') # query_string=request.GET['username']
if query_string:
return HttpResponse("This is a cms index page.")
else:
print(''30)# 启动项目,访问这个url,可以在终端查看效果
# 这里reverse的作用,传入name名,获取url
print(reverse('cms_login'))
print('*' * 30)
return redirect(reverse('cms_login'))
'''
/cms/login
'''
review 示例:
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
def get_query_string(request):
username=request.GET.get('username') # 获取用户输入的关键词
local_url='?username='+username # 手动拼接url并输出
print(local_url)
if username:
return HttpResponse('Hello {},welcome to the person page.'.format(username))
else:
query_url=reverse('get_query_string')
print('*'*40)
print(query_url)
login_url=reverse('person_login')
return redirect(login_url)
def person_login(request):
return HttpResponse('This is person login page.')
• url命名---name
from django.urls import path
from . import views
urlpatterns=[
path('index',views.cms_index,name='cms_index'),
path('login',views.cms_login,name='cms_login')
]
若没有name这个参数,那么一旦网址发生变更,不仅view,url需要修改,而且前端HTML文档也需要修改,非常麻烦又易出错.
app命名:若有2个app的url name 参数值一样,django会混乱,所以引入'app_name'这个参数,防止出现name参数同名而出现的url错误
示例:
cms.urls
from django.urls import path
from . import views
app_name='cms' # 添加app_name参数
urlpatterns = [
path('index',views.index,name='index'),
path('login',views.login,name='login'),
]
cms.views
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
def index(request):
query_string = request.GET.get('username')
if query_string:
return HttpResponse("This is a cms index page.")
else:
print('' * 30)
print(reverse('cms:login')) # 注意此时的格式
print('' * 30)
return redirect(reverse('cms:login')) # 注意此时的格式
front.urls.py
from django.urls import path
from . import views
app_name='front' # 添加app_name参数
urlpatterns = [
path('index', views.index,name='index'),
path('login', views.login,name='login'),
]
front.views
from django.shortcuts import render,reverse,redirect
from django.http import HttpResponse
def index(request):
query_string = request.GET.get('username')
if query_string:
return HttpResponse("This is a front index page.")
else:
print('' * 30)
print(reverse('front:login')) # 反转url格式
print('' * 30)
return redirect(reverse('front:login'))
• 实例命名空间 namespace --- 属于 include()函数的参数
一个app可以创建多个实例,也就是说,可以使用多个url映射到同一个app
这就会产生一个问题:以后在做反转的时候,如果使用应用命名空间,那么就会发生混淆
示例:
main_url.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('front/',include('front.urls')),
path('cms1/',include('cms.urls'),), # cms1,cms2两个实例均映射到同一app:cms
path('cms2/',include('cms.urls')),
]
首先,访问:http://127.0.0.1:8000/cms1/index
跳转到:http://127.0.0.1:8000/cms1/login # 没毛病
再来,访问:http://127.0.0.1:8000/cms2/index
跳转到:http://127.0.0.1:8000/cms1/login # 显然就有问题了
现在使用实例命名空间---namespace参数
main_url
urlpatterns = [
...
path('cms1/',include('cms.urls',namespace='cms1'),),
path('cms2/',include('cms.urls',namespace='cms2')),
]
cms.views
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
def index(request):
query_string = request.GET.get('username')
if query_string:
return HttpResponse("This is a cms index page.")
else:
# 获取实例命名空间
current_namespace=request.resolver_match.namespace
# 使用实例命名空间做url跳转
return redirect(reverse('{}:login'.format(current_namespace))) # 这里如果把{}写死,显然不能达到目的,所以只能动态获取
小结:
<1>url反转---reverse('login'):反向解析域名,避免硬编码url
<2>应用空间命名:app_name='cms':避免不同app之间的name参数同名
<3>实例空间命名:include('cms.urls',namespace='cms1'),避免映射同一app的实例的url出现混乱
redirect和reverse的区别,示例:
main_url
...
path('book/login',book_views.login,name='book_login'),
path('book/',book_views.index,name='book_index'),
book.views
from django.shortcuts import render,reverse,redirect
from django.http import HttpResponse
def index(request):
return HttpResponse('index page')
def login(request):
username=request.GET.get('username')
if username:
return HttpResponse('login page')
else:
print(''40)
print(redirect('book_index'))
print(type(redirect('book_index'))) #
print('*' * 40)
print(reverse('book_login'))# /book/login
print(reverse('book_index'))# /book/
return redirect('book_index')
结果:
'''
...
/book/login
/book/
'''
结论:同样都是接收name名,reverse()返回的是普通的url字符串;redirect()返回的是一个'重定向的类'
当url只有一个name参数(或者有app_name参数)的时候,使用以下代码是没有区别的,都能实现效果:
return redirect('book_index')
return redirect(reverse('book_index'))
但是,reverse()不仅可以接收'appname:name'的形式,还可以接收'namespace:name'的形式,示例:
main_url
from django.contrib import admin
from django.urls import path,include
from front import views as front_views
from book import views as book_views
urlpatterns = [
...
#path('book/',include('book.urls')),
path('book1/',include('book.urls',namespace='book1')),
path('book2/',include('book.urls',namespace='book2')),
]
book.urls
from django.urls import path
from . import views
app_name='book'
urlpatterns = [
path('login/',views.login,name='login'),
path('',views.index,name='index'),
]
book.views
def login(request):
username=request.GET.get('username')
if username:
return HttpResponse('login page')
else:
namespace=request.resolver_match.namespace
print(namespace) # book2
print(''40)
url=reverse('{}:index'.format(namespace)) # /book2/ # 使用reverse的'namespace:name'形式获取动态的namespace
print(url)
#return redirect('book:index') # 这里若采用'appname:name'形式,无法实现url的动态跳转
return redirect(url)
小结:需要url跳转的时候,最好使用return redirect(reverse())的形式,这种形式'无视各种'name参数
● url转换器,先看示例:
def front_user(request,user_id):
text='your user_id is {}'.format(user_id)
return HttpResponse(text)
urlpatterns=[
...
path('user/
]
这种写法表明,只接收用户输入int类型的url,此时若输入str(比如输入'jim'),就报错.
我们导入转换器,查看源码类型:
from django.urls import converters # Ctrl+b
class IntConverter: # int
regex = '[0-9]+' # 只接收1-9的一个或者多个数字
...
url默认的接受用户输入的类型,就是str
class StringConverter: # str
regex = '[^/]+' # 除了反斜杠,其他字符串一律都可以
...
class UUIDConverter:
regex = '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
...
class SlugConverter(StringConverter):
regex = '[-a-zA-Z0-9_]+'
class PathConverter(StringConverter):
regex = '.+'
● include的三种用法:
最常见的用法,用来包含app的url路径
<1>path('front/',include('front.urls')),
include的第一个参数可以是元组,包含子url文件名和app_name,第二个参数是namespace参数
<2>path('test_include/',include(('test_include.urls','test_include'),namespace='test_include'), # 推荐写法
<3>接受path_list,示例:
urlpatterns = [
# 这么写,无需在app下面,再新建urls.py了,在这已经完成了...
path('test_include/',include([
path('index',views.include_index),
path('book',views.include_book),
])),
● reverse用法
reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None)
<1>传入path()的name参数:
main_url
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.front_index,name='front_index'),
path('login/',views.front_login,name='front_login'),
path('article/
]
app.views
def front_index(request):
username=request.GET.get('username')
if username:
return HttpResponse('Hello {},it is front index page.'.format(username))
else:
return redirect(reverse('front_login')) # 传入name值
<2>增加kwargs参数(就是'kwargs=dict()'),作用在于,比如需要输入额外的url参数,此时按照<1>的做法,就报错
def front_index(request):
username=request.GET.get('username')
if username:
return HttpResponse('Hello {},it is front index page.'.format(username))
else:
return redirect(reverse('front_article_id',kwargs=dict(article_id=3))) # http://127.0.0.1:8000 跳转
def front_article(request,article_id):
return HttpResponse('The article_id is {}'.format(article_id))
注意,若使用reverse跳转网址,涉及到'查询字符串参数'的,则需手动拼接,示例:
def front_index(request):
username=request.GET.get('username')
if username:
text='Hello {},it is front index page.'.format(username)
return HttpResponse(text)
else:
#url=reverse('front_article_id',kwargs=dict(article_id=3))
query='?username=jimgreen'
url = reverse('front_login')+query # 127.0.0.1:8000 跳转到 http://127.0.0.1:8000/login/?username=jimgreen
return redirect(url)
●re_path和path:没有特别需求,使用path足够了;若涉及正则的,就使用re_path
示例:
main_url
urlpatterns = [
......
path('article/
re_path(r'^movie/(?P
]
r'^/$'
app.views
def front_movie(request, year):
return HttpResponse('The year of movie is {}'.format(year))
访问:http://127.0.0.1:8000/movie/2019/
url若不是输入4位数(比如3位或5位),则找不到url了,因为在url正则里面作了严格的限制了
类似'动态url'这样的需求,一般都使用re_path()实现,例如现在有个需求是这样:
'article/python'
'article/python+django'
'article/python+django+js'
...
views
def article_list(request,categories):
text='你的文章列表是{}'.format(categories)
return HttpResponse(text)
main_url
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.index),
path('article/',include('article.urls')),
]
app.url
from django.urls import re_path
from . import views
urlpatterns = [
# 注意正则中,有一个表示'或者'的符号'|'
re_path(r'(?P
]
现在无论访问哪一个url,上述url均满足需求
小结:构建动态url,一般都是使用正则实现,至于正则要写在url里,还是自定义url转换器,看实际的情况
★ 自定义url转换器
相信之前已经了解到过一些django内置的url转换器,包括有int、uuid,等。 有时候这些内置的url转换器并不能满足我们的需求,因此django给我们提供了一个接口可以让我们自己定义自己的url转换器。
自定义url转换器按照以下五个步骤来走就可以了:
1.定义一个类,直接继承自object就可以了.
2.在类中定义一个属性regex,这个属性是用来限制url转换器规则的正则表达式。
3.实现to_python(self ,value)方法,这个方法是将ur1中的值转换一下, 然后传给视图函数的。
4.实现to_url(self,value)方法,这个方法是在做ur1反转的时候,将传进来的参数转换后拼接成一个正确的url。
5.将定义好的转换器,使用django.urls.converters.register_converter 方法注册到django中。
比如需求:
实现一个获取文章列表的demo,用户可以根据/articles/文章分类/的方式来获取文章。其中文章分类采用的是“分类1+分类2+分类3..."的方式拼接的,并且如果只有一个分类,那就不需要加号。示例如下:
- 第一种:获取python分类下的文章
/articles/python/
2.第二种:获取python和django分类下的文章
/articles/python+django/
3.第三种:获取python和django和flask分类下的文章
/articles/python+django+flask/
以此类推...
在“文章分类”参数传到视图函数之前要把这些分类分开来存储到列表中。比如参数是“python+django",那么传到视图函数的时候就要变成“[ 'python', 'django']”,以后在使用reverse反转的时候,限制传递“文章分类”的参数应该是一个列表,并且要将这个列表变成“python+django"的形式。
示例之前,先 review str和list之间的转换,示例:
string='python+django+flask'
不管是split()还是join(),都是str的方法
string=string.split('+')# string以'+'进行拆分,并返回list
string
['python', 'django', 'flask']
list_string=string
list_string
['python', 'django', 'flask']
'+'.join(list_string) # 把字符串'+'添加进list,并返回str
'python+django+flask'
代码如下:
定义一个converter.py文件
from django.urls import converters,register_converter
class CategoryConverter:
regex = r'\w+|(\w++\w+)+'
def to_python(self, value):
value=value.split('+')
return value
def to_url(self, value):
if isinstance(value,list):
value='+'.join(value)
return value
else:
raise RuntimeError('category转换类型失败')
register_converter(CategoryConverter,'cate') # 注册类,并且指定在url中的名称
在该app目录下的init.py文件中注册converter.py文件,否则该文件永远不会获得执行:
init.py
from . import converter
main_url
urlpatterns = [
...
path('article/',include('article.urls') ),
]
app_url
from django.urls import path,converters
from . import views
urlpatterns = [
path('',views.index),
path('list/
path('detail/
]
app_views
from django.shortcuts import render,reverse
from django.http import HttpResponse
def index(request):
return HttpResponse('It is article index page.')
def article_list(request,categories):
text='You enter article list {}.'.format(categories)
url=reverse('categories', kwargs=dict(categories=categories))
print(url) # /article/list/python+django+c
return HttpResponse(text) # You enter article list ['python', 'django', 'c'].
def article_detail(request,article_id):
reverse('detail',kwargs=dict(article_id=article_id))
return HttpResponse('Article detail page.')
小结:自定义这种url转换器,从可读性来说,比直接使用re_path()直观
★ url视图传递默认的参数:适用两个url都调用同一个视图的场景
app.views
def article_detail(request,article_id=1): # 定义article_id默认值为'1'
reverse('detail',kwargs=dict(article_id=article_id))
return HttpResponse('Article detail page.')
app.urls
urlpatterns = [
...
path('detail/',views.article_detail), # 两个url都调用同一个视图,这个url没有传参★
path('detail/
]
效果:
http://127.0.0.1:8000/article/detail/ # 访问成功
http://127.0.0.1:8000/article/detail/3 # 访问成功
模板系统
★ 加载Html文档
项目根目录下,新建'templates'文件夹,settings设置如下:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
# DIRS的模板优先级最高
'DIRS': [os.path.join(BASE_DIR, 'templates')] # 原先的配置是这样'DIRS': []
,
'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',
],
},
},
]
新建frontapp,视图如下:
from django.shortcuts import render
from django.http import HttpResponse
from django.template.loader import render_to_string
def index(request):
# 不仅把html文档转换成string,并且添加了CSS样式以后,也具有CSS样式的效果
string=render_to_string('index.html')
return HttpResponse(string)
urls
from django.contrib import admin
from django.urls import path
from front import views
urlpatterns = [
path('admin/', admin.site.urls),
path('',views.index),
]
index.html
This is an index page.
>使用render便捷封装---render(request, template_name, context=None, content_type=None, status=None, using=None)
views
def index(request):
# 第一个参数必须是request
return render(request,'index.html')
★ 渲染变量---{{ var }},这里的 var 名称必须和dict里的key名称保持一致,这样才能够映射正确的值(value)
基础示例:
views
def index(request):
# context接收的类型是dict
return render(request,'index.html',context=dict(username=Jim Green))
index.html
This is an index page.
>Your username is {{ username }}
# 传到html文档里,注意语法• 使用'.'访问属性示例:
views
from django.shortcuts import render
from django.http import HttpResponse
from django.template.loader import render_to_string
class Person():
def init(self,username):
self.username=username
def index(request):
p=Person('Kate Green')
return render(request,'index.html',context=dict(person=p))
index.html
This is an index page.
>Your username is {{ person.username }}
# 这里的person,是字典的键名.使用'.'号进行访问• 避免使用字典的属性和方法命名,比如使用keys作为context键名,那么文档里面{{ keys }}
会不会返回所有的键名?答案是不会...
● 常用的模板标签示例
• if基础示例:
views
def index(request):
context={
'age':16 # age映射的值作出调整,使html文档呈现不同的文字
}
return render(request,'index.html',context=context)
html
{% if age < 18 %}
Your age is less than 18.
{% elif age == 18 %}
Your age is 18.
{% else %}
Your age is more than 18.
{% endif %}
• if ... in ...示例:
views
def index(request):
context={
'books':[
'xiyouji',
'hongloumeng',
'shuihuzhuan',
'sanguoyanyi',
]
}
return render(request,'index.html',context=context)
html
{% if 'xiyouji' in books %}
xiyouji exists
{% else %}
xiyouji don't exist
{% endif %}
• for基础示例:
views
def index(request):
context={
'books':[
'xiyouji',
'hongloumeng',
'shuihuzhuan',
'sanguoyanyi',
]
}
return render(request,'index.html',context=context)
html
- {{ book }} # 注意语法,如果写成book,那就遍历出4个book了
{% for book in books reversed %} # 添加reversed,倒序遍历
{% endfor %}
效果:
•sanguoyanyi
•shuihuzhuan
•hongloumeng
•xiyouji
• 遍历字典示例:
views
def index(request):
context={
'books':[
'xiyouji',
'hongloumeng',
'shuihuzhuan',
'sanguoyanyi',
],
'person':{
'username':'Jim Green',
'age':20,
'sex':'man'
}
}
return render(request,'index.html',context=context)
html
{% for p in person %} # person指代字典:{{ person }}---{'age': 20, 'sex': 'man', 'username': 'Jim Green'}
- {{ p }}
{% endfor %}
效果:
•age
•sex
•username
可以看出,遍历了字典的键,一样的效果还可以这么写:
{% for key in person.keys %} # 注意这种字典的遍历方法
- {{ key }}
{% endfor %}
还可以使用dict.values(字典的值),dict.items(返回的键对的元组),示例:
# 遍历所有的键值对
- {{ k }}---{{ v }}
{% for k,v in person.items %}
{% endfor %}
'''
sex---woman
age---18
name---Kate Green
'''