三、URL与视图
URL分发器
视图:
视图一般都写在app的view.py中。并且视图的第一个参数永远都是request(一个HttpRequest)对象。这个对象存储了请求过来的所有信息,包括拾的参数以及一些头部信息等。在视图中,一般是完成逻辑相关的操作。比如这个请求是添加一篇博客,那么通过request来接收到这些数据,然后存储到数据库中,最后把执行的结果返回给浏览器。视图函数的返回结果必须是HttpRequestBase对象或者子类的对象。示例代码如下:
from django.http import HttpResponse
def book(request):
return HttpResponse("图书首页")
URL映射:
视图写完后,要与URL进行映射,即用户在浏览器中输入什么url的时候可以请求到这个视力函数。在用户输入了某个url,文玩清台我们的网站的时候,django会从项目的urls.py文件中寻找到对应的视力和。在urls.py文件中有一个urlpatterns变量,以后django就会从这个变量中读取所有的匹配规则,匹配规则需要使用django.urls.pathh函数进行包裹,这个函数会根据传入的参数返回URLPattern或者是URLResolver的对象。示例代码如下:
from django.contrib import admin
from django.urls import path
from book import views
ADMIN_SITE_URLS_ = [
path('admin/', admin.site.urls),
path('book/', views.book)
]
URL中传参数
有时候,url包含了一些参数需要动态调整。比如简书某文章的详情页的url,https://www.jianshu.com/p/a64aa003be0c后面的a64aa003be0c就是这篇文件的id,那么简书的文章详情页面的url就可以写成https://www.jianshu.com/p/,其中id就是文件的id。那么如何在django中实现这种需求呢?这个时候我们可以在path函数中,使用尖括号的形式来定义一个参数。比如我现在想要获取一本书籍的详细信息,那么应该在url中指定这个参数。- 采用在url中使用变量的方式
在path的第一个参数中,使用<参数名>的方式可以传递参数。然后在视图函数中也要定一个参数,视图函数中的参数必须和url中的参数名称保持一致,不然就找不到这个参数。另外,url可以传递多个参数
- 在url.py的代码:
from django.contrib import admin
from django.urls import path
from book import views
ADMIN_SITE_URLS_ = [
path('', views.index),
path('admin/', admin.site.urls),
path('book/', views.book),
path('book/<book_id>', views.book_detail),
]
- 在view.py的代码:
from django.http import HttpResponse
# Create your views here.
#...
def book_detail(request, book_id):
text = "您获取的图书id是:%s"%book_id
return HttpResponse(text)
- 采用查询字符串的方式
在url中,不需要单独的匹配查询字符串的部分。只需要在视图中会用 request.GET.get(‘参数名称’) 的方式来获取。
- 在url.py的代码:
from django.contrib import admin
from django.urls import path
from book import views
ADMIN_SITE_URLS_ = [
path('', views.index),
path('admin/', admin.site.urls),
path('book/', views.book),
#path('book/<book_id>/', views.book_detail),
path('book/author/', views.author_detail)
]
- 在view.py的代码:
from django.http import HttpResponse
# Create your views here.
#...
def author_detail(request):
author_id = request.GET["id"]
text = "作者的id是:%s"%author_id
return HttpResponse(text)
因为查询字符串使用的是GET请求,所有我们通过request.GET来获取参数。并且因为GET是一个类似于字典的数据类型,所有获取值跟字典的方式都一样的
- URL命名
- 为什么需要url命名:
因为url是经常变化的。如果代码中写死可能会经常改代码。给url取个名字,以后使用url的时候就使用他的名字进行反转就可以了,就不需要写死url了 - 如何给一个url指定名称:
在path函数中,传递一个name参数就可以指定
urlpatterns = [
path("", views.index, name='index'),
path("login/", views.login, name='login')
]
- 应用命名空间:
在多个app之间,有可能产生同名的url。这个时候为了避免反转url的时候产生混淆,可以使用应用命名空间,来做区分。
# 应用命名空间
app_name = 'front'
urlpatterns = [
path("", views.index, name='index'),
path("login/", views.login, name='login')
]
以后在做反转的时候就可以使用<应用命名空间:url名称>的方式进行反转
def index(request):
username = request.GET.get("username")
#username = request.GET["username"] 不可用
if username:
return HttpResponse("前台首页")
else:
login_url = reverse('front:login')
print("="*30)
print(login_url)
print("="*30)
return redirect(login_url)
四、模板
在实际生产环境中其实很少这样用,因为实际的页面大多是带有样式的HTML代码,这可以让浏览器渲染出非常漂亮的页面。目前市面上有非常多的模板系统,其中最知名最好用的就是DTL和Jinja2。DTL是Django Template Language三个单词的缩写,也就是Django自带的模板语言。当然也可以配置Django支持Jinja2等其它模板引擎,但是作为Django内置的模板语言,和Django可以达到无疑衔接而不会产生一些不兼容的情况。
DTL与普通的HTML文件的区别
DTL模板是一种带有特殊语法的HTML文件,这个HTML文件可以被Django编译,可以传递参数进去,实现数据动态化。在编译完成后,生成一个普通的HTML文件,然后发送给客户端。
- 渲染模板
渲染模板有多种方式。这里讲下两种常用的方式
- render_to_string: 找到模板,然后将模板编译后渲染成Python的字符串格式。最后通过HttpResponse类包装成一个HttpRequest对象返回回去
from django.template.loader import render_to_string
from django.http import HttpResponse
def index(request):
html = render_to_string("index.html")
return HttpResponse(html)
- 以上方式跃然已经很方便了。但是django还提供了一个更加简便的方式,直接将模板渲染成字符串和包装成HttpResponse对象一步到位完成
from django.shortcuts import render
def index(request):
return render(request, "index.html")
- 模板路径设置
在项目的setting.py文件中。有一个templates配置,这个配置包含了模板引擎的配置,模板查找路径的配置,模板上下文的配置等。模板路径可以在两个地方配置。
- DIRS:这是一个列表,在这个列表中可以存放所有的模板路径,以后在视图中使用render或者render_to_string渲染模板的时候,会在这个列表的路径中查找模板
- APP_DIRS:默认为True,这个设置为True后,会在INSTALLED_APPS的安装了的APP下的templates文件中查找模板。
- 查找顺序:比如代码render(‘list.html’)。先会在DIRS这个列表中依次查找路径下有没有这个模板,如果有,就返回。如果DIRS列表中所有折路径都没有找到,那会先检查当前这个视图所处的app是否已经安装,如果已经安装了,那么就先在当前app下的templates文件中查找模板,如果没有找到,那么会在其他已经安装了的app中查找。如果所有路径下都没有找到,那么会抛出一个TemplateDoesNotExist的异常
DTL模板语法
- 变量:
模板中可以包含变量,Django在渲染模板的时候,可以传递变量对应的值过去进行替换。变量的命名规范和Python非常类似,只能是阿拉伯数字和英文字符以及下划线的组合,不能出现标点符号等特殊字符。变量需要通过视图函数渲染,视力函数在使用render或者render_to_string的时候可以传递一个context的参数,这个参数是一个字典类型。以后在模板中的变量就从这个字典中读取值的
# index2.html模版代码
<p>{{username}}</p>
# views.py代码
def index(request):
context = {
"username": "斯巴达"
}
return render(request, "index2.html", context=context)
模板中的变量同样也支持点的形式。在出现了点的情况,比如person.username,模板是按照以下方式进行解析的:
- 如果person是一个字典,那么就会查找这个字典的username这个key对应的值
def index(request):
context = {
"person":{
"username":"皮卡丘",
"keys":"abc"
}
}
return render(request, "index2.html", context=context)
- 如果person是一个对象,那么就会查找这个对象的username属性,或者是username这个方法
class Person(object):
def __init__(self, username):
self.username = username
def index(request):
p = Person("可达鸭")
context = {"person": p}
return render(request, "index2.html", context=context)
- 如果出现的是person.1,会判断person是否是一个列表或者元组或者任意的可以通过下标访问的对象,如果是的言辞就取这个列表的第1个值。如果不是就获取到的是一个空的字符串。
def index(request):
context = {
"person":[
"程咬金","阿珂"
]
}
return render(request, "index2.html", context=context)
常用的模板标签
- if标签
if标签相当于Python中的if语句,有elif和else相对应,但是所有的标签都需要有标签符号( {%%}
)进行包裹。if标签中可以使用==、!=、<、<=、>、>=、in、not in、is、is not等判断运算符。
{% if '鲁班一号' in heros %}
<p>鲁班一号正在待命</p>
{% else %}
<p>鲁班一号正在睡觉</p>
{% endif %}
- for…in…标签
for…in…类似于Python中的for…in…。可以遍历列表、元组、字符串字典等一切可以遍历的对象
{% for book in books %}
<li>{{ book }}</li>
{% endfor %}
如果想要反向遍历,那么在遍历的时候就加上一个reversed
{% for book in books reversed %}
<li>{{ book }}</li>
{% endfor %}
遍历字典的时候,需要使用item、keys和values等方法。在DTL中,执行一个方法不能使用圆括号的形式
{% for key,value in person.items %}
<p>key:{{ key }}</p>
<p>value:{{ value }}</p>
{% endfor %}
在for循环中,DTL提供了一些变量可供使用。这些变量如下:
- forloop.counter:当前循环的下标。以1作为起始值。
- forloop.counter0:当前循环的下标。以0作为起始值。
- forloop.revcounter:当前循环的反向下标值。比如列表有5个元素,那么第一次遍历这个属性是等于5,第二次是4,以此类推
- forloop.revcounter0:
- forloop.first:是否第一次遍历
- forloop.last:是否最后一次遍历
- forloop.parentloop:如果有多个循环嵌套,那么这个属性代表的是上一级的for循环
{% for book in books %}
<tr>
<td>{{ forloop.revcounter }}</td>
<td>{{ forloop.parentloop }}</td>
<td>{{ forloop.counter }}</td>
<td>{{ book.price }}</td>
</tr>
{% endfor %}
- for…in…empty标签
这个标签使用跟**for…in…**是一样的,只不过是在遍历的对象如果没有元素的情况下,会执行empty中的内容
- url标签
在模版中,我们经常写一些url,比如某个a标签中需要定义href属性。当然如果通过硬盘编码的方式直接将这个url写死在里面也是可以的。但这样对于双后项目维护可能不是一件好事。因此建议使用这种反转的方式来实现,类似django中的reverse一样
<a href="{% url 'book' %}">读书</a>
如果url反转的时候需要传递参数,那么可以在后面传递。但是参数分位置参数和关键字参数。位置参数和关键字参数不能同时使用
# path部分
path('detail/<book_id>/', views.book_detail, name='detail')
# url反转,使用位置参数
<a href="{% url 'detail' 1 %}">最火的图书是</a>
# url反转,使用关键字参数
<a href="{% url 'detail' book_id=1 %}">最火的图书是</a>
如果想要在使用url标签反转的时候要传递查询字符串的参数,那么必须要手动在后面添加
<a href="{% url 'detail' %}?next=1">最火的图书是</a>
如果需要传递多个参数,那么通过空格的方式进行分隔
<a href="{% url 'detail' book_id=1 page=2 %}">最火的图书是</a>
模版常用过滤器
在模版中,有时候需要对一些数据进行处理以后才能使用。一般在Python中我们是通过函数的形式来完成的。在模版中,则是通过过滤器来实现。过滤器使用的时候 “ | ” 来使用。
{{value | add:"2"}}
- add
将传进来的参数添加到原来的值上面。这个过滤器会尝试将值和参数转换成整形后进行相加。如果转换成整形过程中失败,那么会将值和参数进行拼接。如果是字符串,那么会拼接成字符串,如果是列表,那么会拼成一个列表
{{value | add:"2"}}
如果value是等于4,那么将是6。如果value是等于一个普通的字符串,比如abc,那么结果将是abc2,add过滤器的源代码如下:
def add(value, arg):
try:
return int(value)+int(arg)
except (ValueError, TypeError):
try:
return value + arg
except Exception:
return ''
- cut
移除值中所有指定的字符串。类似于Python中的replace(args,"")。
{{value|cut:" "}}
以上救命将会移除value中所有的空格字符。cut过滤器的源代码如下:
def cut(value, arg):
safe = isinstance(value, SafeData)
value = value.replace(arg,"")
if safe and arg != ';':
return mark_safe(value)
return value
- date
将一个日期按照指定的格式,格式化成字符串
{{ data.date | date:"Y-m" }}
还有更多时间格式化的方式。见下表
格式字符 | 描述 | 示例 |
---|---|---|
Y | 四位数字的年份 | 2019 |
m | 两位数字的月份 | 01-12 |
n | 月份,1-9前面没有0 | 1-12 |
d | 两位数字的天 | 06-01 |
j | 天,但是1-9前面没有0 | 06-1 |
g | 小时,12小时格式的,1-9前面没有0 | 1:59 |
h | 小时,12小时格式的,1-9前面有0 | 01:59 |
G | 小时,24小时格式的,1-9前面没有0 | 1:59 |
H | 小时,24小时格式的,1-9前面有0 | 01:59 |
i | 分钟,1-9前面有0 | 01:59 |
s | 秒,1-9前面有0 | 01:59 |
模版继承
在前端页面开发中。有些代码是需要重复使用的。这种情况可以使用include标签来实现。也可以使用另外一个比较强大的方式来实现,那就是模版继承。模版继承类似于Python中的类,在父类中可以先定义好一些变量和方法,然后在子类中实现。模版继承也可以在父模版中先定义好一些子模版需要用到的代码,然后子模版直接继承就可以了。并且因为子模版肯定有自己的不同代码,因此可以在父模版中定义一个block接口,然后子模版再去实现
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<header>
<ul>
<li><a href="/">首页</a></li>
<li><a href="{% url 'company' %}">公司</a></li>
<li><a href="{% url 'school' %}">校园</a></li>
<li>{{ username }}</li>
</ul>
</header>
<div class="content">
{% block content %}
我是父模板中content的代码
{% endblock %}
</div>
<footer>
这是footer部分
</footer>
</body>
</html>
index.html
{% extends "base.html" %}
{% block content %}
<p>这是首页的代码</p>
我是block外面的代码
<p>{{ block.super }}</p>
{% endblock %}
需要注意的是:extends标签必须放在模版的开始的位置
子模版中的代码必须放在block中,否则不会被渲染
如果在某个block中需要使用父模版的内容,那么可以使用 **{{block.super}}来继承。比如上例
在定义block的时候,除了在block开始的地方定义这个block的名字,还可以在block结束的时候定义名字。比如**{% block title %}{% endblock title %}**。这在大型模版中显得尤其有用,能让你快速的看到block包含在哪里
加载静态文件
在一个网页中,不仅仅有一个html骨架,还需要css样式文件,js执行文件以及一些图片等。因此在DTL中加载静态文件是一个必须要解决的问题。在DTL中,使用static标签来加载静态文件。要使用static标签,首先需要 {% load static %}。加载静态文件的步骤如下:
- 首先确保django.contrib.staticfiles已经添加到settings.INSTALLED_APPS中。
- 确保在settings.py中设置了STATIC_URL
- 在已经安装了的app下创建一个文件叫做static,然后再在这个static文件夹下创建一个当前app的名字的文件夹,再把静态文件放到这个文件夹下。例如你的app叫做book,有一个静态文件叫做zhiliao.jpg,那么路径为book/static/book/zhiliao.jpg。(为什么app下创建一个static文件夹,还需要在这个static下创建一个同app名字的文件夹呢?原因是如果直接把静态文件放在static文件夹下,那么在模版加载静态文件的时候就是使用zhiliao.jpg,如果在多个app之间有同名的静态文件,这时候可以就会产生混淆。而在static文件夹下加了一个同名app文件夹,在模版中加载的时候就是使用app/zhiliao.jpg,这样就可以避免产生混淆。)
- 如果有一些静态文件是不和任何app挂钩的。那么可以在settings.py中添加STATICFILES_DIRS,以后DTL就会在这个列表的路径中查找静态文件。比如可以设置为:
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "static")
]
- 在模版中使用load标签加载static标签。比如要加载在项目的static文件夹下的style.css的文件。
<link rel="stylesheet" href="{% static 'index.css' %}">
- 如果不想每次在模版中加载静态文件都使用load加载static标签,那么可以在settings.py中的TEMPLATES/OPTIONS添加 ‘builtins’:[‘django.templatetags.static’],这样以后在模版中就可以直接使用static标签,而不用手动的load了
- 如果没有在settings.INSTALLED_APPS中添加django.contrib.staticfiles。那么我们就需要手机的将请求静态文件的url与静态文件的路径进行映射了
from
本人的经验分享,希望可以帮助到你们,如何不对的地方,可以评论留言,帮我指正一下,如果帮助了你,请给我点个赞吧