我的Django手册

django

原文链接

Django教程

Python一直是我最喜欢的语言,在这个寒假打算认真学习一下Python的Web框架。在Django和Tornado之间我选择了前者,没有特别的原因,网上人云亦云的,肯定不会有一方离另一方差很远,我就直接去看了看Github上两个项目的活跃度,所以选择了前者。

应该说Django坚持自己造轮子,确实为开发者节约了不少的时间,我很看重它的扩展功能,packages数量十分丰富。Django采用的是最流行也是我最熟悉的MVC设计模式,虽然在之前的一个PHP(Laravel)项目中也是采用的MVC模式,但一直都没怎么吃透,始终在各层分离的时候不是很清晰,所以也可趁学习Django对MVC的概念进行强化。

Django另一个我特别喜欢的特性就是Application,它与Project的概念不同,一个APP就相当于一个功能模块,一个Project可以包含多个APP,一个APP可以同时被多个Project引用,App增加了代码的复用机会,提高了扩展性和松耦合性,Django中很多的packages都是以APP的形式存在的。

另外,我学习主要参考的是开源书籍 Django搭建简易博客教程

下面是搭建一个Django环境的基本步骤:

  1. 使用Virtualenv搭建Python3的Django环境

  2. 新建项目 django-admin startproject 项目名 这样会在当前目录新建一个目录,里面已经有一些基本的配置文件:

    django_test
    ├── db.sqlite3
    ├── django_test
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py
    
  3. 新建APP django-admin startapp APP名称 如果是搭建一个非常简单的应用,那么不使用APP也行,只需要吧路由指向目标view就可以了,但是如果要搭建复杂的应用并且需要良好的隔离性,那最好使用APP。同样,使用该命令也会在当前目录下新建一个目录,里面已经包含一些配置文件:

    django_test/testapp
    ├── admin.py # 注册models,用于admin管理
    ├── __init__.py
    ├── migrations   # 数据库迁移
    ├── models.py    # 定义models
    ├── tests.py # 单元测试
    ├── apps.py      # App的配置类,AppConfig用于存储应用程序的元数据
    └── views.py # 视图文件
    

    如过添加了APP,那么需要在主配置文件settings.py里面的INSTALLED_APPS里面添加该APP的名称

  4. Hello World! 所有入门教程都必须要有一个Hello World! 首先,在APP的视图文件views.py里添加函数,该函数直接返回一个字符串的响应:

    from django.shortcuts import render
    from django.http import HttpResponse
    def hello(request):
     return HttpResponse('Hello World!')
    

    然后添加URL,在Project目录里的urls.py里进行管理,添加hello的url如下:

    from django.conf.urls import patterns, include, url
    from django.contrib import admin

    urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'^hello/', 'testapp.views.hello'),
    ]

  5. 运行 python manage.py runserver 如果要以daemon的方式在后台运行,可以使用nohup命令 nohup python manage.py runserver 0.0.0.0:8000 & 使用它可以打开Django自带的默认Web引擎,可以在 http://127.0.0.1:8000中查看 在测试的时候可以使用该引擎,它不仅轻量,而且在打开后还会自动检测代码的更改,进行自动更新,这样就不用每次对代码变动了都来重启一次

配置项

全局配置

需要注意的是,Django官方并没有默认的分离配置文件的方案,我觉得最佳的方式是,建立多个配置文件,然后在默认的配置文件里面进行导入即可。例如:

env = 'local'

if env == 'local':
    from settings_local import *
else:
    from settings_prod import *

配置文件内容

DEBUG = True            # DEBUG模式
TEMPLATE_DEBUG = True   # TEMPLATE的DEBUG模式

ALLOWED_HOSTS = []      # 设置哪些域名可以访问,当debug为false时必须为其指定一个值,['*']表示允许所有的访问

INSTALLED_APPS = [默认APP+自己的APP]
MIDDLEWARE_CLASSES = [中间件]

ROOT_URLCONF = 'admin.urls' # 读取的默认的url文件

# Database 数据库的配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   # MySQL/Mariadb数据库设置
        'NAME': 'admin',
        'USER': 'root',
        'PASSWORD': 'mysql',
        'HOST': '127.0.0.1',
        'PORT': 3307
    }
}

LANGUAGE_CODE = 'en-us'  # 语言,中文可用zh-Hans、zh-CN,完整列表见:http://www.i18nguy.com/unicode/language-identifiers.html
TIME_ZONE = 'Asia/Chongqing'        # 时区
USE_TZ = False  # 数据库中要使用时间戳就应该关闭这个

# Static files (CSS, JavaScript, Images) 静态文件目录  
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # 这个选项默认是没有的,在编码时将静态文件放在APP中的static目录下,部署时用python manage.py collectstatic就可以把静态文件收集到STATIDC_ROOT目录

在其他文件访问全局配置项,可以这样访问:

from django.conf import settings
settings.DEBUG

应用配置

在上面新建的app的目录结构里面又一个apps.py文件,它存储了应用的元数据,通过继承AppConfig来配置其属性,可配置的选项如下:

可配置的属性
AppConfig.name          # 应用的完整Python路径,例如django.crontrib.admin,在整个Django项目中必须是唯一的
AppConfig.label         # 应用的缩写,例如admin,
AppConfig.verbose_name  # 应用的适合阅读的名称
AppConfig.path          # 应用目录的文件系统路径,例如/usr/lib/python3.4/dist-packages/django/contrib/admin

可配置的方法
AppConfig.get_models()          # 返回可迭代的Model类
AppConfig.get_model(model_name) # 返回具体的Model
AppConfig.ready()               # 执行初始化任务

请求与响应

HttpResponse('字符串', content_type="text/plain") # 指定content_type的响应
    
HttpRequest.method   # 请求种类
HttpRequest.GET      # 获取所有的GET参数(字典)
HttpRequest.POST     # 获取POST的参数(字典)
HttpRequest.scheme   # 表示请求的模式,是http还是https
HttpRequest.cookies  # 包含了所有的cookie信息
HttpRequest.session  # session信息
HttpRequest.FILES    # 包含了上传的文件
HttpRequest.meta     # 包含了http请求的各种headers
HttpRequest.user     # 当前的登录的用户,配合着auth使用

get_host()           #不解释了吧
get_full_path()      # 获取路径,不包含域名
build_absolute_uri() # 获取完整路径
is_secure()          # 如果是https返回true,否则false
is_ajax()            # 是否是ajax请求

return JsonResponse(error, status = 422)    # 返回指定状态码
ip = request.META.get('REMOTE_ADDR')    # 获取用户IP
return HttpResponseRedirect('/')        # 重定向

路由与视图

url: web访问请求的入口(相当于Laravel里的路由)
view:应用的逻辑部分,从客户端接收请求,处理并返回数据,一般返回到template模板进行渲染(相当于Laravel里的控制器)
/test定位到article这个APP里面的views里面的home方法来处理的形式

url(r'^test$', 'article.views.home')

url传递参数

Django的路由是采用正则表达式来匹配的,同样能使用命名组,比如(?P<name>),这样就可以通过URL给views传递参数了,例如:

# 有如下视图
def hello(request, name):
    return HttpResponse('name is %s', % name)
# 在url中可以这样写
url(r'^(?P<name>\d+)/$', 'testapp.views.hello)

url命名

url(r'^add/$', 'app.views.add', name='add')

给url命名可以方便我们进行统一修改url样式,比如之前用a-and-b的url可以访问到add这个方法,但现在如果想改成a/b的方式来访问,那么由于后端的模板渲染等都是用的其命名add,就无需修改后端逻辑了

路由按照app分组

首先主urls.py里面使用include包含应用下的urls.py文件

from django.conf.urls import include
urlpatterns = [
    url(r'^app1', include('app1.urls')),
]

然后在app1下的urls.py文件里面,进行如下设置:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^/$', views.hello),
]

这样就可以通过/app1/,来访问app1下的hello方法了

数据库

Django同很多框架一样使用了ORM(Object Relational Mapping,对象关系映射)的方式,每个model类代表一张数据库的表,每一个属性代表其一个字段(这种特性的实现依赖于python的元类)。

数据表定义

定义model的文件是project/app/models.py里面,例如,要定义一张用户表:

fromo django.db import models
class User(models.Model):
    username = models.CharField(max_length = 20)    # 用户名字段
    create_time = models.DateTimeField(auto_now_add = True) # 注册日期字段,如果同时有两个字段对应着同一个外键,那么久得重命名字段名了,比如:
    receiver = models.ForeignKey(Users, null=True, related_name='receiver')
    poster = models.ForeignKey(Users, null=True, related_name='poster')
    
    def __str__(self):
        '''这个函数可以用于str(obj)函数来输出该对象的信息,默认是表名'''
        return self.username
    
    class Meta:
        db_table = '自定义表名'
        unique_together = ('column_1', 'column_2')  # 联合唯一键

当建立好models过后,执行如下命令就可以在数据库中新建或更新数据表了:

python manage.py makemigrations
python manage.py migrate

注:如果是有修改的,那么新添加的数据必须要允许null或者设置默认值,否则会报错,这其实是为了保护已经存在了的数据,当然在添加完该字段后把null去掉再更新数据库就可以了。

字段类型

注:Django默认为每张表设置了一个int(11)的自增主键,不需要自己去定义了。

字段通用参数

primary_key = False/True   # 是否设置为主键
blank = False/True         # 是否可为空,这其实是用于Field的判断
null = False/True          # 是否可为空,这才是真正的数据库里面是否可以为null
max_length = 3             # 最大长度,对整型无效
default = ''               # 设置默认值
verbose_name =             # 相当于备注,如果没给出那么就是该字段,当然,要指定的话,可以直接第一个参数一个字符串就可以指定了
editable = False/True      # 是否可编辑
unique = False/True        # 是否唯一
auto_now = False/True      # 用于时间,每次更新记录的时候更新该字段
auto_now_add = False/True  # 用于时间,创建新纪录的时候自动更新该字段
choices # 很实用的一个功能,相当于存储一个枚举列表,其中左边的key是实际存储在数据库中的值,例如,可以这样定义一个字段:
    YEAR_IN_SCHOOL_CHOICES= (
        ('FR', 'Freshman'),
        ('SO', 'Sophomore'),
        ('JR', 'Junior'),
        ('SR', 'Senior'),
    )
    然后在定义字段的时候给个参数choices=YEAR_IN_SCHOOL_CHOICES,在插入字段的时候,使用'RF'这样的,在获取字段值的时候这样:p.get_year_in_school_display()即可显示'Freshman'
verbose_name        # 定义字段的注释

常用类型

# 数字类型:
AutoField       # 自增长字段
IntegerField    # 长度为11的整数
PositiveIntegerField:
SmallIntegerField
PositiveSmallIntegerField
BigIntegerField:
BinaryField:
BooleanField:
NullBooleanField:
DecimalField(max_digits = None, decimal_places = None)
FloatField

# 字符类型
CharField   # 字符串类型,可用max_length指定长度,枚举类型也使用该方式,只需要指定枚举枚举元组即可,例如type = models.CharField('类型', choices=CONTENT_TYPE),其中CONTENT_TYPE=(('a', 'abc'))
TextField:text类型
CommaSeparatedIntegerField:用逗号分隔的整数,我擦,这有用

# 时间类型
DateField   # DATE类型
TimeField   # datetime.time,时间
DateTimeField() # DATETIME类型,包括了日期和时间,需要注意的是Django默认的TIME_ZONE是UTC,在初始化的时候,格式如"2015-04-27T15:01:00Z",它属于python里面的datetime.datetime类型,可分别用year/month/day等获取时间。另外Django如果要实用MySQL里面的TIMESTAMP类型也是用该字段表示,并且在插入的时候不能直接插入一个整数,依然只能插入一个datetime.datetime对象,用时间戳的时候USE_TZ必须为False
unique_for_date属性:比如,如果有一个title字段,其有一个参数unique_for_date = "pub_date",那么该表就不会出现title和pub_date同时相同的情况,其它的还有unique_for_month,unique_for_year

其它很有用的类型:
EmailField:Email邮箱类型
FileField:文件类型,不过不能设置为primary_key和unique,要使用该字段还有很多需要注意的地方,具体的见官方文档
FilePathField:同上
ImageField
IPAddressField:从1.7开始已经不建议使用了,应该使用下面这个
GenericIPAddressField:
URLField
UUIDField

数据库SQL操作

要使用model,必须先导入它,例如from app.models import Blog,一条记录就是一个model类的实例,要获取其字段值,直接用点号访问即可,例如有Blog对象blog,那么可以直接用blog.userName访问其值。
如果想要在执行数据库操作的时候查看数据库的实际的SQL语句,可以这样

from django.db import connection
print(Blogobjects.filter(name="").query)    # 这样可以将SQL语句打印出来

connection.queries          # 会返回一个所有执行过的SQL的列表,并且每条时一个字典,包含了SQL语句以及SQL所执行的时间

查询记录

Blog.objects.all()          # 获取该表的所有记录,返回的是记录对象组成的列表
Blog.objects.get(pk=1)      # 根据主键获取数据
Blog.objects.get(name="")   # 只会找到第一个匹配的数据
Blog.objects.filter(name="")# 这个就会找到匹配的多个数据
Blog.objects.filter(name__contains="")  # 模糊查找name字段的值,返回列表
Blog.objects.order_by("字段1", "字段2")  # 排序,order_by不加任何参数表示不需要排序
Blog.objects.all().order_by("字段")
Blog.objects.count()                    # 返回记录总数

Blog.objects.values('id', 'name')       # 相当于select id name from Blog,返回的事是一个字典
Blog.objects.values('name').distinct()  # distinct在django的mysql引擎中无法对field进行distinct操作,所以需要这样做
Blog.objects.values_list('id', flat=True)# 查询该字段的所有值并且返回的是id的列表,而不是包括了名字的字典
Blog.objects.all().defer('title')       # 仅仅取某个字段,这里返回是一个model对象
Blog.objects.all().only('title')        # 仅仅取某个字段,也是返回一个model对戏那个
Blog.objects.all().values_list('title') # 仅仅取某个字段,这里返回一个数组

Blog.objects.latest('id')               # 根据某个字段查找其最后一条记录,返回的是一个对戏那个,不是id
Blog.objects.filter(time__gte = '2015-07-23', time__lte = '2015-07-24') # 大于等于并且小于等于,不加e表示不能等于
Blog.objects.filter(time__isnull = True)# 判断某个字段是否为空
Blog.objects.all().exclude(id=7)        # 排除,即不等于
Blog.objects.filter('time__year': '2015', 'time__month': '08', 'time__day': '17'):按年月日查询日期,可仅查询其中某一个条件


# Q查询,可以对关键字参数进行封装,可以使用&,|,~等操作
from django.db.models import Q
Blog.objects.filter( Q(name__startswith='wang') | ~Q(name__startswith='hao') )
Blog.objects.get( Q(name__startswith='wang'), Q(name__startswith='hao')) # 逗号就可以直接表示and了
        print(People.objects.filter(
        (Q(birth_lunar__month=old_month) & Q(birth_lunar__day=old_day)) |
        (Q(birth_new__month=new_month) & Q(birth_new__day=new_day))
    ).query)

新增记录

post = Blog(userName="wanghao", userId=12)
post.save()

# 批量插入/新增
posts = []
for i in title:
    posts.append(Posts(title = title))
Posts.objects.bulk_create(posts)

更新记录

post = Blog.objects.filter(id=1).update(userName="new") #1.7之前更新单条记录如果要用字典的话就只能这样了
Blog.objects.all().update(userName="new")  # 还可以批量更新
obj, created = Posts.objects.update_or_create(pk = 3, title='wang', defaults = updated_values)   # 1.7之后可以用这种方法来更新或者创建一个,如果没找到对象,那么就新建,新建或者更新的字典是defaults的值,返回值中,obj表示该对象,created是一个布尔值
get_or_create(title='wang', defaults=\{\}):获取或者新建

删除记录

Blog.objects.get(userName="xiao").delete()
Blog.objects.all().delete()
Blog.author.through.objects.filter(author = author.id).delete()  # 删除多对多关系,仅仅是删除关系,而不是删除对象

数据约束

ForeignKey

例如:

# modles.py
class System(models.Model):
    name = models.CharField(max_length = 20)

class Server(models.Model):
    ip = models.GenericIPAddressField(default = '127.0.0.1')
    system = models.ForeignKey(System)

# views.py里面这样子使用
server = Server.objects.get(id=1)
server_system = server.system.name # 这样就可以获取到那个name了

OneToMany(hasMany)

一对多关系,同样使用ForeighKey实现,例如

# 在models.py中定义
class Posts(models.Model):
    title = models.CharField('标题', max_length = 50)

class Comments(models.Model):
    post = models.ForeignKey(Posts, related_name = 'comments_set')

# 在views.py中这么用
post = Posts.objects.get(pk = 1)    # 获取一篇文章
comments = post.comments_set.all()  # 获取该文章的所有评论,是一个列表

ManyToManyField

多对多关系,有一种特殊情况,如果需要对这种关系添加额外的字段,可以使用through,添加额外的表来表示,例如,用户一张表,被使用的物品一张表,用户与物品是多对多的关系,但是有时候我们需要记录下用户使用该物品的一些其他属性,比如使用了多少次什么的,这时候就需要给这个多对多关系添加额外的字段来表示,那就需要添加额外的表了,示例如下:

class User(models.Model):
    username = models.CharField(max_length = 20)
    goods = models.ManyToManyField('物品', 'Goods', through='user_goods')

class Goods(models.Model):
    goodsname = models.CharField(max_length = 20)

class user_goods(models.Model):
    user = models.ForeignKey(User)
    goods = models.ForeignKey(Goods)
    clicks = models.IntegerField('点击量', default=0)

OneToOneField

必须是一对一,而不是多对一或一对多

分页

Django使用内建的paginator模块进行分页的操作,十分方便。使用方法见例子:

from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger  # 导入模块
def listing(request):
    contact_list = Contacts.objects.all()   # 获取所有model对象
    paginator = Paginator(contact_list, 25) # 第二个参数是每页显示的数量
    page = request.GET.get('page')          # 获取URL参数中的page number
    try:
        contacts = paginator.page(page)
    except PageNotAnInteger:                # 若不是整数则跳到第一页
        contacts = paginator.page(1)
    except EmptyPage:                       # 若超过了则最后一页
        contacts = paginator.page(paginator.num_pages)

    return render_to_response('list.html', {"contacts": contacts})

虽然contacts是一个Page对象,但是在模板中仍然可以使用for循环对其进行遍历,它其实是一个对象所组成的list。下面是分页按钮html模板例子:

<nav>
    <ul class="pagination">
        <li class="{% if current_page == 1 %}disabled{% endif %}"><a href="#" aria-label="Previous"><span aria-hidden="true"></span></a></li>
        {% for index in page_index %}
            {% if index == current_page %}class="active"{% endif %}<a href="#";{{ index }}<span class="sr-only"(current)/</span></a></li>
            {% endfor %}
        <li class="{% if current_page == num_pages %}disabled{% endif %}<a href="#" aria-label="Previous"><span aria-hidden="true"></span></a></li>
    </ul>
</nav>

初始化数据

为了方便迁移,让别人使用你的APP,有时候需要为APP里面的表提供demo数据,这时候就需要预先填充一些数据.这里使用Django的fixtures方式填充(Django提供两种填充方式)。使用JSON格式,我们可以首先使用manage.py dumpdata data.json方式到处原来数据库中内容看看该格式,类似如下:

[
    {
        "fields": {
        "userName": "小豪",
        "title": "第一篇文章",
        "userId": 1,
        "update": "2015-04-27T15:01:03Z",
        "datetime": "2015-04-27T15:01:00Z",
        "content": "这是文章的内容"
        },
        "model": "digital.blog",
        "pk": 1
    },
    {
        "fields": {
            "userName": "笑总",
            "title": "第二篇文章",
            "userId": 2,
            "update": "2015-04-28T15:01:03Z",
            "datetime": "2015-04-28T15:01:03Z",
            "content": "这是文章的内容吗"
        },
        "model": "digital.blog",
        "pk": 2
    },
]

我们可以自己按照这个模板新建填充数据,其中pk指的是主键值。当建立好json文件过后,执行python manage.py loaddata data.json即可导入数据。

Template: Django模板

和所有的MVC框架一样,模板功能是必须有的。这里介绍一下Django模板的使用方法。

模板定义

为了方便管理,最好在app的目录下新建templates文件夹用于存放模板文件,然后在project的配置文件settings.py中指明模板文件夹的位置:

TEMPLATES['DIRS']这个变量中添加即可,比如
'DIRS': [
    os.path.join(BASE_DIR, 'dashboard/templates').replace('\\', '/'),
]

这样,在该app的view中就可以这样使用templates下的test.html模板文件了。例如:

def test(request):
    return render(request, 'test.html')

参数传递

要向模板中传递参数,可以给render添加第三个参数,该参数其实是一个字典,在模板中可以直接使用该字典的key,例如:

return render(request, 'test.html', {'name1': value1, 'name2': value2} )

这样,在模板文件test.html中就可以直接{{ name1 }}来使用name1的值了。

继承与引用

模板方便之处就是可以使用继承将代码分块并且将重复的地方都写在一个base.html里。当要实现继承的时候在html文件第一行写上{% extends 'base.html' %},然后分别实现其区块即可。 在base模板中一般这样定义区块:

{% block 块名 %}
    这里直接写html代码
{% endblock %}

如果子模块没有定义某个block的内容,那么就采用父模板的,如果需要使用父模板的内容可以用{{ block.super }}
模板也可以通过引用其它模板的代码,例如,在要引用的地方使用:

{% include 'nav.html' %}
{% include 'includes/nav.html' %}

静态文件css、js、img

静态文件一般当然是要存放在自己的app里面,这时候,就应该指定静态文件的路径,在project的配置文件settings.py中添加如下配置:
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
'f:/project/app/static', # 无论是windows还是linux都要用左斜杠哟
)
在模板中使用静态文件就这样:
{% load staticfiles %}
<link href="{% static 'css/style.css' %}" rel="stylesheet">

模板标签

Django内置了一些比较常用又实用的标签:

# 变量
## 通过下标获取列表变量的值
{{ names.0 }}

# 注释
{# 单行注释 #}
{% comment %}多行注释{% endcomment %}

# url路由
有这样一个路由
url(r'^blog/', 'myapp.views.blog'), # 博客页面
那么就这样使用
{% url 'digital.views.blog' %}
或者使用别名,例如
url(r'^blog/', 'myapp.views.blog', name='blog'),
{% url 'blog' %}
如果是其他app的url,那么需要带上该app的namespace,首先在定义的时候需要添加namespace,如
url(r'^oauth/', include('oauth.urls', namespace='oauth'))
然后在实用url的时候:
{% url 'oauth:hello' %}

# for循环
{% for <element> in <list> %}{% endfor %}
{% for <element> in <list> reversed%}{% endfor %} 反向迭代列表
{% for <element> in <list> %}{% empty %}{% endfor %} 列表为空时的输出内容
{{ forloop.counter }}   # 获取当前索引,默认从1开始
{{ forloop.counter0 }}  # 获取当前索引,从0开始

# if语句
{% if <element> %}
{% elif <element> %} 
{% else %}
{% endif %}

{% ifequal 变量1 变量2 %}
比较值
{% endifequal %}
ifnotequal同上

过滤器

可以直接格式化输出,是一种最便捷的转换变量输出格式的方式。

{{ today | data: "F j, Y" }}

这里可以将today这个变量直接按照规定格式输出。
这里是常见的过滤器:

add:将该数字加上一个数字,例如 `{{ value|add:"2" }}`,如果原来的值为4,那么新的值就为6,不仅进可以作用与int,还能作用与列表,将列表中每个值都加
addslashes:添加反斜杠到需要转义的地方前
capfirst:第一个字母大写
center:在字符串前后加空格,并让该字符串位于中间,例如 `{{ value|center: "5" }}`,那么输出时前后都是5个空格
cut:去除字符串中的指定字符,例如`{{ value|cut:" " }}`,表示去除字符串中的所有空格
date:按指定的格式字符串参数格式化date或者datetime对象,参数太多了,看[文档](https://docs.djangoproject.com/en/1.8/ref/templates/builtins/)
default:参数没赋值的情况下给定默认值,例如`{{ value|default: "nothing" }}`
default_if_none:如果它是none就给定默认值
dictsort:将一个字典组成的列表排序,例如`{{ value|dictsort:"name" }}`意思是将value这个列表里面的字典按照字典里的name的顺序来排序
dictsortreversed:与上面顺序相反
divisibleby:是否能被整除,返回True/False,例如`{{ value|divisibleby:"3" }}`
filesizeformat:-h方式输出文件的大小,例如`{{ value|filesizeformat}}`,如果value=123456789,那么输出将是117.7MB
first:返回列表的第一个值
floatformat:设置浮点数的显示形式
get_digit:获取一个整数的倒数第几个数字,例如`{{ value|get_digit:"2" }}`,那么123456789的值为8
join:将一个列表的值添加一个分隔符并以字符串形式输出,例如`{{ value|join:"//"}}`那么['a', 'b', 'c']输出将是"a//b//c"
last:返回列表的最后yield值
length:返回变量的长度,也可以在if语句里面使用,例如 {% if messages|length >= 100%} ...{% endif %}
length_is:判断一长度是否是某个值,例如`{{ vlaue|length_is:"4" }}`如果value长度是4那么就返回True
linebreaks:替换换行符,例如如果value的值是Joel\nis a slug,那么输出就是<<p>Joel<br /> is a slug</p>
linenumbers:在输出的tex前加上标号
ljust:在字符串后面加上指定长度的宽度,例如`{{ value|ljust:"10" }}`
make_list:将整型或字符串转换为单个单个的列表元素组成一个列表
random:在列表里面随机选取一个元素
lower:转换为小写
rjust:在字符串前面加上指定长度的宽度
slice:列表分片,例如`{{ some_list|slice:":2"}}`就表示前面两个元素妈
slugify:
stringformat:
striptags:取出HTML中的tag,只去内容
time:同date
timesince:
timeuntil
title:将一个字符串转换为title的形式,即一般的第一个字母大写那种标题
truncatewords: "30":表示只显示前面30个字符
truncatechars_html:
truncatewords:显示前面多少个字符,单位是词,而不是字符
truncatewords_html:
unordered_list:
upper:转换为大写
urlencode:将url进行编码,例如"http://www.example.org/foo?a=b&c=d"被编码为“http\%3A//www.example.org/foo\%3Fa\%3Db\%26c\%3Dd”
urlize:
urlizetrunc:
wordcount:统计字符串中单词的数量
wordwrap:指定特定的长度来分隔字符串
yesno:

其他标签

# autoescape标签
{% autoescape on %}       # 去掉自动转义
{{ body }}
{% endautoescape %}

# cycle标签:每次使用该标签,标签中的值就会变化,比如下面这个,第一次该值为row1,第二次则为row2,第三次又变为了1,感觉可以用于循环里面的奇偶什么的
{% for o in some_list %}
<tr class="{% cycle 'row1' 'row'2 %}
...
</tr>
{% endfor %}

# now 标签,直接将当前时间按指定格式输出:
{% now "jS F Y H:i" %}

# spaceless标签:移出HTML tags之间的空白
{% spaceless %}
<p>
...
</p>
{% endspaceless %}

# verbatim:停止模版引擎,一般用于在模板里面写Javascript什么的
{% verbatim %}
...
{% endverbatim %}

# with标签:和语法里面的with类似
{% with total=business.employees.count %}
{{ total }} employee{{ total|pluralize }}
{% endwith %}

用户认证系统

Django项目默认添加了用户认证系统的,可以通过

python manage.py makemigrations
python manage.py migrate

将认证系统的数据表添加到数据库中去.

创建用户

from django.contrib.auth.models import User
user = User.objects.create_user('john', 'lennon@thebeatles.com', 'johnpassword')
user.save()

创建超级用户

$ python manage.py createsuperuser --email=896499825@qq.com --username=wanghao
Password:
Password (again):
Superuser created successfully.

验证用户登录

需要注意的是,为了不与login冲突,views最好不要写成login

from django.contrib.auth import authenticate, login
user = authenticate(username='hao', password='test')
if user is not None:
    if user.is_activate:
        print('用户验证并登录成功')
        login(request, user)     # 这才是登录,才会写入session
    else:
        print('密码正确,但是用户无法登录')
    return HttpResponse('居然有这个用户')
else:
    return HttpResponse('用户不存在')

若登录成功,则会返回一个user对象,否则返回None,但是Django默认的认证系统只能认证username和password,却不能认证其它的字段,比如
email字段,看似非常糟糕,但是Django却提供了十分方便的功能来扩展默认的验证组件。查看文档:Customizing authentication
in Django

如果要验证用户名或email可以这样做:首先,在myapp目录下新建一个文件,姑且取名叫backends.py,其内容如下:

from django.conf import settings
from django.contrib.auth.models import User

class EmailOrUsernameModelBackend(object):
    def authenticate(self, username=None, password=None):
        if '@' in username:
            kwargs = {'email': username}
        else:
            kwargs = {'username': username}
        try:
            user = User.objects.get(**kwargs)
            if user.check_password(password):
                return user
            except User.DoesNotExist:
                return None

def get_user(self, user_id):
    try:
        return User.objects.get(pk=user_id)
    except User.DoesNotExist:
        return None</pre>

然后在settings.py中添加如下代码:

# 该字段指定了默认的验证后台,从上到下顺序验证,如果上面验证不成功就验证下面的
AUTHENTICATION_BACKENDS = (
    'myapp.backends.EmailOrUsernameModelBackend',   # 自定义的认证后台
    'django.contrib.auth.backends.ModelBackend',    # 这是默认的认证后台
)

这样,就可以依然使用刚才的代码对用户登录进行验证了。

退出登录

from django.contrib.auth import logout
def logout_view(request):
    logout(request)

限制登录用户访问路由

某些路由只能登录用户才能访问,那么只需要添加这个装饰器:

from django.contrib.auth.decorators import login_required
@login_required
def my_view(request):
    ......

未登录的用户将会重定向到settings.LOGIN_URL

Channels

用于与websockets通信

signal

参考django中signal与操作系统的signal是完全不一样的.Django的signal是一种同步的消息队列.通常在以下情况进行使用:

  • signal的receiver需要同时修改对多个model时
  • 将多个app的相同signal引到同一receiver中处理时
  • 在某一model保存之后将cache清除时
  • 无法使用其他方法, 但需要一个被调函数来处理某些问题时
  • 作为网站的通知

django-crontab插件

Django下的定时任务插件,依赖于Linux的cron服务。

安装

pip install django-crontab进行安装,然后在django的配置文件中添加一个APP

INSTALLED_APPS = (
    'django_crontab',
    ...
)

使用

编写完定时任务逻辑以后,需要在配置文件中添加上,例如

CRONJOBS = [
    ('*/5 * * * *', 'myapp.cron.my_scheduled_job')
    ('*/5 * * * *', 'myapp.cron.other_scheduled_job', ['arg1', 'arg2'], {'verbose': 0}),
    ('0 4 * * *', 'django.core.management.call_command', ['clearsessions']),
]

最后,将其添加到系统cron服务中去

python manage.py crontab add    # 将当前配置文件中的定时任务添加到cron中去,当add以后,会在crontab -e里面出现类似这样的一条记录
# */5 * * * * /usr/local/bin/python /usr/src/app/manage.py crontab run e0418752956c4dd997212171486888ff # django-cronjobs for admin
# 前面是命令部分,后面是根据定义的函数计算出来的hash值,最后面则是自带的注释
python manage.py crontab show   # 列出当前已经添加到cron中的定时任务
python manage.py crontab remove # 移除所有的定时任务
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容