Django,发音为[`dʒæŋɡəʊ],是用python语言写的开源web开发框架,并遵循MVC设计。劳伦斯出版集团为了开发以新闻内容为主的网站,而开发出来了这个框架,于2005年7月在BSD许可证下发布。这个名称来源于比利时的爵士音乐家DjangoReinhardt,他是一个吉普赛人,主要以演奏吉它为主,还演奏过小提琴等。由于Django在近年来的迅速发展,应用越来越广泛,被著名IT开发杂志SDTimes评选为2013SDTimes100,位列"API、库和框架"分类第6位,被认为是该领域的佼佼者。
       Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用,多个组件可以很方便的以"插件"形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNotRepeatYourself)原则。


安装 Python3.6

yum install python36


安装虚拟环境

pip3 install virtualenv
pip3 install virtualenvwrapper


修改/root/.bashrc  设置虚拟环境,添加如下内容,注意目录可以自定义设置,可执行文件根据自己安装目录填写,默认如下:

export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3.6
source /usr/local/bin/virtualenvwrapper.sh


使用如下命令使配置文件生效。

source /root/.bashrc

使用如下命令创建虚拟环境,其中mycode是虚拟环境的名子

mkvirtualenv -p python3.6 mycode

提示如下:

[root@localhost]# mkvirtualenv -p python3.6 mycode
created virtual 
environment in 399ms CPython3Posix(dest=/root/.virtualenvs/mycode, 
clear=False, global=False) with seeder FromAppData pip=latest 
setuptools=latest wheel=latest 
app_data_dir=/root/.local/share/virtualenv/seed-v1 via=copy
virtualenvwrapper.user_scripts creating /root/.virtualenvs/mycode/bin/predeactivate
virtualenvwrapper.user_scripts creating /root/.virtualenvs/mycode/bin/postdeactivate
virtualenvwrapper.user_scripts creating /root/.virtualenvs/mycode/bin/preactivate
virtualenvwrapper.user_scripts creating /root/.virtualenvs/mycode/bin/postactivate
virtualenvwrapper.user_scripts creating /root/.virtualenvs/mycode/bin/get_env_details


如果创建的有问题,可以使用如下命令删除创建的虚拟环境

rmvirtualenv mycode


使用如下进入虚拟环境

workon mycode


使用如下命令退出虚拟环境

deactivate


进入虚拟环境后 命令提示符如下:

(mycode) [root@localhost ~]#


然后再虚拟环境中查看已安装的包,提示如下

(mycode) [root@localhost ~]# pip list
Package    Version
---------- -------
pip        20.0.2 
setuptools 45.2.0 
wheel      0.34.2

在虚拟环境下安装django,这里使用3.0.3版本

pip install Django==3.0.3


用如下命令查看Django是否安装成功

pip freeze


使用如下命令安装pymysql

pip install pymysql


安装成功后用如下命令创建项目,其中new是项目名称。

django-admin startproject new


项目创建在当前目录,如果有改变目录,可以进入该目录后再执行创建命令,本次创建在home目录。


进入创建后的目录 /home/new
其中子目录new与项目同名的目录,此处为也是new 如下文件:

manage.py是项目管理文件,通过它管理项目。
_init_.py是一个空文件,作用是这个目录mycode可以被当作包使用。
settings.py是项目的整体配置文件。
urls.py是项目的url配置文件。
wsgi.py是项目与WSGI兼容的Web服务器入口。

退到manage.py文件所在的目录创建应用,使用如下命令创建应用

python manage.py startapp app01


如果报错SQLite版本过低请升级,安装过程如下:

wget https://www.sqlite.org/2020/sqlite-autoconf-3310100.tar.gz

# 编译

tar xvzf sqlite-autoconf-3310100.tar.gz 
cd sqlite-autoconf-3310100/
./configure --prefix=/usr/local
make && make install


# 替换系统低版本 sqlite3

mv /usr/bin/sqlite3  /usr/bin/sqlite3_old
ln -s /usr/local/bin/sqlite3   /usr/bin/sqlite3
echo "/usr/local/lib" > /etc/ld.so.conf.d/sqlite3.conf
ldconfig
sqlite3 -version


项目创建完成后进入app01目录,文件如下:
_init.py_是一个空文件,表示当前目录可以当作一个python包使用。
tests.py文件用于开发测试用例。
models.py文件跟数据库操作相关。
views.py文件跟接收浏览器请求,进行处理和返回页面相关。
admin.py文件跟网站的后台管理相关。
migrationsy用来装迁移文件


应用创建完成后,设置路由、配置settings.py文件。

在settings.py文件配置数据库,如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mycode',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'localhost',
        'PORT': '3306'

    }
}


在项目new文件下的__init__.py文件里面增加如下两句

import pymysql
pymysql.install_as_MySQLdb()


进入数据库创建mycode数据库

create database mycode charset=utf8;


生成迁移文件

python manage.py makemigrations


迁移文件

python manage.py migrate


如果提示
 (mysql.W002) MySQL Strict Mode is not set for database connection 'default'

在settings.py文件数据库配置部分,加入OPTIONS这部分内容

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mycode',
        'USER': 'root',
        'PASSWORD': '123456',
        'HOST': 'localhost',
        'PORT': '3306'
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO'",
            'charset': 'utf8mb4',
            }
    }
}



在settings.py文件配置语言和时区

LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = True


在settings.py文件设置调试运行所有访问.

ALLOWED_HOSTS = [‘*’]


在settings.py文件配置注册应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01',
]



设置管理后台,使用如下命令创建管理员

python manage.py createsuperuser


然后输入用户名、邮箱、密码即可。


之后使用如下命令运行服务器

python manage.py runserver

然后访问:
http://127.0.0.1:8000/admin/
用刚才创建的用户名登录,django完成安装

创建模型:

BooleanField:布尔字段,值为True或False。
NullBooleanField:支持Null、True、False三种值。
CharField(max_length=字符长度):字符串。类似mysql的varchar字段
    参数max_length表示最大字符个数。
    参数null = False 表示不能为空
    参数unique = True 表示值是唯一的。
    参数blank  = False 设置为True时,字段可以为空。设置为False时,字段是必须填写的。字符型字段CharField和TextField是用空字符串来存储空值的。
extField:大文本字段,一般超过4000个字符时使用。
IntegerField:整数。
DecimalField(max_digits=None, decimal_places=None):十进制浮点数。
    参数max_digits表示总位数。
    参数decimal_places表示小数位数。
FloatField:浮点数。
DateField[auto_now=False, auto_now_add=False]):日期。
    参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false。
    参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false。
    参数auto_now_add和auto_now是相互排斥的,组合将会发生错误。
TimeField:时间,参数同DateField。
DateTimeField:日期时间,参数同DateField。
FileField:上传文件字段。
ManyToManyField  多对多关联
ForeignKey  一对多关联

模型

在APP目录的models.py文件里面创建模型
创建之前线安装一个django的复选框插件,如下命令

pip install django-multiselectfield


然后再models.py文件里面引入。

from multiselectfield import MultiSelectField



如下例子:

from django.db import models
from multiselectfield import MultiSelectField

# Create your models here.
#基础信息类
class BaseInfo(models.Model):
    created_time = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    updated_time = models.DateTimeField(verbose_name='更新时间',auto_now=True)
    is_delete = models.BooleanField(verbose_name='显示控制',default=False)
    class Meta:
        abstract = True
    '''
    参数含义
    class Meta  作为抽象类继承使用
    verbose_name 字段名称,
    auto_now_add 自动增加日期和时间
    auto_now无论是你添加还是修改对象,时间为你添加或者修改的时间。
    auto_now_add为添加时的时间,更新对象时不会有变动。
    '''

#行程信息
class TripInfo(BaseInfo):
    name = models.CharField(verbose_name='行程描述',max_length=50,null=False)
    class Meta:
        verbose_name = '行程信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return  self.name

# 症状信息
class SymptomInfo(BaseInfo):
    name = models.CharField(verbose_name='症状描述',max_length=20,null=False,unique=True)

    class Meta:
        verbose_name = '症状信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return  self.name

#人员信息类
class PersonInfo(BaseInfo):
    name = models.CharField(verbose_name='人员姓名',max_length=10,null=False)
    cid = models.CharField(verbose_name='身份证ID',max_length=18,null=False)
    age = models.IntegerField(verbose_name='年龄',null=False)
    sex_type = ((1,'男'),(0,'女'),(2,'保密'))
    sex = models.IntegerField(verbose_name='性别',choices=sex_type,null=False)
    address = models.CharField(verbose_name='现住址',max_length=50,null=False)
    temperature = models.FloatField(verbose_name=' 体温',max_length=5,null=False)
    #symptom = models.ForeignKey(to=TripInfo,on_delete=models.CASCADE,verbose_name='症状信息')
    symptom = models.ManyToManyField(to=SymptomInfo,verbose_name='症状信息')
    #trip = models.ForeignKey(to=SymptomInfo, on_delete=models.CASCADE,verbose_name='行程信息')
    trip = models.ManyToManyField(to=TripInfo, verbose_name='行程信息')


    '''
    class Meta 这里表示在后台管理里面的标题显示中文
    '''
    class Meta:
        verbose_name = '人员信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return  self.name

创建完成之后生成迁移文件和迁移文件

python manage.py makemigrations
python manage.py migrate

在后台注册管理用,再app01目录下的admin.py文件注册,如下

先导入模块

from app01.models import PersonInfo,TripInfo,SymptomInfo

admin.site.register(PersonInfo)
admin.site.register(TripInfo)
admin.site.register(SymptomInfo)




视图
views.py文件写入视图函数 并导入模块

from .models import SymptomInfo,TripInfo,PersonInfo
如下例子:

from django.shortcuts import render
# Create your views here.
from app01.models import SymptomInfo,TripInfo,PersonInfo
from django.db.models import Avg
from django.http import HttpResponse

#人员查询
def index(request):
    #查询所有未删除人员信息并以创建时间降序排列
    person = PersonInfo.objects.filter(is_delete=False).order_by('-created_time').all()
    #查询所有人员的平均温度
    avg_temp = PersonInfo.objects.aggregate(Avg('temperature'))
    #查询所有人员的平均年龄
    avg_age = PersonInfo.objects.aggregate(Avg('age'))

    return  render(request, 'app01/index.html', locals())  

#详细页面查询
def show(request,id): #注意参数过多可以直接使用  def show(request,*args,**kwargs)
    details = PersonInfo.objects.filter(id=id).all()  #可以加.[:10],只查询前10个人员信息
    if details:
        return render(request, 'app01/show.html', locals())
    else:
        return HttpResponse('没有查到数据')

#查询此症状对应的所有人
def zhengzhuang(request,id):
    zzs = PersonInfo.objects.filter(symptom=id).order_by('-created_time').all()
    if zzs:
        return render(request, 'app01/zhengzhuang.html', locals())
    else:
        return HttpResponse('没有查到数据')

#查询此行程的所有人员
def xingcheng(request,id):
    xcs = PersonInfo.objects.filter(trip=id).order_by('-created_time').all()
    if xcs:
        return render(request, 'app01/xingcheng.html', locals())
    else:
        return HttpResponse('没有查询到数据')

 # def creates(request):
 #     tests = PersonInfo.objects.create(name = '老魏') #创建人员信息
 #     delete = PersonInfo.objects.filter(id=3).delete() #删除人员信息

模板

注意模板文件在项目目录templates文加下创建,

例如第一个应用 ,名称是app01,目录就是 templates/app01

然后如上视图中的模板文件 xingcheng.html, zhengzhuang.html,index.html 都放在如上 templates/app01/目录下。


模板如下例子,以index.html为例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home</title>
    <style>
        table {text-align: center;border:1px solid royalblue;}
        td {border:solid royalblue;}
        p { font-size: 20px;color: red;}
    </style>
</head>
<body>
<h1>Django测试页面</h1>
<table>
    <tr>
        <td>姓名</td>
        <td>身份证ID</td>
        <td>现在住址</td>
        <td>年龄</td>
        <td>性别</td>
        <td>体温</td>
        <td>症状信息</td>
        <td>行程信息</td>
        <td>建立时间</td>
        <td>详情</td>
    </tr>
    {% for p in person %} {#此处的person是在视图文件views.py里面的对象,name,cid都是这个对象的属性。#}
        {% if not p.is_delete %}
        <tr>
            <td>{{ p.name }}</td>
            <td>{{ p.cid }}</td>
            <td>{{ p.address }}</td>
            <td>{{ p.age }}</td>
            <td>{{ p.get_sex_display }}</td>
            <td>{{ p.temperature }}</td>

            <td>
            {% for i in p.symptom.all %}
                {% if  not forloop.last %}
                    <a href="{% url 'app01:zhengzhuang' i.id %}">{{ i.name }}</a>  {#注意这里的{% url 'app01:zhengzhuang' i.id %}是反向解析的写法,也可以写成 href="/app01/zhengzhuang/{{i.id}}" #}
                {% else %}
                    <a href="{% url 'app01:zhengzhuang' i.id %}">{{ i.name }}</a>
                {% endif %}
            {% endfor %}
            </td>

            <td>
            {% for i in p.trip.all %}
                {% if  not forloop.last %}
                    <a href="{% url 'app01:xingcheng' i.id %}">{{ i.name }}</a>,
                {% else %}
                    <a href="{% url 'app01:xingcheng' i.id %}">{{ i.name }}</a>
                {% endif %}
            {% endfor %}
            </td>

            <td>{{ p.created_time }}</td>
              {#URL采用别名进行反向解析#}
              <td><a href="{% url 'app01:show' p.id %}">{{ p.id }}</a></td>
        </tr>
        {% endif %}
    {% endfor %}
</table>
<p>共有{{ person.count }}个人员信息.</p>
<p>人员平均温度:{{ avg_temp.temperature__avg | floatformat:2}}°</p>
<p>人员平均年龄:{{ avg_age.age__avg | floatformat:2}}岁</p>

</body>
</html>

urls.py里面导入视图

from app01 import views

写入路由

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.index)  #新增路由,此处可以增加别名,写成path('', views.index,name='index'),然后就可以模板的连接中写反向解析
]


上传图片

#安装必要的包

pip install Pillow


配置 settings.py 放入如下内容

MEDIA_ROOT = os.path.join(BASE_DIR, 'media').replace("\\", "/")
MEDIA_URL = '/media/'

配置 settings.py文件  模板加入media

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',
                'django.template.context_processors.media'  #加入此行
            ],
        },
    },
]




在项目根目录文件urls.py加入如下内容

from django.conf import settings  #导入包
from django.conf.urls.static import static #导入包

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include(('app01.urls', 'app01'), namespace='app01')),
    path('app02/', include(('app02.urls', 'app02'), namespace='app02')),

] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) #这里是新加


上传图片的简单模型如下,

from django.db import models
from app02.storage import ImageStorage
# Create your models here.

#基础类
class BaseInfo(models.Model):
    created_time = models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    updated_time = models.DateTimeField(verbose_name='更新时间',auto_now=True)
    is_delete = models.BooleanField(verbose_name='显示控制',default=False)
    class Meta:
        abstract = True


#用户类
class UserUpload(BaseInfo):
    name =models.CharField(verbose_name='姓名',max_length=10,null=False)
    set_type = ((1,'男'),(0,'女'),(2,'保密'))
    set = models.IntegerField(verbose_name='性别',choices=set_type,null=False)
    image = models.ImageField(verbose_name='头像',upload_to='app02/%Y/%m/%d',storage=ImageStorage())   
    #注意  ImageStorage是一个图片更名的函数,因为上面我们有定义media 因此upload_to='app02/%Y/%m/%d'的意思是上传到哪里,这里是指上传到media/app02/年份/月份/天,这个目录

    class Meta:
        verbose_name = '人员信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name

图片更名函数,可以放在应用的根目录,和视图模型文件同一个目录,文件名例如 storage.py

内容如下:

rom django.core.files.storage import FileSystemStorage
from django.http import HttpResponse


class ImageStorage(FileSystemStorage):
    from django.conf import settings

    def __init__(self, location=settings.MEDIA_ROOT, base_url=settings.MEDIA_URL):
        # 初始化
        super(ImageStorage, self).__init__(location, base_url)

    # 重写 _save方法
    def _save(self, name, content):
        # name为上传文件名称
        import os, time, random
        # 文件扩展名
        ext = os.path.splitext(name)[1]
        # 文件目录
        d = os.path.dirname(name)
        # 定义文件名,年月日时分秒随机数
        fn = time.strftime('%Y%m%d%H%M%S')
        fn = fn + '_%d' % random.randint(0, 100)
        # 重写合成文件名
        name = os.path.join(d, fn + ext)
        # 调用父类方法
        return super(ImageStorage, self)._save(name, content)

上传图片的简单视图如下:

from django.shortcuts import render
from app02.models import UserUpload
from django.http import HttpResponse
from django.conf import settings
from datetime import  datetime
import os,uuid
from app02.models import UserUpload
from django.shortcuts import redirect
# Create your views here.

def index(request):
    pics = UserUpload.objects.all()
    return  render(request,'app02/index.html',locals())
    # return  HttpResponse('OK')

def upload(request):
    if request.method == 'GET':
        return  render(request,'app02/upload.html')
    else:
        name = request.POST.get('username')
        sex = request.POST.get('sex')
        pics = request.FILES.get('mypic')
        media_root = settings.MEDIA_ROOT
        pics_type = settings.UPLOAD_TYPE
        dates = datetime.now()
        path = 'app02/{}/{}/{}/' .format(dates.strftime('%Y'),dates.strftime('%m'),dates.strftime('%d'))
        if pics.name.split('.')[-1] in pics_type:
            full_path = media_root + '/' + path
            if not os.path.exists(full_path):
                os.makedirs(full_path)
            #更换图片名称
            pics.name = str(uuid.uuid1()) + '.' + str(pics.name.split('.')[-1]) #注意这里使用了UUID更改了上传图片名称.
            #保存图片
            with open(full_path + pics.name,'wb') as pic:
                for c in pics.chunks(): #图片切片
                    pic.write(c)
            # 写入数据库
            UserUpload.objects.create(name=name,set=sex,image=path + pics.name)
            # 返回首页
            return redirect('app02:index')
        else:
            return HttpResponse('非法图片')   
            #注意这里,如果图片格式不对,则会返回这个提示,图片格式在settings.py定义  UPLOAD_TYPE = ['JPG','jpg','jpeg','JPEG','PNG','png','gif','GIF']


上传图片的简单模板如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传图片</title>
</head>
<body>
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}  
    姓名:<input type="text" name="username" maxlength="10"><br>
    性别: 男 <input type="radio" name="sex" value="1"> 女 <input type="radio" name="sex" value="0"> 保密 <input type="radio"  name="sex" value="2"><br>
    头像: <input type="file" name="mypic" ><br>
    <input type="submit" value="提交信息">
</form>
</body>
</html>


静态配置:

settings.py文件的STATIC_URL = '/static/' 末尾配置如下内容
STATICFILES_DIRS = [ os.path.join(BASE_DIR),'static']

其中 STATIC_URL = '/static/'  用于伪装图片地址, static可随意修改

然后在项目根目录新建 static文件夹,这个文件夹下放置一些静态文件,例如css、js、images
然后项目中的css、js、图片文件可从此目录引入
例如:
<script src="/static/js/aaa.js"></script>
<link rel="stylesheet" href="/static/css/head.css" type="text/css">

在模板文件里面引入静态反向解析

{% load static from staticfiles %}  或者 {% load staticfiles %}

引入后图片地址可进行如下引用

<img src="{% static 'images/pic.jpng'%}" alt="">
其中CSS JS文件也可以这样导入

<script src="{% static 'js/aaa.js' %}"></script>
<link rel='stylesheet' href="{% static 'css/head.css' %}">

注意: 如果项目文件settings.py文件里面的DEBUG 设置为 False 则静态资源会失效,这个在项目部署后可以用NGINX解决。


模板语言

for循环
{% for i in abc %}
   表达式
{% empyt %}  如果是空值输出XX,用于循环内部
{% endfor %}


if语句
{% if a == b %}
    表达式
{else}
    表达式
{% endif %}

变量
{{变量名}}

常见方法
forloop 包括 first last counter0  counter1 等



常用过滤器

{{ abc | default:'哈哈'}}   #如果有ABC有值,则返回ABC的值,如果没有,则返回哈哈
{{ abc.time | date:'Y-m-d'}}  格式化日期和时间
{{ abc | length}} 计算字符串长度
{{ abc | truncatechars:2}}  截取字符
{{ abc | divisibleby:2}}  能被2整除  用于判断

自定义过滤器


注释{#     #}  单行注释

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


模板继承:

相同内容用于继承,一般用于网站的头部和底部

父模板HTML
例如
titie标签
<title>{% block title %}默认标题{% endblock%}</title>

中间内容
{% block content%}
内容部分
{% endblock%%}


css或者java
{% block style %}
<style></style>
{% endblock %}


子模板继承父模板HTML
{% extends 'app01/base.html' %}

{% block title %}子标题{% endblock %}
{% block style %}<style></style>{% endblock %}

{% block comment %}
子内容
{% endblock %}

子类调用父类内容
{{block.super}}


转义
浏览器默认转义

如果要非转义则使用过滤器
{{ content | safe }}

或者非转义标签
{% autoescape off %}
{{ content}}
{% endautoescape %}

反向解析
url反向
在urls.py 加上别名
其中根urls.py的如下
    path('app02/', include(('app02.urls', 'app02'), namespace='app02')),
    其中 namespace='app02'  这个就是根别名
    
APP的urls.py如下
     path('show/', views.show,name='show')
     其中 name='show'  是应用别名
    
之后重定向或者超链接可以如下写
href = "{% url 'app02:show'%}"
反向的好处是应用目录变更,解析仍然有效。

关键字带参数
href = "{% url 'app02:show' id=1234 %}"

位置参数:
re_path(r'^app01/(\d+)/',views.index,name=index)
href = "{% url 'app02:show' 1234 %}"



分页功能

安装插件
pip install django-pure-pagination

settings.py文件里面注册应用
INSTALLED_APPS = (
    'pure-pagination',
)


settings.py文件配置
PAGINATION_SETTINGS = {
    'PAGE_RANGE_DISPLAYED': 1,  #间距
    'MARGIN_PAGES_DISPLAYED': 2, #间距
    'SHOW_FIRST_PAGE_WHEN_INVALID': True,  #无效页码返回第一页
}


编写视图
from pure-pagination import PageNotAnIntege,Paginator


def index(request):
    try:
        page = request.GET.get('page', 1)
    except PageNotAnInteger:
        page = 1
    arts = Article.objects.all()
    p = Paginator(objects_list = arts, per_page=1, request=request)
    articles = p.page(page)
    return render(request, 'index.html', locals())
    
分页模板

<ul>
    {% for art in articles.object_list %}
        <li>{{ art.title }}</li>
    {% endfor %}
</ul>

{% include '_pagination.html' %}

分页模板源文件
{% load i18n %}
<div class="pagination">
    {% if articles.has_previous %}
        <a href="?{{ articles.previous_page_number.querystring }}"
           class="prev">上一页</a>
    {% else %}
        <span class="disabled prev">上一页 </span>
    {% endif %}
    {% for page in articles.pages %}
        {% if page %}
            {% ifequal page articles.number %}
                <span class="current page">{{ page }}</span>
            {% else %}
                <a href="?{{ page.querystring }}" class="page">{{ page }}</a>
            {% endifequal %}
        {% else %}
            ...
        {% endif %}
    {% endfor %}
    {% if page_obj.has_next %}
        <a href="?{{ articles.next_page_number.querystring }}" class="next">下一页</a>
    {% else %}
        <span class="disabled next">下一页</span>
    {% endif %}
</div>


文本框插件
DjangoUeditor



邮件发送

settings.py文件加入如下配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
# 发送邮件的邮箱
EMAIL_HOST_USER = '18622881126@163.com'
# 在邮箱中设置的客户端授权密码
EMAIL_HOST_PASSWORD = 'abc1234567'
# 设置是否启用安全链接
EMAIL_USER_TLS = True




视图导入邮件相关模块
from django.conf import settings
from django.core.mail import send_mail, send_mass_mail, EmailMultiAlternatives
from django.http import HttpResponse

def send(request):

    res = send_mail('元旦放假已通知',
                    '元旦放假一天',
                    '18622881126@163.com',
                    ['496155678@qq.com'],
                    fail_silently=False)
    # 值1:邮件标题   
    # 值2:邮件内容
    # 值3:发件人  
    # 值4:收件人  
    # 值5:如果失败,是否抛出错误
    if res == 1:
        return HttpResponse('邮件发送成功')
    else:
        return HttpResponse('邮件发送失败')


def send(request):
    message1 = ('元旦放假已通知',
                '元旦放假一天',
                '18622881126@163.com',
                ['496155678@qq.com', '18622881126@qq.com'])

    message2 = ('元旦放假已通知?',
                '元旦放假一天',
                '18622881126@163.com',
                ['496155678@qq.com', '496155678@qq.com'])
    res = send_mass_mail((message1, message2))
    if res == 2:
        return HttpResponse('多封邮件发送成功')
    else:
        return HttpResponse('多封邮件发送失败')
        
        
form表单

应用目录新建一个forms.py文件,

from django import forms
from django.core.exceptions import ValidationError
import re

一个简单的form 表单模型、视图、模板如下:

from django import forms
from django.core.exceptions import ValidationError
from captcha.fields import CaptchaField
from app03.models import UserRegister
from django.contrib.auth import authenticate
import re

#手机号码验证函数
def check_phone(phone):
    phone_nums = re.match(r'^1[3-9]\d{9}$',phone)
    if not phone_nums:
        raise ValidationError('手机号码格式不正确,请重新输入')
#验证用户名
def check_username(username):
    username = re.match(r'^[a-zA-Z]+\d*$',username,re.I)
    if not username:
        raise ValidationError('用户名不能以数字开头,请重新输入')
#验证邮箱
def check_email(email):
    mails = re.match('^[0-9a-zA-Z]+@[0-9a-zA-Z]+\.[a-zA-Z]{2,4}',email,re.I)
    if not mails:
        raise ValidationError('邮箱地址格式不正确,请重新输入')

#验证密码复杂度:
def check_password(password):
    pwd1 = re.search('[a-zA-Z]+',password)
    pwd2 = re.search('[0-9]+',password)
    if not pwd1 or not pwd2:
        raise ValidationError('密码必须是数字和字母的结合')

#注册表单类
class RegisterForm(forms.Form):
    username = forms.CharField(label='用户名',required=True,min_length=6,max_length=16,
                           error_messages={'required':'用户名必须填写','min_length':'用户名最短6位','max_length':'用户名最长16位'},
                                 validators=[check_username,],widget=forms.TextInput({'placeholder':'请输入用户名'}))
    phone = forms.CharField(label='手机号码',required=True,error_messages={'required':'手机号码必须填写'},
                            validators=[check_phone,],widget=forms.TextInput({'placeholder':'请输入手机号码'}))
    email = forms.EmailField(label='邮箱',required=True,error_messages={'required':'邮箱必须填写'},
                             widget=forms.TextInput({'placeholder':'请输入邮箱地址'}))
    last_name = forms.CharField(label='真实姓名',required=True,max_length=8,
                           error_messages={'required':'真实姓名必须填写','max_length':'真实姓名不超过8个字符'},
                                widget=forms.TextInput({'placeholder':'请输入真实姓名'}))
    password = forms.CharField(label='密码',min_length=8,max_length=16,required=True,validators=[check_password,],
                               error_messages={'required':'密码必须填写','min_length':'密码最短6位','max_length':'密码最长16位'},
                               widget=forms.PasswordInput(attrs={'placeholder':'请输入密码'}),)
    re_password = forms.CharField(label='密码',min_length=8,max_length=16,required=True,validators=[check_password,],
                               error_messages={'required':'密码必须填写','min_length':'密码最短6位','max_length':'密码最长16位'},
                                widget=forms.PasswordInput(attrs={'placeholder':'请再次确认密码'}),)
    code = CaptchaField(label='验证码',required=True,error_messages={'required':'请填写验证码','invalid':'验证码错误'},)

    #验证用户名是否重复
    def clean_username(self):
        username = self.cleaned_data.get('username')
        is_user = UserRegister.objects.filter(username=username).count()
        if is_user:
            raise ValidationError('用户名已存在,请重新选择一个')
        return username

    #验证手机号码是否重复
    def clean_phone(self):
        phone = self.cleaned_data.get('phone')
        is_phone = UserRegister.objects.filter(phone=phone).count()
        if is_phone:
            raise ValidationError('手机号已存在,请重新填写')
        return phone

    #验证两次输入的密码是否一致
    def clean(self):
        clean_data = self.cleaned_data
        pwd1 = clean_data.get('password')
        pwd2 = clean_data.get('re_password')
        if pwd1 != pwd2:
            raise ValidationError('两次输入的密码不一致')
        return clean_data

表单视图如下:

class RegisterView(View):
    def get(self, request, *args,**kwargs):
        reg = RegisterForm()
        return render(request, 'app03/register.html', locals())

    def post(self,request,*args,**kwargs):
        reg = RegisterForm(request.POST)
        if reg.is_valid():
            ##将数据写入数据库
            phone = reg.cleaned_data.get('phone')
            last_name = reg.cleaned_data.get('last_name')
            username = reg.cleaned_data.get('username')
            emails = reg.cleaned_data.get('email')
            password = reg.cleaned_data.get('password')
            UserRegister.objects.create_user(
                username=username,
                phone=phone,
                email=emails,
                password=password,
                last_name = last_name,
            )
            messages.success(request,'注册成功')
            return redirect(reverse('app03:index'))

        else:
            return render(request, 'app03/register.html', locals())

表单模板如下:

<form method="post" action="{% url 'app03:register' %}">
         {% csrf_token %}
<div class="register_con password_con">
   <div class="text_box">
      <div>
         <div class="inputs"><i><img src="{% static '/app03/images/icon01.png' %}"></i>{{ reg.username }} <span>*
                         {% for err in reg.errors.username %}
                             {{ err}}</span>
                         {% endfor %}
         </div>
         <div class="inputs"><i><img src="{% static '/app03/images/icon04.png' %}"></i>{{ reg.email }} <span>*</span>
                         {% for err in reg.errors.email %}
                             <span>{{ err}}</span>
                         {% endfor %}
         </div>
                     <div class="inputs"><i><img src="{% static '/app03/images/icon04.png' %}"></i>{{ reg.last_name }} <span>*</span>
                         {% for err in reg.errors.last_name %}
                             <span>{{ err}}</span>
                         {% endfor %}
         </div>
                     <div class="inputs"><i><img src="{% static '/app03/images/icon04.png' %}"></i>{{ reg.phone }} <span>*</span>
                         {% for err in reg.errors.phone %}
                             <span>{{ err}}</span>
                         {% endfor %}
         </div>
         <div class="inputs"><i><img src="{% static '/app03/images/icon02.png' %}"></i>{{ reg.password }} <span>*</span>
                         {% for err in reg.errors.pasword %}
                             <span>{{ err}}</span>
                         {% endfor %}
         </div>
         <div class="inputs"><i><img src="{% static '/app03/images/icon02.png' %}"></i>{{ reg.re_password }} <span>*</span>
                         {% for err in reg.errors.re_password %}
                             <span>{{ err}}</span>
                         {% endfor %}
                         {% for errs in reg.non_field_errors %}
                             <span>{{ errs }}</span>
                         {% endfor %}
         </div>
         <div class="yzm inputs" ><i><img src="{% static '/app03/images/icon03.png' %}"></i>{{ reg.code }} <span>*</span>
                         {% for err in reg.errors.code %}
                             <span>{{ err}}</span>
                         {% endfor %}
         </div>
         <button>注册账号</button>
      </div>
   </div>
</div>
 </form>


验证码:
pip install django-simple-captcha
注册应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app01'
    'app02',
    'captcha',
]


部署
uwsgi + nginx


安装nginx和uwsgi

pip install uwsgi 
yum install nginx,python3-devel

修改settings.py文件的DEBUG为False

注意项目目录是news, news目录下是app

uwsgi.ini 文件配置如下

[uwsgi]
socket = /usr/share/nginx/html/cacti/news.sock
chdir = /usr/share/nginx/html/cacti
wsgi-file = news/wsgi.py
touch-reload = /usr/share/nginx/html/cacti/reload
module = news.wsgi
master = true
buffer-size = 65536
processes = 8
threads = 4
vacuum =  true
chown-socket = nginx
pidfile = uwsgi.pid
daemonize = uwsgi.log
virtualenv = /root/.virtualenvs/mycode

启动 uwsgi --ini uwsgi.ini

nginx配置文件如下:

server {
    listen      9988;
    server_name 0.0.0.0;
    charset     utf-8;
 
    client_max_body_size 75M;
 
        location /static {
        alias /usr/share/nginx/html/cacti/static;
        }

        location / {
        uwsgi_pass unix://usr/share/nginx/html/cacti/news.sock;
        include         /etc/nginx/uwsgi_params;

        }
}