天天看点

自学Django之路---Day4.View,路由规则,反向解析

知识点:

  • 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版本已经解决了。

带参数的路由

如果想在查询的时候传参数:

自学Django之路---Day4.View,路由规则,反向解析

在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、主机名…