Django世界-迈出第二步-DTL/URL

Django 知识点

Django 模板语言 DTL

  1. 后端传递数据到前端

通过字典的方式返回给前端页面

msg = '字符串'   /  任意变量
return render(request,'html  页面'{'msg':msg}
  1. 前端显示信息

直接显示信息

{{msg}}

显示对象的属性

{{user.email}}

更改显示对象的格式:过滤器

#在参数后加  |  data:'' 时间的格式即可转换
{{user.birthday | date:'m/d/Y'}}

判断数据是否存在,进行不同的操作

#例如判断用户是否登录
{% if request.session.login.name %}
      代码
{% else %}
      代码
{% endif %}

判断数据是否是固定数据,进行不同的操作

#例如判断 order.oispay 是否为0
{% ifequal order.oispay 0 %}
      代码
{% else %}
      代码
{% endifequal %}

for 循环遍历数据显示

{% for u in ulist %}
      <tr>
            #forloop.count 实在前端按顺序排号
            <td>{{forloop.counter}}</td>
            <td>{{u.name}}</td>
            <td>{{u.sex}}</td>
      </tr>
#当列表ulist为空时显示
{% empty %}
      代码
{%  endfor %}

引入静态资源

{% load static %}
<script src=''{% static 'js/libs/jquery-3.1.1/jquery-3.1.1.js %}''></script>

前端屏蔽模板语法

#在需要屏蔽的内容前后加以下内容,使msg模板语法失效
{ % comment % }
      代码
      {{msg}}
{% encomment %}

当页面大量的重复显示某些页面,设置页面模板,引入模板,减少代码冗余
主页面设置为模板

#在模板页面添加模板语法标记
{% block 标记名  %}
      #其他继承引用的页面需要修改或者不需要的地方
      前端代码
{% endblock %}

其他页面引用主页面为模板

#在页面开始天际模板语法,引用模板
{% extends '文件夹、模板html文件‘  %}

#引用模板主页面需要修改的地方
{% block 标记名 %}
      #引用的主页面需要修改的地方
      本需要展示的前端代码
{% endblock %}
  1. forloop 用法

在每个{% for %}循环里有一个称为forloop
模板变量。这个变量有一些提示循环进度信息的属性。

forloop.counter 总是一个表示当前循环的执行次数的整数计数器。
这个计数器是从1开始的,所以在第一次循环时 forloop.counter 将会被设置为1。

 {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}

forloop.counter0 类似于 forloop.counter ,但是它是从0计数的。 第一次执行循环时这个变量会被设置为0。

forloop.revcounter 是表示循环中剩余项的整型变量。 在循环初次执行时 forloop.revcounter 将被设置为序列中项的总数。 最后一次循环执行中,这个变量将被置1。

forloop.revcounter0 类似于 forloop.revcounter ,但它以0做为结束索引。在第一次执行循环时,该变量会被置为序列的项的个数减1。

forloop.first 是一个布尔值,如果该迭代是第一次执行,那么它被置为````在下面的情形中这个变量是很有用的:

System Message: WARNING/2 (<string>, line 1071); backlink
Inline literal start-string without end-string.
    {% for object in objects %}
        {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
        {{ object }}
        </li>
    {% endfor %}

forloop.last 是一个布尔值;在最后一次执行循环时被置为True。 一个常见的用法是在一系列的链接之间放置管道符(|)

参考文档:https://blog.csdn.net/qq_33961117/article/details/83900608

Django URL

url的匹配规则:

  1. 普通用法
#1.普通用法,无参数情况,配置URL及其视图如下:
from django.conf.urls import url,include
from TestApp import views
urlpatterns = [
    url('^test/2018/$',views.TestOne.as_view())
]
#views.py
# -*- coding: utf-8 -*-
from django.views import View
from django.http import HttpResponse
class TestOne(View):
    def get(self,request):
        return HttpResponse('TestOne:普通url的写法')

输入地址:http://127.0.0.1:8000/test/2018/

  1. 正则url写法
#urls.py写法:
from django.conf.urls import url,include
from TestApp import views
urlpatterns = [
    url('^test/[0-9]{4}/$',views.TestTwo.as_view())
]
#views.py写法:
# -*- coding: utf-8 -*-
from django.views import View
from django.http import HttpResponse
class TestTwo(View):
    def get(self,request):
        return HttpResponse('TestTwo:正则url写法')
#2.传递参数情况,URL中通过正则指定参数:
url('^test/([0-9]{4})/$',views.TestThree.as_view())
class TestThree(View):
    def get(self,request,year):
        return HttpResponse('TestThree:带单个非命名写法')


url('^test/([0-9]{4})/([0-9]{2})/([0-9]{2})/$',views.TestFour.as_view())

class TestFour(View):
    def get(self,request,year,month,day):
        return HttpResponse('TestFour:带多个非命名写法')

#因为当加上圆括号的时候,django就能从URL中捕获这一个值并传递给相对应的views函数,当然使用的是位置传参。
* 域名部分会被过滤掉;
* 不需要添加一个前导的反斜杠,因为每个URL 都有。例如,应该是^test 而不是 ^/test;
* 每个正则表达式前面的'r' 是可选的但是建议加上,表示字符串中任何字符都不转义;
* 若要从URL 中捕获一个值,只需要在它周围放置一对圆括号。
* 任何组匹配的变量,都会议字符串的形式传递给view, 例如通过([0-9]{4})匹配出了2019,但2019会被当做字符串传递给year。
从这里可以看出,视图的参数是根据URL的正则式,按顺序匹配并自动赋值的。虽然这样可以实现任意多个参数的传递,但是却不够灵活,URL看起来很混乱,而且
由于是正则匹配,有些情况下容易出错。

输入地址:http://127.0.0.1:8000/test/2017

  1. 命名组

刚才我们在使用圆括号进行传参的时候是位置传参,那么如果我们希望使用关键字传参的时候该怎么办呢?
这时候我们就使用到了命名组,命名组的正则表达式语法是(?Ppattern),其中name是指传递参数的名字,pattern是指匹配模式。

url('^test/(?P<year>[0-9]{4})/$',views.TestFive.as_view())
class TestFive(View):
    def get(self,request,year):  #入参year一定要与url中的参数命一致
        print(year)  #获取的是传过来的值
        return HttpResponse('TestFive:带单个命名参数的写法')

url('^test/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$',views.TestSix.as_view())
class TestSix(View):
    def get(self,request,year,month,day):
        print(year,month,day)
        return HttpResponse('TestSix:带多个命名参数的写法')
  1. 指定视图参数的默认值

利用named group可以为view指定一个默认参数来匹配多条规则。

url(r'^test/$',views.TestFive.as_view()),
url(r'^test/(?P<year>[0-9]{4})/$',views.TestFive.as_view())

class TestFive(View):
    def get(self, request, year = '2010'):
        print('-->===>', self, request, year)
        return HttpResponse('TestFive: 命名组1')
#地址栏输入http://127.0.0.1:8000/test,调用TestFive的话,则会取year默认值2010
  1. 错误处理

当Django 找不到一个匹配请求的URL 的正则表达式时,或者当抛出一个异常时,Django 将调用一个错误处理视图。
每一个请求,都会返回一个HTTP状态码

状态码含义
200请求正常;
404就是找不到页面,或者说匹配不到对应的path路径;
403是指服务器拒绝, 一般出现这种情况,是用户被服务器拉进黑名单,或者安全拦截;
400你的request异常,一般就是你的请求缺少内容;
500服务器异常,这个一般就是代码出现了异常;
  1. 在URLconf中指定参数,这些参数分别是
    状态码 | 含义
    -------- | -----
    handler404| 一个callable或一个字符串,表示如果没有URL模式匹配,应该调用的视图的完整Python导入路径。默认情况下,这是’django.views.defaults.page_not_found’。
    handler500| 默认情况下,这是’django.views.defaults.page_not_found’。
    handler403| 默认情况下,这是’django.views.defaults.permission_denied’。
    handler400| 默认情况下,这是’django.views.defaults.bad_request’。

可以自定义报错信息:
handler404 = ‘TestApp.views.erropage’ #需要app名称.views.函数名, 这个指定的实际上是一个引入的路径。只能调用函数,不能用类方法

#views.py中
def erropage(request):
    return HttpResponse('not found page')
#settings.py中调试模式关闭才能看到自定义的信息
DEBUG = False
  1. 包含其它的URLconfs

在任何时候,你的urlpatterns 都可以包含其它URLconf 模块。这实际上将一部分URL 放置于其它URL 下面。

#在应用app中新建一个urls.py放置具体的
url(r'^2018/$',views.TestOne.as_view()),
url(r'^(?P<year>[0-9]{4})/$',views.TestFive.as_view()),
url(r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.TestSix.as_view()),
url(r'^(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<day>[0-9]{2})/$', views.TestSeven.as_view())

#在项目的根 urls.py 配置文件改为:
import views
from django.conf.urls import url,include
url(r'^test/',include('TestApp.urls'))
*如果项目非常庞大,应用非常多,应用的 URL 都写在根 urls.py 配置文件中的话,会显的非常杂乱,
*还会出现名称冲突之类的问题,这样对开发整个项目是非常不利的。可以这样解决,把每个应用的 URL
*写在它们各自的 urls.py 配置文件里,然后在根 urls.py 里用 include() 函数引用

url的反向解析:

要获取一个URL,最初拥有的信息是负责处理它的视图的标识(例如名字),与查找正确的URL 的其它必要的信息如视图参数的类型(位置参数、关键字参数)和值。
Django 提供了一个解决方案使得URL 映射是URL 设计唯一的储存库。你用你的URLconf填充
它,然后可以双向使用它:

  1. 根据用户/浏览器发起的URL 请求,它调用正确的Django 视图,并从URL 中提取它的参数需要的值。
  2. 根据Django 视图的标识和将要传递给它的参数的值,获取与之关联的URL。
    第一种方式是我们在前面的章节中一直讨论的用法。第二种方式叫做反向解析URL、反向URL匹配、反向URL查询或者简单的URL反查。
    在需要URL 的地方,对于不同层级,Django 提供不同的工具用于URL 反查:
    1)在模板中:使用url 模板标签。
    2)在Python 代码中:使用django.core.urlresolvers.reverse() 函数。
    3)在更高层的与处理Django 模型实例相关的代码中:使用get_absolute_url() 方法。
重定向:
#利用redirect跳转
#主程序urls.py中
from django.conf.urls import url,include
url(r'^test/',include('TestApp.urls'))

#应用程序urls.py中
import views
url(r'^2018/$',views.TestOne.as_view()),
url(r'^visit/(?P<flag>[0-9]{1})$',views.Visit.as_view())

#views.py中
from django.shortcuts import render,redirect
class Visit(View):
    def get(self,request,flag):
        #这是一个重定向方法
        #如果需要在这做一个跳转
        if flag == '1':
            return HttpResponse('hello success!')
        else:
        #重定向跳一个地址, 因为url地址开始肯定是一个/,  如果你不带斜杠的话,它就会当成一个相对地址,它自会自动在当前的url中往后添加。
            return redirect('/test/2018')   
URL 的反向解析
模板标签
reverse

URL反向解析一般是通过[reverse函数]使用django.core.urlresolvers.reverse() 函数,以及模板中的url标记实现。

  1. 未带参数
url(r'^visit/(?P<flag>[0-9]{1})$',views.Visit.as_view())
url(r'^2018/$',views.TestOne.as_view(),name='login'),


#views.py
class TestOne(View):
    def get(self,request):
        print('-->===>',self,request)
        return HttpResponse('TestOne:普通用法')

class Visit(View):
    def get(self,request,flag):
        #这是一个重定向方法
        #如果需要在这做一个跳转
        if flag == '1':
            return HttpResponse('hello success!')
        else:
            # reverse未带参数
            return redirect(reverse('login'))
  1. 带非命名参数的跳转
url(r'^visit/(?P<flag>[0-9]{1})$',views.Visit.as_view()),
url(r'^([0-9]{4})/([0-9]{2})/([0-9]{2})$', views.TestFour.as_view(),name='login3')

class Visit(View):
    def get(self,request,flag):
        #这是一个重定向方法
        #如果需要在这做一个跳转
        if flag == '1':
            return HttpResponse('hello success!')
        else:
            # reverse
            return redirect(reverse('login3',args=("2018", "03","21")))
class TestFour(View):
    def get(self, request, year,month,day):
        print('-->===>', self, request, year,month,day)
        return HttpResponse('TestFour: 3个非命名参数')
  1. 命名参数的跳转
#主urls.py
url(r'^test/',include('TestApp.urls')) 
#应用urls.py
url(r'^visit/(?P<flag>[0-9]{1})/$',views.Visit.as_view()),
url(r'^login/(?P<username>[a-z]{3})/$', views.Login.as_view(),name='regist')
#views.py
class Visit(View):
    def get(self,request,flag):
        if flag =='1':
            return HttpResponse('登录成功')
        else:
            return redirect(reverse('regist',kwargs={'username':'llp'}))
class Login(View):
    def get(self,request,username):
        message = '{} please login'.format(username)
        return HttpResponse(message)
  1. get_absolute_url方法

在FBV(Funtional-Base View)中反向获取url的方式是重载get_absolute_url方法。
电子购物网站的模型:

from django.db import models
from django.urls import reverse


class Category(models.Model):
    name = models.CharField(max_length=150, db_index=True)
    slug = models.SlugField(max_length=150, unique=True ,db_index=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('name', )
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('shop:product_list_by_category', args=[self.slug])


class Product(models.Model):
    category = models.ForeignKey(Category, related_name='products', on_delete=models.CASCADE)
    name = models.CharField(max_length=100, db_index=True)
    slug = models.SlugField(max_length=100, db_index=True)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    available = models.BooleanField(default=True)
    stock = models.PositiveIntegerField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)

    class Meta:
        ordering = ('name', )
        index_together = (('id', 'slug'),)

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('shop:product_detail', args=[self.id, self.slug])

视图 views.py

from django.shortcuts import render, get_object_or_404
from .models import Category, Product


def product_list(request, category_slug=None):
    category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available=True)
    if category_slug:
        category = get_object_or_404(Category, slug=category_slug)
        products = Product.objects.filter(category=category)

    context = {
        'category': category,
        'categories': categories,
        'products': products
    }
    return render(request, 'shop/product/list.html', context)


def product_detail(request, id, slug):
    product = get_object_or_404(Product, id=id, slug=slug, available=True)
    context = {
        'product': product
    }
    return render(request, 'shop/product/detail.html', context)

路由urls.py

from django.urls import path, re_path

from . import views

app_name = "shop"

urlpatterns = [
        path("", views.product_list, name="product_list"),
        re_path(r"^(?P<category_slug>[\w-]+)/$", views.product_list,
            name="product_list_by_category"),
        re_path(r"^(?P<id>\d+)/(?P<slug>[\w-]+)/$", views.product_detail,
            name="product_detail"),
        ]

这是在html页面的逻辑:
list.html

{% for product in products %}
                  <div class="col-md-4">
                    <div class="thumbnail">
                        <a href="{{ product.get_absolute_url }}">
                            <img src="{% if product.image %} {{ product.image.url }} {% else %} {% static 'img/default.jpg' %} {% endif %}" alt="..." style="height: 130px; width: auto">
                        </a>
                        <div class="caption">
                            <h3 class="text-center">
                                <a href="{{ product.get_absolute_url }}">{{ product.name }}</a>
                            </h3>
                            <p class="text-center">Kshs. {{ product.price }}</p>
                        </div>
                    </div>
                  </div>
              {% endfor %}

页面逻辑:
在product.get_obsolute_url里面用的是product.id和product.slug来连接到该product的detail.html。

  1. 总结:

如果url里面带了无名或者有名参数,那么在重定向时,reverse需要带上args或者kwargs。

URL 的命名
  1. URL 模式的命名

为了完成上面例子中的URL 反查,你将需要使用命名的URL 模式。URL 的名称使用的字符串可以包含任何你喜欢的字符。并不仅限于合法的Python 名称。
当命名你的URL 模式时,请确保使用的名称不会与其它应用中名称冲突。如果你的URL 模式叫做comment,而另外一个应用中也有一个同样的名称,当你在模板中使用这个名称的时候不能保证将插入哪个URL。
在URL 名称中加上一个前缀,比如应用的名称,将减少冲突的可能。我们建议使用myapp-comment 而不是comment。

  1. URL 命名空间

为什么需要命名空间呢?
在之前如果我们通过URL反查的话是通过URL模式中的name属性来进行反查标记的,但是name属性容易重复并且不利于复用,当我们要多次部署一个URL配置模块的时候,就无法通过简单的name属性来进行标记了。
一般来说,同一应用下的不同实例应该具有相同的应用命名空间,但是,这并不意味着不同应用可以使用相同的实例命名空间,因为实例命名空间在你所有项目中都是唯一的。
反查带命名空间的URL
当解析一个带命名空间的URL(例如’polls:index’)时,Django 将切分名称为多个部分,然后按下面的步骤查找:
1)首先,Django 查找匹配的应用命名空间(在这个例子中为’polls’)。这将得到该应用实例的一个列表。
2)如果有一个当前应用被定义,Django 将查找并返回那个实例的URL 解析器。当前应用可以通过请求上的一个属性指定。
3)当前应用还可以通过reverse() 函数的一个参数手工设定。
4)如果没有当前应用。Django 将查找一个默认的应用实例。默认的应用实例是实例命名空间 与应用命名空间 一致的那个实例(在这个例子中,polls 的一个叫做’polls’ 的实例)。
5)如果没有默认的应用实例,Django 将挑选该应用最后部署的实例,不管实例的名称是什么。
6)如果提供的命名空间与第1步中的应用命名空间 不匹配,Django 将尝试直接将此命名空间作为一个实例命名空间查找。
7)如果有嵌套的命名空间,将为命名空间的每个部分重复调用这些步骤直至剩下视图的名称还未解析。然后该视图的名称将被解析到找到的这个命名空间中的一个URL。

如果name是唯一的时候,你可以直接只使用name,但是如果name在其它include中也存在相同name,为了区分,我们可以给inlude设置一个namespace
我们通过命名空间名称+名称访问。
为了程序的健壮性,我们尽量使用命名空间+名称访问
顶层其实有一个app名称,但是我们日常开发中,几乎不使用,该功能了解知道即可

#主urls.py
#namespace只有在有多个include时,才需要
#namespace 与name的设置规则一样,同一级别,名称需要保持唯一
url(r'^test/',include('TestApp.urls',namespace='test'))
#应用下的urls.py
url(r'^visit/(?P<flag>[0-9]{1})$',views.Visit.as_view()),
url(r'^2018/$',views.TestOne.as_view(),name='login')
#views.py
class Visit(View):
    def get(self,request,flag):
        #这是一个重定向方法
        #如果需要在这做一个跳转
        if flag == '1':
            return HttpResponse('hello success!')
        else:
            return redirect(reverse('test:login')) 
#如果url写了namespace,则这里需要namespace:name形式,否则报错;

class TestOne(View):
    def get(self,request):
        print('-->===>',self,request)
        return HttpResponse('TestOne:普通用法')

地址栏输入:http://127.0.0.1:8000/test/visit/0会重定向到http://127.0.0.1:8000/test/2018/

url(r'^test/',include('TestApp.urls',namespace='visit'))

url(r'^visit/(?P<flag>[0-9]{1})/$',views.Visit.as_view()),
url(r'^login/(?P<username>[a-z]{3})/$', views.Login.as_view(),name='regist')

class Visit(View):
    def get(self,request,flag):
        if flag =='1':
            return HttpResponse('登录成功')
        else:
            print(reverse('visit:regist',kwargs={'username':'Gavin'}))
            return redirect(reverse('visit:regist',kwargs={'username':'llp'}))

class Login(View):
    def get(self,request,username):
        message = '{} please login'.format(username)
        return HttpResponse(message)
#输入
http://127.0.0.1:8000/test/visit/0=>跳转到http://127.0.0.1:8000/test/login/Gavin/
打印出来的是:Gavin please login
要记住reverse这个函数其实就是返回一个字符串/test/login/Gavin/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值