配置
安装
pip install django
创建项目
django-admin startproject [项目名]
- 示例, 创建名为test_django项目
 

(1) init: 一个空文件, 声明所在目录包为一个 python 包
(2) settings: 管理项目配置信息
(3) urls: 声明请求 url 映射关系
(4) wsgi: python 程序和 web 服务器通信协议
(5) manage: 一个命令行工具, 用来和 Django 项目进行交互
创建应用
- 示例, 创建名为app应用
 
python manage.py startapp app

应用目录结构
(1) admin: app 应用后台管理配置文件
(2) apps: app 应用配置文件
(3) models: 数据模块, 用于设计数据库等
(4) tests: 编写测试脚本
(5) views: 视图层, 直接和浏览器进行交互
应用注册
新建应用需在项目settings.py里INSTALLED_APPS 列表中注册, 使程序找到该服务

setting.py默认内容
import os
# 项目相对路径, 启动服务时候会运行这个文件所在路径的manage.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 安全密钥
SECRET_KEY = 'oyj48d_pm2jhr2!4e9cmv+&h5j^4y6im_a7t)pqs87q0!5p4+4'
# 是否开启Debug
DEBUG = True
# 允许访问主机ip, 可以用通配符*
ALLOWED_HOSTS = []
# Application definition
# 用来注册App 前6个是django自带应用
INSTALLED_APPS = [
    'django.contrib.admin', 
    'django.contrib.auth', 
    'django.contrib.contenttypes', 
    'django.contrib.sessions', 
    'django.contrib.messages', 
    'django.contrib.staticfiles', 
]
# 中间件, 需要加载的中间件.比如在请求前和响应后根据规则去执行某些代码的方法
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配置
ROOT_URLCONF = 'demo.urls'
# 加载网页模板路径
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates', 
        'DIRS': [], 
        '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', 
            ], 
        }, 
    }, 
]
# WSGI的配置文件路径
WSGI_APPLICATION = 'demo.wsgi.application'
# 数据库配置 默认的数据库为sqlite
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', 
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 
    }
}
# 相关密码验证
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 
    }, 
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 
    }, 
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 
    }, 
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 
    }, 
]
# 语言设置 默认英语, 中文是zh-hans
LANGUAGE_CODE = 'en-us'
# 时区设置, 中国的是:Asia/Shanghai
TIME_ZONE = 'UTC'
# i18n字符集是否支持
USE_I18N = True
USE_L10N = True
# 是否使用timezone
# 保证存储到数据库中的是 UTC 时间;
# 在函数之间传递时间参数时, 确保时间已经转换成 UTC 时间;
USE_TZ = True
# 静态文件路径
STATIC_URL = '/static/'
视图
编辑app/view.py
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
def hello(request):
    return HttpResponse('Hello World')
路由
编辑test_django/urls.py
from django.contrib import admin
from django.urls import path
import app.views
urlpatterns = [
    path('admin/', admin.site.urls), 
    # 将hello()函数绑定路由
    path('hello/', app.views.hello)
]
测试
python manage.py runserver 8080
访问 http://127.0.0.1:8000/hello
数据库
迁移
# 终端执行, 为改动创建迁移记录
python manage.py makemigrations
# 将操作同步到数据库
python manage.py migrate
- 同步到多个数据库
 
python manage.py migrate --database=路由表中应用对应数据库
创建管理员用户
python manage.py createsuperuser
部署
uwsgi部署
建立项目mysite
django-admin.py startproject mysite
python manage.py runserver 0.0.0.0:8000
- uwsgi
 
[uwsgi]
socket = 127.0.0.1:8000
# 启动主进程, 来管理其他进程
master = true
# 开启进程数量
processes = 2
# 运行线程
threads = 8
# 设置用于uwsgi包解析内部缓存区大小为64k.默认是4k
buffer-size = 32768
- nginx
 
server {
    listen 80;
    server_name localhost;
    location / {
        include  uwsgi_params;
        
        # 必须和 uwsgi中设置一致
+       uwsgi_pass  127.0.0.1:8000;
        
        # 入口文件, 即wsgi.py相对于项目根目录位置, "."相当于一层目录
+       uwsgi_param UWSGI_SCRIPT /mysite/mysite.wsgi;
        
        # 项目根目录
+       uwsgi_param UWSGI_CHDIR /mysite;
        index  index.html index.htm;
        client_max_body_size 35m;
    }
}
问题
跨域
pip install django-cors-headers
- 修改settings.py文件
 
INSTALLED_APPS = [
    ...
    # 增加
+   'corsheaders',
    ...
]
...
MIDDLEWARE = [
    ...
    # 增加
+   'corsheaders.middleware.CorsMiddleware',
+   'django.middleware.common.CommonMiddleware',
    ...
]
数据库操作
MySQL
建立应用 app
python3 manage.py startapp app

主目录下 settings.py文件中DATABASES为默认配置, 会建立 sqlite3 数据库

若使用其他数据库需进行修改, 以MySQL为例,
DATABASES = {
    'default': {
        # 数据库引擎
        'ENGINE': 'django.db.backends.mysql', 
        'NAME': 数据库名, 
        'USER': 用户名, 
        'PASSWORD': 密码, 
        'HOST': IP, 
        'PORT': 端口, 
    }
}
建表
编辑 app/models.py
from django.db import models
# 学生表
class Stu(models.Model):
    choices=(('M',  '男'), ('W',  '女')), 
    num = models.CharField(primary_key=True, verbose_name='学号', help_text='请输入学号', max_length=5)
    name = models.CharField(verbose_name='姓名', help_text='请输入姓名', max_length=5)
    age = models.IntegerField(verbose_name='年龄', help_text='请输入年龄')
    sex = models.CharField(verbose_name='性别', help_text='请选择性别', default='M', max_length=1)
    # 元数据
    class Meta:
        # 按'age'字段升序排列
        ordering = ['age']
    # 显示类信息
    def __str__(self):
        sex = '男' if self.sex == 'M' else '女'
        return '学号:%s  姓名:%s  年龄:%s 性别:%s' % (self.num,  self.name,  self.age, sex)
字段
| 类型 | 说明 | 
|---|---|
| AutoField | 一个自动增加整数类型字段, django 会自动添加自增字段:id = models.AutoField(primary_key=True), 从 1 开始计数 | 
| CharField | 字符串类型.必须接收一个 max_length 参数, 表示字符串长度不能超过该值.默认表单标签是 input text | 
| TextField | 大量文本内容, 在 HTML 中表现为 Textarea 标签 | 
| IntegerField | 整数类型, 取值范围-2147483648 到 2147483647 | 
| DateField | 日期类型, python 中datetime.date()实例, 例如 2020-08-05 | 
| DateTimeField | 日期时间类型, python datetime.datetime()实例.多了小时、分和秒显示, 例如 2020-08-05 23:08 | 
| FileField | 上传文件类型 | 
| ImageField | 图像类型 | 
字段参数
| 类型 | 说明 | 
|---|---|
| default | 字段默认值, 可以是值或者一个可调用对象 | 
| help_text | 额外显示在表单部件上帮助文本 | 
| primary_key | 若为字段设置了 primary_key=True, 则当前字段变为主键; primary_key=True 隐含 null=False 和 unique=True 意思 | 
| verbose_name | 为字段设置可读, 直观别名 | 
| choices | 选择框标签, 值为一个二维二元元组;第一个元素表示数据库内真实值, 第二个表示页面上显示内容 | 
# 为改动创建迁移记录
python manage.py makemigrations
# 将操作同步到数据库
python manage.py migrate

此时 app/migrations 下生成 0001_initial.py 文件

管理
Django 自带后台管理页面, 使用前需创建管理员用户
python manage.py createsuperuser
输入用户名和密码, 邮箱地址可选

启动项目, 访问http://127.0.0.1:8000/admin/

现在还无法看到建好表, 需在 admin 中注册, 将 app 模型加入站点内, 接受站点管理
打开 app/admin.py 文件, 修改
from django.contrib import admin
from .models import Stu
# Register your models here.
# 注册Stu
admin.site.register(Stu)

新增
可视化, 直接使用 Django 管理页面新增数据

由于之前设定了 ordering = [‘age’], 故添加数据会按从小到大顺序排列
MongoDB
MongoEngine
Models
from mongoengine import *
connect('admin', host='localhost', port=27017, username='admin', password='123456')
class Score(EmbeddedDocument):
    name = StringField(required=True, max_length=200)
    score = IntField(required=True)
class User(Document):
    name = StringField(required=True, max_length=200)
    age = IntField(required=True)
    scores = ListField(EmbeddedDocumentField(Score))
    meta ={
        'collection': 'user'
    }
转JSON
user = User.objects.filter(name='Wang').first().to_json(ensure_ascii=False)
增加
User(....).create()
# 嵌套文档增加
scores = User.objects.filter(name='Wang').first().scores
scores = list(scores)
scores.append(Score(name='C++', score=90))
User.objects.filter(name='Wang').first().update(scores=scores)
修改
# 修改值 set__
User.objects.filter(name='Wang').update(set__age=27)
# 修改嵌套文档
连接多数据库
设置
建立Django项目, 在建立两个应用app1与app2
修改settings.py文件中DATABASES配置
连接sqlite, mysql
DATABASES = {
    # 默认数据库
    'default': {
        'ENGINE': 'django.db.backends.sqlite3', 
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 
    }, 
    # MySQL
    'test_mysql': {
        'ENGINE': 'django.db.backends.mysql', 
        # 数据库名
        'NAME': 'test_mysql', 
        'USER': 'root', 
        'PASSWORD': '123456', 
        # 数据库ip地址
        'HOST': '123.56.233.200', 
        # 访问端口
        'PORT': 3306, 
        #设置mysql启用严格模式
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", 
            'charset': 'utf8'
        }
    }
连接Redis
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache", 
        "LOCATION": "redis://[IP]:6379/0", 
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient", 
            "CONNECTION_POOL_KWARGS": {
                # 最大连接数
                "max_connections": 10, 
                # 以字符串形式写入Redis, 为False话写入字节类型
                "decode_responses": True
            }, 
            "PASSWORD": "[密码]"
        }
    }
}
数据库路由方法类
与settings.py同级目录下建立database_router.py文件
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DatabaseAppsRouter(object):
    """
    A router to control all database operations on models for different
    databases.
    In case an app is not set in settings.DATABASE_APPS_MAPPING, the router
    will fallback to the `default` database.
    Settings example:
    DATABASE_APPS_MAPPING = {'app1': 'db1', 'app2': 'db2'}
    """
    def db_for_read(self, model, **hints):
        """"Point all read operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None
    def db_for_write(self, model, **hints):
        """Point all write operations to the specific database."""
        if model._meta.app_label in DATABASE_MAPPING:
            return DATABASE_MAPPING[model._meta.app_label]
        return None
    def allow_relation(self, obj1, obj2, **hints):
        """Allow any relation between apps that use the same database."""
        db_obj1 = DATABASE_MAPPING.get(obj1._meta.app_label)
        db_obj2 = DATABASE_MAPPING.get(obj2._meta.app_label)
        if db_obj1 and db_obj2:
            if db_obj1 == db_obj2:
                return True
            else:
                return False
        return None
    def allow_syncdb(self, db, model):
        """Make sure that apps only appear in the related database."""
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(model._meta.app_label) == db
        elif model._meta.app_label in DATABASE_MAPPING:
            return False
        return None
    def allow_migrate(self, db, app_label, model=None, **hints):
        """
        Make sure the auth app only appears in the 'auth_db'
        database.
        """
        if db in DATABASE_MAPPING.values():
            return DATABASE_MAPPING.get(app_label) == db
        elif app_label in DATABASE_MAPPING:
            return False
        return None
增加数据路由表与规则方法
settings.py文件中添加内容
# 数据库路由规则方法
DATABASE_ROUTERS = ['项目名.database_router.DatabaseAppsRouter']
# 数据库路由表
DATABASE_APPS_MAPPING = {
    'app1':'default', 
    'app2': 'test_mysql', 
}
Django Manager
Models
class User(models.Model):
    name = models.CharField(max_length = 10)
    pwd = models.CharField(max_length = 300)
此时对数据库操作需通过,
User.objects.create(name = name, pwd = pwd)
User.objects.filter(name = name)
...
若在操作过程中执行其他操作显得异常麻烦, 因此可以自定义管理器
class UserManager(models.Manager):
    def add_user(self, name, pwd):
        # 可执行若干代码
        ...
        user = self.create(name = name, pwd = generate_password_hash(pwd))
        return user
    def get_user(self, name):
        # 可执行若干代码
        ...
        return self.get(name = name)
    def find_name(self, name):
        # 可执行若干代码
        ...
        return self.filter(name = name).exists()
class User(models.Model):
    name = models.CharField(max_length = 10)
    pwd = models.CharField(max_length = 300)
    
    # 赋值objects
    objects = UserManager()
对User类相关操作可简化为
User.objects.add_user(name, pwd)
User.objects.find_name(name)
DRF
# models.py
import uuid as uuid
from werkzeug.security import generate_password_hash
from django.db import models
class User(models.Model):
    uuid = models.UUIDField(primary_key=True, auto_created=True, default=uuid.uuid4, editable=False)
    name = models.CharField(max_length=300)
    pwd = models.CharField(default=generate_password_hash('123456'), max_length=300)
    time = models.DateTimeField(auto_now_add=True)
from django.shortcuts import render
from rest_framework import generics
from rest_framework.viewsets import ModelViewSet
from .models import User
from .serializers import UserModelSerializer
class UserList(generics.ListCreateAPIView):
    """
        get:返回全部数据
        post:创建新数据
    """
    queryset = User.objects.all()
    serializer_class = UserModelSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    """
        get:返回指定数据
        put:更新数据
    """
    queryset = User.objects.all()
    serializer_class = UserModelSerializer
app/urls.py
from .views import UserList, UserDetail
from rest_framework.routers import DefaultRouter
from django.urls import path
# 路由列表
urlpatterns = [
    path("user/", UserList.as_view(), name="user_list"), 
    path("user/<str:pk>", UserDetail.as_view(), name="user_detail")
]
# urls.py
from rest_framework import serializers
from app.models import User
class UserModelSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = "__all__"
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls), 
    path('app/', include("app.urls")), 
]
视图与路由
访问网站本质即为访问对应 html 文件, 后再由浏览器等对其进行渲染, 最终展示出页面
视图
创建项目test_django并创建App应用
静态读取
创建目录
在 app 目录下新建 templates 文件夹

注册模板目录, 新建模板目录需在项目设置文件中进行注册
打开主目录 settings.py文件, 找到 TEMPLATES, 将新增模板路径添加至’DIRS’项中:
TEMPLATES = [
    {
        ...
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'), 
+           os.path.join(BASE_DIR, 'app/templates')
        ], 
        ...
    }, 
]

创建静态文件
在 app/templates 目录下新建 index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Title</title>
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>
编写视图函数
app/views.py 文件
from django.shortcuts import render
# Create your views here.
def index(request):
    return render(request, 'index.html')
绑定子路由
编写好函数后, 需将其绑定到对应路由, 由于此处是在项目中应用里配置, 故为子路由
在 app 目录下新建 urls.py 文件
from django.urls import path
from app import views
urlpatterns = [
    # 将函数绑定至对应路由
    path('index/', views.index)
]
注册子路由
绑定本应用路由后, 还需在项目中进行注册
主目录urls.py文件添加
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    ...
    # 注册app应用路由
+   path('app/', include('app.urls'))
    ...
]
此处 include()函数含义为包含 app 中所有路由, 即实现从主路由分发至子路由(路由转发)
运行, 访问http://127.0.0.1:8000/app/index
python manage.py runserver 8080
读取数据
访问http://127.0.0.1:8000/app/db, 在网页上显示数据库中所有元素信息
在 app/templates 目录下新建 db.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Title</title>
    </head>
    <body>
        {% for i in data %}
        <h1>学号 : {{ i.num }}</h1>
        <h2>姓名 : {{ i.name }}</h2>
        <h2>年龄 : {{ i.age }}</h2>
        {% if i.sex == 'M' %}
        <h2>性别 : 男</h2>
        {% elif i.sex == 'W' %}
        <h2>性别 : 男</h2>
        {% endif %} {% endfor %}
    </body>
</html>
此处 data 为数据库中值, 读取 html 作为参数传入
编写视图函数
编辑 app/views.py, 增加
...
def get_db(request):
    # 获取数据中全部信息
    stu_list = [i for i in Stu.objects.all()]
    return render(request, 'db.html', {'data' : stu_list})
添加路由
编辑 app/urls.py, 增加
urlpatterns = [
    ...
    path('get_db/', views.db)
]
运行项目, 访问http://127.0.0.1:8000/app/get_db
动态路由
关键字实现
单关键字
app/views.py
from django.http import HttpResponse
from django.shortcuts import render
# Create your views here.
def index(request, value):
    return HttpResponse("URL value = " + value)
app/urls.py
from django.urls import path, re_path
from app import views
urlpatterns = [
    # 将函数绑定至对应路由
    re_path(r'^index/(\w+)$', views.index)
]
项目urls.py文件
from django.contrib import admin
from django.urls import path, include
import app.views
urlpatterns = [
    path('admin/', admin.site.urls),
    # 注册app应用路由
    path('app/', include('app.urls'))
]
| 参数 | 含义 | 
|---|---|
\w | 
      匹配字母、数字、下划线, 等价于[A-Za-z0-9_] | 
+ | 
      匹配前面的子表达式一次或多次 | 
$ | 
      表示结尾 | 
此时可匹配127.0.0.1:8080/index/<字符>, 并且<字符>部分值将作为 index 函数第二个参数字符>字符>
多关键字
app/views.py
from django.http import HttpResponse
def value(request, v1, v2):
    res = "v1:%s v2:%s"%(v1, v2)
    return HttpResponse(res)
app/urls.py
from django.urls import path, re_path
from app import views
urlpatterns = [
    re_path(r'^value/(?P<v1>\w+)/(?P<v2>\w+)', views.value), 
]
前面 w+传给 v1, 后面 w+传给 v2, 此时可匹配127.0.0.1:8000/value/<字符1>/<字符2>这类 url, 并且字符1将传给 value 函数v1参数, 字符2将传给value函数v2参数
反射实现
反射, 通过输入函数名调用函数
# app/urls.py
from django.urls import path, re_path
from app import views
urlpatterns = [
    re_path(r'^func/(?P<fuc_name>\w+)/$', views.use_fuc_byname), 
]
# app/views.py
from django.http import HttpResponse
def use_fuc_byname(request, fuc_name):
    return eval(fuc_name)(request)
def hello_world(request):
    return HttpResponse('HELLO WORLD')
def goodbye_world(request):
    return HttpResponse('GOODBYE WORLD')
访问http:127.0.0.1:8000/func/hello_world, 会自动调用 hello_world()
删除硬编码URL
硬编码即href里的”/polls/”部分
<li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
它对于代码修改非常不利, 前面给urls定义了一个name别名, 可以用它来解决这个问题, 具体代码如下:
<li><a href="{% url 'detail' question.id %}">{{ question.question_text }}</a></li>
Django会在polls.urls文件中查找name=’detail’的路由, 具体的就是下面这行:
path('<int:question_id>/', views.detail, name='detail'), 
若想将polls的detail视图的URL更换为polls/specifics/12/, 那么仅仅只需要在polls/urls.py文件中, 将对应的正则表达式改成下面这样的就行, 所有模板中对它的引用都会自动修改成新的链接
# 添加新的单词'specifics'
path('specifics/<int:question_id>/', views.detail, name='detail'),