Django基础

Python的web框架有Django、Tornado、Flask 等多种,Django相对于其他web框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。

关于web框架MVC&MTV模型可参考另一篇文章:Django之浅析请求生命周期及MVC&MTV

一 Django基础操作

1.1 Django安装

pip3 install django      # 常见如pycharm等IDE都已经存在Django

1.2 创建并运行Django程序

# 创建Django程序
django-admin startproject project_name   # IDE创建Django程序时,本质上都是自动执行上述命令	

# 进入程序目录,可创建app应用
cd project_name

# 启动socket服务端,等待用户发送请求
python manage.py runserver           # 默认为:127.0.0.1:8000

页面访问结果如下,代表成功

image

1.3 Django程序目录

mytestweb1/              # 项目的容器,名称自定义
	manage.py        # Django用于管理本项目的命令行工具,之后进行站点运行、数据库自动生成、静态文件收集等都要通过该文件完成
	mysite/          # 实际的Python项目
		__init__.py  # 告诉Python该目录是一个包,其中暂无内容
		settings.py  # 这个Django项目配置文件
		urls.py      # 这个Django项目的URL声明; 一个Django驱动网站的“目录”,负责把URL模式映射到应用程序
		wsgi.py      # 定义WSGI的接口信息,用于与其他web服务器的集成,一般文件在生成后无须改动

目录如下(包含应用app01):

image

1.4 创建应用

cd projectname
python manage.py startapp app文件夹名称

注意:利用pycharm创建Django工程时也可进行制定。

目录结构如下:

image

app文件夹目录说明:

migrations包:用于在之后定义引用迁移功能
admin.py:管理站点模型的声明文件,默认为空
apps.py:应用信息定义文件。在其中生成了类AppConfig,该类用于定义应用名等Meta数据
models.py:添加模型层数据类的文件。根据类创建数据库表
test.py:测试代码文件
views.py:定义URL响应函数

1.5 其他常用命令

# 启动Django
python manage.py runserver
python manage.py runserver 8080
python manage.py runserver 0.0.0.0:8000	

# 新建app,确保和manage.py在同一级目录
python manage.py startapp app_name

# 创建数据库表或更改数据库表或字段
	# Django1.7.及以上版本:
		#1.创建更改的文件
		python manage.py makemigrations
		#2.将生产的py文件应用到数据库
		python manage.py migrate
	# Django1.6及以下:
		python manage.py syncdb

# 查看Django版本
python -m django --version
	
# Django项目环境终端
python manage.py shell
	
# 清空数据库(保留空表)
python manage.py flush
	
# admin创建超级管理员
pyhton manage.py createsuperuser

# 修改用户密码
python manage.py changepassword username

# 更多命令插叙
python manage.py

二 配置文件

2.1 模板

Django中默认以template目录存放模板文件,需要在settings.py文件里进行配置:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],           # 配置模板文件路径
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

2.2 静态文件

如CSS,js,image文档都属于静态文件。该步配置也是在settings.py中进行:

STATIC_URL = '/static/'  # 引用名,Django默认

STATICFILES_DIRS = [
    os.path.join(BASE_DIR,'static'),  # 实际名,即文件夹的名称
]

注意:Django对应用名和实际名进行映射,引用时按照引用名进行,不按照实际名进行,所以为了方便,我们将两者名字进行统一,当然是可以不同的。

2.3 额外配置

在settings.py文件中做如下操作:

 MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    # 'django.middleware.csrf.CsrfViewMiddleware',   # 注释掉
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

注:这里牵涉到中间件的问题,前期我们为了更好的学习,将此暂时注释掉,后面会进行详细学习。

三 路由系统

路由系统的本质个人理解为:建立URL与URL需要调用的视图函数之间的一种映射,就是以这种方式告诉Django,对于这个URL调用这段代码,对于那个URL调用那段代码。

Django中的路由系统和其他语言的框架有所不同,在Django中每一个请求的URL都要有一条路由映射,这样才能将请求交给对应的一个view中的函数去处理。其他大部分的Web框架则是对一类的URL请求做一条路由映射,从而使路由系统变得简洁。

urlpatterns = [
    url(正则表达式, views视图函数,参数,别名),
]

# 参数说明
# 一个正则表达式字符串
# 一个可调用对象,通常为一个视图函数或一个指定视图函数路径的字符串
# 可选的要传递给视图函数的默认参数(字典形式)
# 一个可选的name参数

3.1 一对一

顾名思义:一个URL对应一个函数,例如:

login/ ---> def login(request)

3.2 应用正则表达式

# 方式一:位置形式(此方式需严格按照位置进行传递)
login/(\+w) --> def login(request,a)       #用a来接收利用正则传到后端的数据,多个值时严格按照位置进行传递

# 方式二:键值形式(此方式可根据名称传值,位置随意)
login/(?P<a>\+w) --> def login(request,a)       #用a来接收利用正则传到后端的数据,该方式称为有名分组,语法为:?P<name>pattern,其中name是组的名称,pattern是要匹配的模式

注意:以上两种方式,不可以混用,多个参数时,后端可以用:*args/**kwargs接收数据。

补充说明:在django2.x的路由系统中,摒弃了1.x中的url,而改用path。需要导入path

from django.urls import path,re_path

在1.x中,使用url()即可实现正则匹配,但是在2.x中,是否使用正则需要使用不同的方式。

path()无法使用正则;re_path()可以使用正则。

3.3 终止符

实则上路由系统中的URL实则为正则表达式,比如:

url(r'^del_student', views.del_student)   

以上^del_student代表以del_student开头的URL均可以访问到views.del_student,解决办法:

方式一:在URL后加“/”加以明示,比如:^del_student/,访问时后面必须加入“/”,该方式没有根本解决问题,不建议使用

方式二:在URL后加终止符“$”,比如:^del_student$,推荐使用

注意:以上方式若出错,清除缓存再进行测试。

3.4 伪静态

url(r'^edit/(\w+).html$', views.edit)        #在URL后加入.html,其他也行

说明:使用正则方式取消数据以GET方式发送及使用伪静态有利于提高网站的SEO。

SEO(Search Engine Optimization):汉译为搜索引擎优化。搜索引擎优化是一种利用搜索引擎的搜索规则来提高目前网站在有关搜索引擎内的自然排名的方式。SEO的目的理解是:为网站提供生态式的自我营销解决方案,让网站在行业内占据领先地位,从而获得品牌收益;SEO包含站外SEO和站内SEO两方面;SEO是指为了从搜索引擎中获得更多的免费流量,从网站结构、内容建设方案、用户互动传播、页面等角度进行合理规划,使网站更适合搜索引擎的索引原则的行为;使网站更适合搜索引擎的索引原则又被称为对搜索引擎优化,对搜索引擎优化不仅能够提高SEO的效果,还会使搜索引擎中显示的网站相关信息对用户来说更具有吸引力(百度百科)。
扫盲:SEO

3.5 路由分发

# 主urls.py中
from django.conf.urls import include
url(r'^app01/', include('app01.urls'))
url(r'^app02/', include('app02.urls'))

# app01.urls.py中
url(r'^index.html$', views.index),

# app02.urls.py中
url(r'^index.html$', views.index),

应用场景:多个业务线(app文件夹)为了避免各个app中URL不冲突,采用此方式。

3.6 为路由映射设置名称(反向解析)

url(r'^edit/', views.edit, name="alias"),

#py中
from django.urls import reverse
a = reverse("alias")    #a则为URL名称

#前端模板中
{% url "alias" %}    #数据发送时可以用此方式指定发送路径
方式一
url(r'^edit/(\w+)', views.edit, name="alias"),

#py中
from django.urls import reverse
a = reverse("alias",args=(123,))    #a则为URL名称,args中可以随意指定值

#前端模板中
{% url "alias" value %}    #正则中的值,可能为可迭代对象,如有多个值,空格继续添加值即可
方式二
url(r'^edit/(?P<a1>\w+)', views.edit, name="alias"),

#py中
from django.urls import reverse
a = reverse("alias",kwargs={"a1":123})    #a则为URL名称,kwargs中可以a1随意指定值
方式三

注:当url地址较长时,可以使用此方式方便操作;在用户权限管理中使用较为广泛。该点仅有Django中采用。

3.7 默认路由

def default(request):
    return HttpResponse('xxxxx')

urlpatterns = [
    url(r'', default),        # 当不输入url或者url输入错误时,执行default函数(视图)
]

3.8 添加额外的参数

url(r'^manage/(?P<name>\w*)', views.manage,{'name':joe1991}),

3.9 名称空间

命名空间(Namespace )是表示标识符的可见范围。一个标识符可在多个命名空间中定义,它在不同命名空间中的含义是互不相干的。这样,在一个新的命名空间中可定义任何标识符,它们不会与任何已有的标识符发生冲突,因为已有的定义都处于其它命名空间中。

由于name没有作用域, Django 在反解 URL 时,会在项目全局顺序搜索,当查找到第一个 name 指定 URL 时,立即返回

我们在开发项目时,会经常使用name 属性反解出 URL ,当不小心在不同的 app 的 urls 中定义相同的 name 时,可能会导致 URL 反解错误,为了避免这种事情发生,引入了命名空间。注:以下代码以Django2.0为例。

from django.contrib import admin
from django.urls import path
from django.conf.urls import include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(('app01.urls','app01'), namespace='app01')),
    path('app02/', include(('app02.urls','app02'), namespace='app02')),
]
project.urls.py
from django.urls import path
from app01 import views


urlpatterns = [
    path('index/', views.index, name='index'),
]
app01.urls.py
from django.urls import path
from app02 import views


urlpatterns = [
    path('index/', views.index, name='index'),
]
app02.urls.py

以上定义带命名空间的url之后,使用name生成URL时候,应该如下:

v = reverse('app01:detail', kwargs={'xxx':'ooo'})
{% url 'app01:index' xxx=ooo %}

django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给对一个的view中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。

提醒:如果存在多级namespace,从内到外依次寻找,在反向生成时格式如下:

reverse("最外层namespace:...:最内层namespace:name")

3.10 Django2.0中的path

先看一个简单的示例:

from django.urls import path
from app02 import views


urlpatterns = [
    path(r'index/<int:year>/', views.index, name='index'),
]
View Code

通过这个示例可知在Django2.0的path中我们可以指定“数据类型”。使用规则如下:

  1. 使用尖括号<>从url中捕获值;
  2. 捕获值中可以包含一个转化器类型( converter type ),比如使用 <int:name> 捕获一个整数变量。若果没有转化器,将匹配任何字符串,当然也包括了 /字符;
  3. 无需添加前导斜杠

其实这一功能称为Path converters,暂且将其翻译为转换器。Django默认支持以下5个转换器:

  1. str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
  2. int,匹配正整数,包含 0 
  3. slug,匹配字母、数字以及横杠、下划线组成的字符串。
  4. uuid,匹配格式化的 uuid ,如 075194d3 6885 417e a8a8 6c931e272f00 。
  5. path,匹配任何非空字符串,包含了路径分隔符

注册自定义转化器

对于一些复杂或者复用的需要,可以定义自己的转化器。转化器是一个类或接口,它的要求有三点:

  1. regex 类属性,字符串类型
  2. to_python(self, value) 方法,value 是由类属性 regex 所匹配到的字符串,返回具体的 Python 变量值,以供Django 传递到对应的视图函数中。
  3. to_url(self, value) 方法,和 to_python 相反, value 是一个具体的 Python 变量值,返回其字符串,通常用于 url反向引用。

四 视图(Views)

视图接收用户的请求,做出相应的逻辑处理,然后返回响应,返回可以是普通字符串、或HTML内容的网页,或重定向,或404错误等,只要能提供路径。

通常视图文件命名为:views.py,放在项目或应用程序目录中

一个HTTP请求会产生两个核心对象(存在于django.http中):

  • HTTP请求:HttpRequest对象
  • HTTP响应:HttpResponse对象

4.1 HttpRequest对象

新建一个简易的views.py,如下:

# 从django.http模块中导入HttpResponse
from django.http import HttpResponse

def test(request):
    return HttpResponse('OK')

在这个views.py中每一个函数称作为视图函数,视图函数都以一个HttpRequest对象为第一个参数,该参数通常命名为request,通过request,我们可以拿到以下信息:

# 通过request.xx的方式进行获取
path              # 请求页面的全路径,不包括域名
get_full_path()   # 可以得到带数据得路径,而path就不行
method            # 请求中使用的HTTP方法的字符串表示,全大写表示
GET               # 包含所有HTTP GET参数的类字典对象
POST              # 包含所有HTTP POST参数的类字典对象
COOKIES           # 一个标准的Python字典,包含所有cookie,键和值都是字符串
session           # 一个可读写的类字典对象,表示当前session
body              # POST的原始数据, 用于对数据的复杂处理
has_key()         # 返回True或False,标识request.GET或request.POST是否包含所给的键
is_secure()       # 如果请求是安全的,则返回True 。也就是说,请求是以HTTPS的形式提交的
FILES             # 通过表单上传的文件全都封装在该属性中
                   """
                   filename :字符串,表示上传文件的文件名。
                   content-type :上传文件的内容类型。
                   content :上传文件的原始内容。
                   """
META              # 一个标准的Python字典,包含所有有效的HTTP头信息
                   """
                   CONTENT_LENGTH
                   CONTENT_TYPE
                   QUERY_STRING :未解析的原始请求字符串
                   REMOTE_ADDR  :客户端IP地址
                   REMOTE_HOST :客户端主机名
                   SERVER_NAME :服务器主机名
                   SERVER_PORT :服务器端口号
                   HTTP_ACCEPT_ENCODING
                   HTTP_ACCEPT_LANGUAGE
                   HTTP_HOST :客户端发送的 Host 头信息。
                   HTTP_REFERER :被指向的页面,如果存在的。
                   HTTP_USER_AGENT :客户端的user-agent字符串。
                   HTTP_X_BENDER : X-Bender 头信息的值,如果已设的话
                   """

注意:键值对的值是多个的时候,比如:checkbox类型的input标签,select标签,需要用:

request.POST.getlist("xxx")

4.2 HttpResponse对象

对于HttpRequest对象来说,它是由django自动创建的,但是,HttpResponse对象就必须我们自己创建。每个view请求处理方法必须返回一个HttpResponse对象。

比如:

return HttpResponse('OK')

在HttpResponse对象上扩展的常用方法如下:

4.2.1 render()

此方法的作用:结合一个给定的模板和一个给定的上下文字典,返回一个渲染后的 HttpResponse 对象。

通俗的讲就是把context的内容,加载进templates中定义的文件, 并通过浏览器渲染呈现.

render(request, template_name, context=None, content_type=None, status=None, using=None):
	# request: 是一个固定参数, 没什么好讲的。
	# template_name: templates 中定义的文件, 要注意路径名. 比如'templates\index.html', 参数就要写'index.html'
	# context: 要传入文件中用于渲染呈现的数据, 默认是字典格式
	# content_type: 生成的文档要使用的MIME 类型。默认为DEFAULT_CONTENT_TYPE 设置的值
	# status: http的响应代码,默认是200
	# using: 用于加载模板使用的模板引擎的名称
from django.shortcuts import render

def test(request):
    userinfo_list = {'name':'joe1991','age':18}
    return render(request,'test.html',{'userinfo_list':userinfo_list})

4.2.2 render_to_response()

此方法和 render() 差不多,它不需要传入request对象。

render_to_response(template_name, context=None, content_type=None, status=None, using=None)

4.2.3 redirect()

redirect(to, *args, permanent=False, **kwargs)

此方法可称之为页面跳转,默认情况下,为临时重定向;通过 permanent=True 设置永久重定向。

def login(request):
    '''用户登录'''
    if request.method == 'GET':
        return render(request,'login.html')
    else:
        username = request.POST.get('username')
        pwd = request.POST.get('pwd')
        if username == 'joe1991' and pwd == '123':
            # return redirect('/main/')
            return render(request, 'main.html')
        else:
            return render(request, 'login.html')

def main(request):
    '''主界面'''
    return render(request,'main.html')

前端内容略。如果我们在此render会如何?这样也可以跳转到主界面,但是url没有跳转到/main/,还是停留在/login/,这也是render()与redirect最重要的区别

4.3 其他

4.3.1 JsonResponse()

在通过Ajax发送请求时,后端传参时可能会用到JSON序列化,我们可以调用django内置JsonResponse完成

from django.http import JsonResponse
result = {'status': True, 'data': None}
return JsonResponse(result)

# 等同于
import json
return HttpResponse(json.dumps(result))

4.3.2 locals()

# 通常在后端想要将一些变量传递给前端或替换一些内容时,我们会这样做:
def test(request):
    userinfo_list = {'name':'joe1991','age':18}
    user_list = ['joe1991','joe1','joe2']
    return render(request,'test.html',{'userinfo_list':userinfo_list,'user_list':user_list})

# 那么如果变量很多的情况下,还需要再组成多组键值对进行传递?这里我们可以使用locals(),只需要将返回值做如下改变:
    return render(request,'index.html',locals())

注意:locals()是局部的,且必须保证视图函数内的变量名称和前端(模板)代码的变量名称一致

五 模板(Template)

5.1 简介

先看下面两个视图函数:

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)
def test(request):
    list = models.Boy.objects.all()
    return render(request, 'test.html', {'list': list})

我们先看第一个视图函数,HTML被直接硬编码在 Python代码之中,这种方式便于解释视图是如何工作的,但直接将HTML硬编码到视图里却并不是一个好主意,这是因为:

  • 对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多

  • Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作

  • 程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作

基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本节需要学习的内容。

再看第二个视图函数,我们单独完成一个HTML文件(test.html),然后通过从模型中获取我们需要的数据,然后传给HTML文件,完成用户层面的展示。

一句话说明:对于模板,其实就是读取模板(HTML代码+逻辑控制代码),然后将 Model 中获取的数据插入到模板中,最后将信息返回给用户。

5.2 模板语法

5.2.1 模板的组成

模板的组成包含两部分:HTML代码+逻辑控制代码

5.2.2 逻辑控制代码的组成

1. 变量(使用双大括号来引用变量)

语法格式:{{var_name}}

a. Template和Context对象

>>> python manage.py shell  (进入该django项目的环境)
>>> from django.template import Context, Template
>>> t = Template('My name is {{ name }}.')
>>> c = Context({'name': 'Joe'})
>>> t.render(c)
'My name is Joe.'


# 同一模板,多个上下文,一旦有了模板对象,我们就可以通过它渲染多个context,无论何时我们都可以
# 像这样使用同一模板源渲染多个context。只进行一次模板创建然后多次调用render()方法渲染会更为高效
# Low
for name in ('Joe', 'Jack', 'Kobe'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))

# Good
t = Template('Hello, {{ name }}')
for name in ('Joe', 'Jack', 'Kobe'):
    print t.render(Context({'name': name}))
View Code

Django 模板解析非常快捷。大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎形成鲜明对比,那些引擎承担了 XML 解析器的开销,且往往比 Django 模板渲染引擎要慢上几个数量级。

from django.shortcuts import render,HttpResponse,redirect
import datetime
from django.template import Template,Context
from django.template.loader import get_template # 记得导入


# 原始的视图函数
# def current_time(req):
#     now = datetime.datetime.now()
#     html = "<html><body>现在时刻:<h3>%s.</h3></body></html>" %now
#     return HttpResponse(html)

# django模板修改的视图函数
# def current_time(req):
#     now = datetime.datetime.now()
#     t = get_template('current_date.html')
#     c = Context({'current_date':now})
#     html = t.render(c)
#     return HttpResponse(html)

# 推荐写法
def current_time(req):
    now = datetime.datetime.now()
    return render(req, 'current_date.html', {'current_date':now})
推荐方式

b. 深度变量的查找(万能的句点号)

到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如:list、dictionary和自定义的对象。

在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。

# 句点用于访问列表索引
>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

# 句点用于访问Python字典:通过字典键访问该字典的值
>>> from django.template import Template, Context
>>> person = {'name': 'Joe', 'age': '18'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Joe is 18 years old.'

# 句点用于访问对象的属性
# 比如:Python的datetime.date对象有year、month和day几个属性,我们同样可以在模板中使用句点来访问这些属性
>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

# 句点用于访问自定义的类
# 演示了通过实例变量加句点来访问它的属性,这个方法适用于任意的对象。
>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

# 句点用于引用对象的方法
# Python字符串有upper()和isdigit()方法,在模板中可以使用同样的句点语法来调用它们
>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

# 注意这里用方法时并没有使用圆括号,而且也无法给该方法传递参数;只能调用不需参数的方法。
View Code

c. 变量的过滤器(filter)的使用

语法格式:{{obj|filter:param}}
upper				//字母大写
add             	//给变量加上相应的值
addslashes     		//给变量中的引号前加上斜线
capfirst         	//首字母大写
cut             	//从字符串中移除指定的字符
date            	//格式化日期字符串
default         	//如果值是False,就替换成设置的默认值,否则就是用本来的值
default_if_none  	//如果值是None,就替换成设置的默认值,否则就使用本来的值
...

# 实例:
# value1="aBcDe"
{{ value1|upper }}<br>

# value2=5
{{ value2|add:3 }}<br>

# value3='he  llo wo r ld'
{{ value3|cut:' ' }}<br>

# import datetime
# value4=datetime.datetime.now()
{{ value4|date:'Y-m-d' }}<br>

# value5=[]
{{ value5|default:'空的' }}<br>

# value6='1234'
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|slice:":-1" }}<br>
View Code

2. 标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)

{% tags %}

a.if...else

{% if %}会对一个变量求值,如果它的值是 “ True”(存在、不为空、且不是 boolean 类型的 false 值),对应的内容块会输出。

{% if 条件 %} 
    执行代码
{% elif 条件 %} 
    执行代码
{% else %} 
    执行代码
{% endif %}

注意:

{% if %} 标签接受and,or或者not来测试多个变量值或者否定一个给定的变量
{% if %} 标签不允许同一标签里同时出现and和or,否则逻辑容易产生歧义

# 例如下面的标签是不合法的:
{% if obj1 and obj2 or obj3 %}  

b. for循环

def test(request):
    list = models.Boy.objects.all()
    return render(request,'test.html',{'list':list})
{% for item in list %}
    <p>{{ item.name }}</p>
{% endfor %}

可以利用{% for obj in list reversed %}反向完成循环。

遍历一个字典:

{% for key,val in dic.items %}
    <P>{{key}}:{{val}}
{% endfor %}

注意:循环序号可以通过{{ forloop }}显示

forloop.counter             The current iteration of the loop (1 indexed)
forloop.counter0            The current iteration of the loop (0 indexed)
forloop.revcounter          The number of iterations from the end of the loop (1 i ndexed)
forloop.revcounter0         The number of iterations from the end of the loop (0 indexed)
forloop.first               True if this is the first time through the loop
forloop.last                True if this is the last time through the loop

for ... empty

for标签带有一个可选的 {% empty %} 从句,以便在给出的组是空的或者没有被找到时,可以有所操作。

{% for person in person_list %}
    <p>{{ person.name }}</p>
{% empty %}
    <p>sorry,no person here</p>
{% endfor %}

c. {%csrf_token%}:csrf_token标签

用于生成csrf_token的标签,用于防治跨站攻击验证。如果在view的return里用的是render_to_response方法,不会生效。其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。

d. {% url %}:引用路由配置的地址

<form action="{% url "xxx"%}" >
	<input type="text">
	<input type="submit" value="提交">
	{%csrf_token%}
</form>
View Code

e. {% with %}:用更简单的变量名替代复杂的变量名

{% with new_name=old_name %} {{ new_name }} {% endwith %}

f. {% verbatim %}: 禁止render

g. {% load %}:加载标签库

3. include--导入小组件

在学习了模板加载机制之后,我们再学习一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <h3>用于include导入</h3>
        <div class="title">标题:{{ name }}</div>
        <div class="content">内容:{{ content }}</div>
    </div>
</body>
</html>
待导入小组件pub.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
        {% include 'pub.html' %}
        {% include 'pub.html' %}
        {% include 'pub.html' %}
</body>
</html>
include导入
def test(request):
    return render(request, 'test.html', {'name': '你好','content':'hello world'})
视图函数

注:include可以导入多次,接下来学习的extends仅一次

4. 模板继承

到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,我们将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?

解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承

本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。结构如下:

//母板mother_blank.html
<html>
    ...
    {% block css %} {%endblock%}
    ...
    {% block html %} {%endblock%}
    ...
    {% block js %} {%endblock%}

</html>
    
//子板.html文件
{% extends "mother_blank.py "%}        //扩展母板

{% block css %} 
    添加内容 
{%endblock%}

{% block html %} 
    添加内容 
{%endblock%}

{% block js %} 
    添加内容 
{%endblock%}

下面看一个实例:

首先,我们先建立一个母板:base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>我的个人网站</h1>
    {% block content %}{% endblock %}
    <hr>
    {% block footer %}
    <p>欢迎访问我的个人网站</p>
    {% endblock %}
</body>
</html>
base.html

这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。

我们使用模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。

接下来,我们新建页面来继承它:

{% extends "base.html" %}

{% block title %}页面1{% endblock %}

{% block content %}
<p>这是页面1</p>
{% endblock %}
page1.html
{% extends "base.html" %}

{% block title %}页面2{% endblock %}

{% block content %}
    <p>这是页面2</p>
{% endblock %}

{% block footer %}
    <p>我修改了网站页脚</p>
{% endblock %}
page2.html

这样每个子模板只包含对自己而言独一无二的代码,无需多余的部分。 如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。

其工作方式如下:

在加载 page1.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。

注意:由于page1.html子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值(page2.html修改了则按照修改进行渲染)。 父模板 {% block %} 标签中的内容总是被当作一条退路。继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:

1.创建 base.html 模板,在其中定义站点的主要外观感受。这些都是不常修改甚至从不修改的部分
2.为网站的每个区域创建base_SECTION.html模板(例如:base_photos.html 和 base_forum.html )。这些模板对base.html进行拓展,并包含区域特定的风格与设计
3.为每种类型的页面创建独立的模板,例如:论坛页面或者图片库。这些模板拓展相应的区域模板
View Code
1.如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。

2.一般来说,基础模板中的 {% block %} 标签越多越好。记住,子模板不必定义父模板中所有的代码块,
因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。

3.如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。
如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。

4.不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。
也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。
如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
使用模板继承的一些诀窍

5. 内置函数的使用

def test(request):
    list = {'k1':'v1','k2':'v2','k3':'v3'}
    return render(request, 'test.html', {'list': list})
视图函数
{% for item in list %}
    {{ item }}                    //k1 k2 k3
{% endfor %}

{% for item in list.keys %}
    {{ item }}                    //k1 k2 k3
{% endfor %}

{% for item in list.values %}
    {{ item }}                    //v1 v2 v3
{% endfor %}

{% for k,v in list.items %}
    {{ k }}--{{ v }}            //k1--v1 k2--v2 k3--v3
{% endfor %}
模板文件

注:使用自带函数不需要加‘括号’,Django内部会自动解析执行。

6. 自定义filter&simple_tag

第一步:在app中创建templatetags模块(必须的)

第二步:创建任意.py文件,如:xxx.py

from django import template

register = template.Library()     #名称必须是register

@register.filter
def my_upper(value):                #无参
    return value.upper()

@register.filter
def my_func1(value,arg):            #有参,最多只能有两个参数,模板调用方式{{第一个参数|函数名称:"第二个参数"}}
    return value.upper()+ arg

@register.filter
def my_bool(value):             #作为模板if判断条件
    return False

@register.simple_tag
def my_lower(value):            #无参
    return value.lower()

@register.simple_tag
def my_func2(value,a1,a2,a3):   #有参,可以有多个函数,模板调用方式{% 函数名称 '第一个参数' ... '第N个参数'%}
    return value + a1 + a2 + a3
xxx.py

第三步:在使用自定义filter/simple_tag的html文件中导入之前创建的 xxx.py 文件名

{% load xxx %}

第四步:使用filter/simple_tag 

<h2>filter示例</h2>
<p>无参:{{ name|my_upper}}</p>
<p>有参:{{ name1|my_func1:' hello'}}</p>
<h4>filter作为if判断条件</h4>
{% if name|my_bool %}
    <h4>真</h4>
{% else %}
    <h4>假</h4>
{% endif %}

<h2>simple_tag示例</h2>
<p>无参:{% my_lower "JOE" %}</p>
<p>有参:{% my_func2 'joe' '!' ' ' 'hello world' %}</p>

注意点:在settings中需要配置当前app,不然django无法找到自定义的simple_tag

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',           #进行配置
)
filter
    - 最多两个参数,方式: {{ 参数1|函数名称:"参数2" }}    #注意:冒号与参数之间不能有空格
    - 可以做条件判断
    - 装饰器为:@register.filter

simple_tag
    - 参数个数无限制,方式: {% 函数名 参数1 ... 参数N %}
    - 装饰器为:@register.simple_tag
filter&simple_tag的区别

六 模型(Models)

Django提供了一个抽象层("Model")来构建和管理Web应用程序的数据。

6.1 数据库的配置

6.1.1 django默认支持sqlite,mysql,oracle,postgresql数据库

1. sqlite

django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 ,引擎名称:django.db.backends.sqlite3

2. mysql

引擎名称:django.db.backends.mysql

6.1.2  mysql驱动程序
  •    MySQLdb(mysql python)
  •    mysqlclient
  •    MySQL
  •    PyMySQL(纯python的mysql驱动程序)
6.1.3 在django的项目中会默认使用sqlite数据库,在settings里有如下设置:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

如果我们想要更改数据库为MySQL,需要修改配置如下:

DATABASES = {
    'default': {
    'ENGINE': 'django.db.backends.mysql',
    'NAME':'dbname',  # 数据库名称
    'USER': 'root',  # 数据库用户名
    'PASSWORD': 'xxx',  # 数据库密码
    'HOST': '',  # 数据库主机,留空默认为localhost
    'PORT': '',  # 数据库端口
    }
}
在mysql连接该数据库前,该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建

设置完后,在启动我们的Django项目前,我们需要激活我们的mysql

然后,启动项目,会报错:no module named MySQLdb

这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL

所以,我们只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()
注意点

6.2 ORM

请参考后续文章

七 其他

正常情况下,我们会在Django工程中,创建多个application,创建方式:

方式一:终端输入

python manage.py startapp app_name      #需在manage.py同级目录下执行

方式二:在pycharm创建Django工程时,按如下操作

image

截止到目前所学的知识,还没有对Django ORM知识的了解,但是我们可以通过直接对数据库的操作进行web开发了。

 

参考:https://www.cnblogs.com/yuanchenqi/articles/6083427.html

posted @ 2018-09-27 12:15  Joe1991  阅读(229)  评论(0)    收藏  举报