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

    技术2022-07-11  68

    知识点:

    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:

    <h3>{{ students.0.s_name }}</h3>

    "."访问字典:

    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倒数至1revcounter0倒数至0first是否为第一个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 lang="en"> <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 }}/">{{ 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/">Go to study</a> <hr> <a href="{% url 'test:view' %}">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%}">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,POSTencoding编码,常用utf-8GET字典形式,包含get所有参数POST字典形式,包含post所有参数FILES字典形式,包含上传的文件COOKIES字典形式,包含cookiesession字典形式,回话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、主机名…

    Processed: 0.011, SQL: 9