django使用

 

配置

安装

python install django

创建项目

django-admin startproject [项目名]
  • 示例, 创建名为TestDjango 项目

(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')

路由

编辑TestDjango/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 文件, 后再由浏览器等对其进行渲染, 最终展示出页面

视图

创建项目TestDjango并创建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'),