知识点:
- locals()可以将局部变量以字典的方式打包
- 状态码
- 2xx 成功
- 3xx 重定向
- 301 永久转移
- 302 暂时转移
- 4xx 客户端错误
- 5xx 服务端错误
点语法
"."访问属性,之前已经多次用到了:
{% for student in students %}
<li>
{{ student.s_name }}
</li>
{% endfor %}
"."访问方法:
在Student类中定义一个方法:
class Student(models.Model):
s_name = models.CharField(max_length=16)
def get_name(self):
return self.s_name
通过.访问方法:
{% for student in students %}
<li>
{{ student.get_name }}
</li>
{% endfor %}
"."作为index:
"."访问字典:
views中:
def get_students(request):
students = Student.objects.all()
text = {
'hobby': 'reading',
}
data = {
'students': students,
'info': text
}
return render(request, "student_list.html", context=data)
html中:
<h3>{{ info.hobby }}</h3>
模板
for
之前用for的时候如果迭代器里没有数据那么在网页上是不会有输出的,现在想即使没有可输出的东西也有个提示,就要使用empty:
{% for student in students %}
<li>
{{ student.s_name }}
</li>
{% empty %}
<h2>查无此人</h2>
{% endfor %}
forloop
方法 | 解释 |
---|---|
counter | 从1开始记录 |
counter0 | 从0开始记录 |
revcounter | 倒数至1 |
revcounter0 | 倒数至0 |
first | 是否为第一个 |
last | 是否为最后一个 |
可以在循环时记录次数:
{% for student in students %}
<li>
{{ forloop.counter }}:{{ student.get_name }}
</li>
{% endfor %}
结果:
1:Ming0
2:Ming1
3:Ming2
4:Ming3
5:Ming4
6:Ming5
7:Ming6
8:Ming7
9:Ming8
10:Ming9
first的使用,如果是第一个数据,就将其变色:
{% for student in students %}
{% if forloop.first %}
<li style="color: #0000ff">{{ student.s_name }}</li>
{% else %}
<li>{{ student.s_name }}</li>
{% endif %}
{% endfor %}
注释
之前的注释是:
这样写在网页上能看到,下面的写法看不到:
{# #} # 单行
{% comment %} ... {% endcomment %} # 多行
一些运算
加减
使用{% 要加减的数|add 加减多少 %}
{{ count|add:2 }}
{{ count|add:-2 }}
乘除
使用{% widthratio 数 分母 分子 %}:
<h3>{% widthratio count 1 5 %}</h3>
结果是25
整除
{% if num|divisibleby:要整除的数 %},== |左右不能加空格?我加了报错…==
{% for student in students %}
{% if forloop.counter|divisibleby:2 %}
<li style="color: #0000ff">{{ student.s_name }}</li>
{% else %}
<li style="color: red">{{ student.s_name }}</li>
{% endif %}
{% endfor %}
两数相等/不相等
{% ifequal/ifnotequal value1 value2 %}
{% for student in students %}
{% ifequal forloop.counter 5 %}
<li style="color: #0000ff">{{ student.s_name }}</li>
{% else %}
<li style="color: red">{{ student.s_name }}</li>
{% endifequal %}
{% endfor %}
第五行变蓝,其它变红
大/小写转换
<h3>{{ info.hobby|lower }}</h3>
<h3>{{ info.hobby|upper }}</h3>
模板可以继承
结构标签
- block : 用来规划布局
- 如下例所示,base作为父类,home继承了它,那么home就可以用同名block来填充
- 若有其它的(例如my_home)继承了home,那么这个模板会有home的信息(比如home填充了head,那么my_home即使不填充head,显示的也是home的head,但是如果my_home也填充了head那么就会把home的head给覆盖掉),当然,如果home没有填充content,那么my_home填充时也就不存在覆盖的问题了。
- 若不想覆盖父模板的内容,则需要在相应部位下添加{{block.super}}
- extends : 继承父模板并获得其结构
- include : 包含,将页面作为一部份,嵌入到其它页面中(像积木?),但效率比block+entends低一些
示例如下:
先创建一个文件,base.html,使用block来布局
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<title>{{ title }}</title>
</head>
<body>
{% block header %}
{% endblock %}
{% block banner %}
{% endblock %}
{% block content %}
{% endblock %}
{% block footer %}
{% endblock %}
</body>
</html>
再创建home.html,使用extends继承base.html
{% extends 'base.html' %}
{% block header %}
<h3>This is head.</h3>
{% endblock %}
{% block footer %}
<h3>This is footer</h3>
{% endblock %}
在views中:
def temp(request):
return render(request, 'home.html', context={'title': ' Home'})
include:
{% include 'footer.html' %}
但要注意,父类通过block挖的坑,子类只能在这些坑里填数据,否则会被优化掉不显示
View
视图的响应分两类:
- 以json形式返回
- 以网页形式返回
- (40X, 50X)
- 重定向到另一个网页
在Django1.1版本,是按照列表的书写顺序进行匹配的,并且没有最有匹配的概念(即上条是haha,下条是hahaha,那么输入hahaha会匹配上面那条),但是在2.2版本已经解决了。
带参数的路由
如果想在查询的时候传参数:
在1.1版本中是用正则来做的,但在2.2中使用了path,如果还想使用正则,可以用re_path
urlpatterns = [
path('student/<int:num><str:s>/', views.student),
]
views:
def student(request, num, s):
print(num, s)
print(type(num), type(s))
return HttpResponse('get student successful!')
在调用时:
http://127.0.0.1:8000/Two/student/2m/
结果:
2 m
<class 'int'> <class 'str'>
练习:点击班级然后显示班级中的学生
主要代码如下:
先构建好models:
class Grade(models.Model):
g_name = models.CharField(max_length=16)
class Student(models.Model):
s_name = models.CharField(max_length=16)
s_grade = models.ForeignKey(Grade, on_delete=models.CASCADE)
然后写urls,要先进入到班级列表界面:
urls:
path('grade/', views.grade),
views:
def grade(request):
grade_list = Grade.objects.all()
return render(request, 'grade_list.html', context=locals())
grade_list.html:
<body>
<ul>
{% for grade in grade_list %}
<li><a href="/Two/students/{{ grade.id }}/" target="_blank" rel="external nofollow" >{{ grade.g_name }}</a></li>
{% endfor %}
</ul>
</body>
这里给每个班级加了a标签以便跳转,而跳转到的地址就应该显示具体的学生信息,那么我们根据每次循环可以得到每个班级的id,以此作为参数写入a标签中。
在urls中写入a标签跳转过来的地址:
path('students/<int:g_id>/', views.students)
views:
def students(request, g_id):
res = Student.objects.filter(s_grade_id=g_id)
return render(request, 'student_grade.html', context=locals())
student_grade.html:
<ul>
{% for re in res %}
<li>{{ re.s_name }} : {{ re.s_grade.g_name }}</li>
{% endfor %}
</ul>
反向解析
之前的url都是写死的,但是当名称更改时所有相关的代码都要更改,这就很麻烦了,因此引入了反向解析。
在模板中使用
在主urls中:
path('more/', include(('Two.urls', 'Two'), namespace='test')),
Two中:
path('grade/', views.grade, name='vv'),
path('jiexi/', views.jiexi, name='view'),
path('study/', views.learn, name='hehe'),
views:
def grade(request):
grade_list = Grade.objects.all()
return render(request, 'grade_list.html', context=locals())
grade_list.html:
<hr>
<a href="/more/study/" target="_blank" rel="external nofollow" >Go to study</a>
<hr>
<a href="{% url 'test:view' %}" target="_blank" rel="external nofollow" >GO GO</a>
来看html文件,上一行是写死的方法,下一行是反向解析,具体意思是:
在主urls中在more/这个url后面加了一个namespace,然后在从urls中指定了name,这就相当于起了个别名,我们在模板中使用时的形式为:{% url ‘namespace:name’ %},在渲染时会自动的找到相应的view方法。
今天在这块卡了好久,我以为改名之后在浏览器里输入新名字也能访问,但是却报404,查了好久,后来才知道这个name是用于模板语法的,也就是只能在模板中这么用,或者在python代码中也可以用,但要用reverse(),所以我在浏览器中这么干肯定是不行的
前面说到了带参数的路由,那么同样可以用到这里:
path('get_time/<int:year><int:month><int:day>', views.get_time, name='gtime'),
def get_time(request, year, month, day):
return HttpResponse("Time %s %s %s" % (year, month, day))
<a href="{% url 'test:gtime' 2020 7 1%}" target="_blank" rel="external nofollow" >Time</a>
通过点击Time选项即可跳转到get_time方法进而返回内容。
在views中使用
reverse()
url:
path('hello/', views.hello, name='hi'),
views:
def buy(request):
url = reverse('App:hi')
return HttpResponseRedirect(url)
如果要带参数,则:
- 位置参数:reverse(‘namespace:name’, args=(value1, value2, …))
- 关键字参数:reverse(‘namespace:name’, kwargs={key1:value1, key2:value2, …}
重写404界面
当出现404界面时,可以重写它,在templates下新建一个名为404的html文件即可。
HttpRequest
服务器收到Http请求后,会根据报文创建HttpRequest对象,也就是view中的第一个参数。
方法如下:
属性方法 | 释义 |
---|---|
method | 请求的方法,GET,POST |
encoding | 编码,常用utf-8 |
GET | 字典形式,包含get所有参数 |
POST | 字典形式,包含post所有参数 |
FILES | 字典形式,包含上传的文件 |
COOKIES | 字典形式,包含cookie |
session | 字典形式,回话 |
is_ajax() | 判断是否ajax() |
查看这些参数
GET
views:
def show_request(request):
print(request.path)
print(request.method)
print(request.GET)
print(request.POST)
return HttpResponse('hehe')
urls:
path('show_request/', views.show_request),
运行结果如下:
/more/show_request/
GET
<QueryDict: {}>
<QueryDict: {}>
可以看到路径,方法是GET,由于没传参所以第三行为空,由于不是POST请求,所以第四行为空
现在稍作更改,在地址栏写http://127.0.0.1:8000/more/show_request/?age=17&age=18,结果如下
/more/show_request/
GET
<QueryDict: {'age': ['17', '18']}>
<QueryDict: {}>
既然QueryDict是类字典,那么就可以像字典那样访问:
request.GET.get(‘key名称’)即可得到value,但这样只能得到最后一个value;若想得到所有则要用request.GET.getlist(‘key名称’)
POST
要验证post则先要弄个表单:
<form action="" method="post">
<span>UserName:</span> <input type="text" name="username" placeholder="请输入用户名">
<button>Submit</button>
</form>
urls:
path('show_post/', views.show_post),
views:
def show_post(request):
return render(request, 'post.html')
点击后会报错,CSRF验证失败. 请求被中断。目前还没学,等后面学完了再来看看。
暂时的解决方案:主文件夹->settings->‘django.middleware.csrf.CsrfViewMiddleware’,屏蔽掉即可
运行结果:
/more/create/
POST
<QueryDict: {}>
<QueryDict: {'username': ['jack']}>
META
使用request.META可以访问客户端的所有信息,包括ip、主机名…