1 Django简述

image.png

2 Django安装

2.1 环境要求

python版本:>= 3.6 Django版本:3.2

2.1.1 pip安装

  1. pip install django==3.2

测试是否安装成功

  1. python # 开始python
  2. >>> import django
  3. >>> django.get_version()

2.1.2 二进制安装

到django官网下载安装包 解压到目录下,打开虚拟开发环境,执行以下命令

  1. python -m pip install .

3 创建项目

3.1 创建

  1. django-admin startproject [projectName]

3.2 目录 apps下

├── apps
│ ├── init.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py

  • init.py 一个空文件,告诉python这个目录应该被看做一个python包
  • settings.py 项目的配置文件
  • urls.py 项目的URL声明
  • wsgi.py 项目与WSGI兼容的Web服务器入口
  • manager.py 一个命令行工具,可以使我们用多种方式对Django项目进行交互

    3.3 创建应用

    一个项目中可以创建多个应用,每个应用进行一种业务处理

在项目下执行,也就是manage.py同级

python manage.py startapp myApp
# 若报错,请检查是否只安装了python3.x

image.png
目录介绍:

  • admin.py 站点配置
  • models.py 模型
  • views.py 视图

    3.4 激活应用

  • 在settings.py文件中,将myApp应用加入到INSTALLED_APPS选项中

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

    3.5 配置settings.py

    ```python from pathlib import Path

BASEDIR = Path(_file).resolve().parent.parent

SECRET_KEY = ‘=ij=9js^@-k)0bw!sd53zh8aeg$vm*46t#18bw0i+8fp2j+qux’

DEBUG = True

ALLOWED_HOSTS = [‘*’]

Application definition

安装应用

INSTALLED_APPS = [ ‘django.contrib.admin’, ‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.messages’, ‘django.contrib.staticfiles’, ‘myApp’ ]

中间件

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’,

'middleware.myApp.myMiddle.MyMiddle',

]

根路由

ROOT_URLCONF = ‘project.urls’

模板配置

TEMPLATES = [ { ‘BACKEND’: ‘django.template.backends.django.DjangoTemplates’, ‘DIRS’: [Path.joinpath(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’, ], }, }, ]

WSGI_APPLICATION = ‘project.wsgi.application’

数据库配置

DATABASES = { ‘default’: { ‘ENGINE’: ‘django.db.backends.mysql’, ‘NAME’: ‘test’, ‘USER’: ‘root’, ‘PASSWORD’: ‘root’, ‘HOST’: ‘localhost’, ‘PORT’: 3306 } }

Password validation

https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators

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’, }, ]

Internationalization

https://docs.djangoproject.com/en/3.1/topics/i18n/

语言

LANGUAGE_CODE = ‘zh-Hans’

时区

TIME_ZONE = ‘Asia/Shanghai’

USE_I18N = True

USE_L10N = True

是否使用国际标准时间,改为False,数据库存储的时间和当前时间一致

USE_TZ = True

Static files (CSS, JavaScript, Images)

https://docs.djangoproject.com/en/3.1/howto/static-files/

import os

STATIC_URL = ‘/static/‘

普通文件

STATICFILES_DIRS = [

os.path.join(BASE_DIR, ‘static’)

]

SESSION_ENGINE = ‘redis_sessions.session’ SESSION_REDIS = { ‘host’: ‘localhost’, ‘port’: 6379, ‘db’: 0, ‘password’: ‘’, ‘prefix’: ‘session’, ‘socket_timeout’: 1 }

上传文件目录

MEDIA_ROOT = os.path.join(BASE_DIR, r’static/upfile’)

富文本

TINYMCE_DEFAULT_CONFIG = {

# // General options
'theme': "advanced",
'width': '700',
'height': '400'

}

celery

import djcelery

djcelery.setup_loader() # 初始化 BROKER_URL = ‘redis://127.0.0.1:6379/0’ CELERY_IMPORTS = (‘myApp.task’)

<a name="BXbbt"></a>
# 4 路由
<a name="mviPO"></a>
## 4.1 定义路由
<a name="OSgU4"></a>
### 4.1.1 根路由
<a name="4YQ9d"></a>
#### project/urls.py
```python
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include(('myApp.urls', 'myApp'), namespace="myApp")),
]

4.1.2 子路由

myApp/urls.py

from django.urls import path
from . import views

# 路由列表
urlpatterns = [
    # 不能以/ 开头 name用于反向解析
    # url 视图函数 路由名称
    path('home/', views.home, name='home'),
]

4.2 路由参数

4.2.1 path

在path中使用<参数名>表示所传参数,视图函数中的参数名必须和<>中参数名一致。参数可以是一下类型:

  • str: 如果没有指定参数类型,默认是字符串类型。字符串参数可以匹配除/和空字符串外的其他字符串
  • int: 匹配0和正整数,视图函数的参数将得到一个整数值
  • slug: 匹配由数字、字母、-和_组成的字符串参数
  • path: 匹配任何非空字符串,包括/。 ```python from django.urls import path, re_path from . import views

urlpatterns = [

# 不能以/ 开头
# 参数:路由匹配 视图函数 路由名称
path('', views.home, name='home'),
# string
path('change/<name>/',views.change_name,name='change'),
# int
path('show/<int:age>/', views.show, name='show'),
# slug
path('list/<slug:name>/', views.list_user, name='list'),
# path,如果有多个参数,path类型必须在最后一个
path('access/<path:path>/', views.access, name='access'),
re_path(r'^tel/(1[3-9]\d{9})/$', views.get_phone, name='phone'),
re_path(r'^tel/(?P<tel>1[3-9]\d{9})/$', views.get_tel, name='tel'),

]

<a name="OwI30"></a>
### 4.2.2 re_path
> re_path 正则匹配

```python
    re_path(r'^tel/(1[3-9]\d{9})/$', views.get_phone, name='phone'),
    # 命名组,视图中必须也是tel
    re_path(r'^tel/(?P<tel>1[3-9]\d{9})/$', views.get_tel, name='tel'),

5 视图

5.1 概述

视图本质上是一个函数(类)。这个函数第一个参数的类型是HttpRequest,返回一个HttpResponse实例。 为了使一个Python的函数成为一个Django可识别的视图,它必须满足这两个条件。

  • 作用:接收并处理请求,调用模型和模板,响应请求(返回HttpResponse或其子类)

    • 响应模板
    • 重定向
    • 直接响应字符串
    • 响应错误模板
    • json数据

      5.2 HttpRequest

      HttpRequest是从web服务器传递过来的请求对象,经过Django框架封装产生的,封装了原始的Http请求

  • 服务器接收到http请求后,django框架会自动根据服务器传递的环境变量创建HttpRequest对象

  • 视图的第一个参数必须是HttpRequest类型的对象
  • 在django.http模块中定义了HttpRequest对象的API
  • 使用HttpRequest对象的不同属性值,可以获取请求中多种信息

    def request(request):
      # get传参获取
      print(request.GET)
      # 获取单一值
      print(request.GET.get('username'))
      # 获取一个返回值列表
      print(request.GET.getlist('age'))
    
      # post
      print(request.POST.get('username'))
      print(request.POST.getlist('age'))
      print(request.body)
    
      # 获取请求方法
      print(request.method)
      # 获取请求路径
      print(request.path)
      print(request.get_full_path())
      print(request.get_host())
      print(request.get_raw_uri())
      print(request.get_full_path_info())
      # 其他属性
      # print(request.META)
      # 客户端地址
      print(request.META.get('REMOTE_ADDR'))
      # 来源页面
      print(request.META.get('HTTP_REFERER'))
    
      return HttpResponse('test')
    

    5.3 HttpResponse

    每一个视图函数必须返回一个响应对象,HttpResponse对象由程序员创建并返回。

属性 说明
content 字节字符串
charset 字符编码
status_code http状态码
content_type 指定输出的MIME类型

5.3.1 不调用模板,直接返回内容

def good(req):
    # res = HttpResponse()
    # res.content = b'good'
    # res.charset = 'utf-8'
    # res.content_type = 'text/html'
    # res.status_code = 200
    # return res
    return HttpResponse(b'good', status=400, charset='utf-8', content_type='text/html')

5.3.2 模板返回

def handle(req, data):
    return render(request, 'myApp/index.html', {'data': data})

5.3.3 JSON返回

json.dumps给ajax返回数据以后js代码中需要将json数据使用Json.parse()进行解析成js对象,而JsonResponse会自动转换成js对象(少了异步Json.parse()的转换)

import json
def handle2(req):
    # 直接返回字典
    # return JsonResponse({'name': 'xxx'})
    # return JsonResponse(json.dumps({'name': 'xxx'})) 等同于上面
    # 默认处理字典,若返回非字典,需要将safe变为False,不然会报错TypeError
    return JsonResponse([1, 2, 3, 4, 5], safe=False)

5.4 重定向

HttpResponseRedirect是HttpResponse的子类 HttpResponseRedirect只支持硬编码url,不能直接使用命名url,在使用URL命名时,需要先通过URL反向解析方法reverse先对命名URL进行解析,然后使用重定向

  • HttpResponseRedirect
  • redirect(HttpResponseRedirect的简写)

    5.4.1 不带参数重定向

    def handle_redirect(request):
      # return HttpResponseRedirect('/main')
      return redirect('/main')
    

    5.4.2 带参数重定向

    # 带参数重定向
    def handle_redirect2(req):
      return redirect("/home/{}/{}/".format('xxx', 30))
    

    5.4.3 应用内跳转和应用外跳转

    def handle_redirect3(req):
      # 应用内跳转可以不写http://127.0.0.1:8000
      # return redirect("/home")
      # 应用外跳转
      return redirect("https://www.baidu.com")
    

    5.4.4 反向定位

    # 反向定位:由应用命名空间:name来确定路由
    # 正向:路由->视图函数 反向:由名称来确定路由
    def handle_redirect4(req):
      # return redirect(reverse("myApp:home"))  # 不带参数
      # return redirect(reverse('myApp:list', kwargs={'name': 'admin'}))
      return redirect(reverse('myApp:list', args=('xxx')))
    

    5.5 错误视图

    Django内置了处理HTTP错误的视图(在django.views.defaults包下),主要错误及视图包括:

  • 403错误:permission_denied (权限错误)

  • 404错误:page_not_found(找不到指定文件)
  • 500错误:server_error(服务器内部错误)

    5.5.1 404错误及视图

    url匹配失败后,django会调用内置的django.views.defaults.page_not_found()函数,该视图函数会调用404.html模板进行显示。开发阶段可以开启调试模式,但产品上线后,要关闭调试模式。关闭调试模式后,会显示一个标准的错误界面。

# settings.py
DEBUG = False

404错误界面可以自定义:在项目templates目录下创建404.html,django找不到界面时,就会显示该界面。缺省会传递参数request_path,就是出错的url

6 模板

6.1 模板的渲染

6.1.1 loader加载

好处是可以加载一次模板,然后进行多次渲染

from django.template import loader

def index(request):
    temp = loader.get_template('myApp/index.html')
    print(temp.__dict__)
    # 渲染模板,生成Html源码
    res = temp.render(context={'content': 'hello index'})
    print(res)
    return HttpResponse(res)

6.1.2 render

from django.shortcuts import render
render(request,templatesname,context=None)
参数:
    request: 请求对象
  templatesname: 模板名称
  context: 参数字典,必须是字典

6.2 自定义过滤器

内置过滤器功能有限,如果不能满足需求,可以自定义过滤器

  • 在app里创建一个包:templatetags,包名是指定的
  • 在包里创建一个py文件 ```bash import datetime

from django import template

实例化自定义过滤器注册对象

register = template.library()

name代表在模板中使用的过滤器名称

@register.filter(name=’sub1’) def sub(value, arg): # 参数最多两个 “”” :param value: 传给sub过滤的值 :param arg: sub自带的参数 “”” return value + str(arg)

@register.filter(‘time_ago’) def time_ago(value): ‘’’

1. 1分钟内,显示刚刚
2. 1小时内,显示xx分钟前
3. 24小时内,显示xx小时前
4. 30天内,显示xx天前
5. 大于30天,显示具体时间
'''
if not isinstance(value, datetime.datetime):
    return value
now = datetime.datetime.now()
timestamp = (now - value).total_seconds()
if 60 <= timestamp < 60 * 60:
    return '{}分钟前'.format(timestamp / 60)
elif 60 * 60 <= timestamp < 60 * 60 * 24:
    return '{}小时前'.format(timestamp / 60 / 60)
elif 60 * 60 * 24 < timestamp < 60 * 60 * 24 * 30:
    return '{}天前'.format(int(timestamp / 60 / 60 / 24))
else:
    return value.strftime('%Y-%m-%d %H:%M')

- 在模板中使用
```bash
{% load customfilter %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>{{ time | time_ago }}</h1>
</body>
</html>

6.3 CSRF

6.3.1 局部禁用

# 局部禁用csrf
@csrf_exempt
def handleAjax(request):
    if request.is_ajax():
        return JsonResponse({"code": 0, "msg": 'success'})
    return render(request, 'myApp/ajax.html')

7 模型

7.1 数据库与模型

7.1.1 先创建模型后使用迁移生成表

若是新表,可以先创建模型,然后通过迁移来生成表

from django.db import models


# Create your models here.

class User(models.Model):
    # 字段名:不能是Python的关键字,不能使用连续的下划线
    # db_column: 数据表中的字段名称
    id = models.AutoField(primary_key=True, db_column='id')
    # CharField类型必须指明长度max_length
    username = models.CharField(max_length=30, unique=True)
    password = models.CharField(max_length=128)
    createTime = models.DateTimeField(auto_now_add=True)

    class Meta:  # 元数据
        db_table = 'users'
        ordering = ['username']
python manage.py makemigrations
python manage.py migrate

7.1.2 先创建表然后反向生成模型

7.1.2.1 查看生成的model

生成后需要自定加对应的model

python manage.py inspectdb msr_robot
python manage.py inspectdb (表名)
python manage.py inspectdb  # 不加表名则为全部表

7.1.2.2 自动生成model到指定文件

# 不加表名则为全部表
python manage.py inspectdb (表名) > apps/app_name/models.py

7.2 增删改

def handle_data(request):
    # 增加
    # user = User(username='tom', password=md5(b'123').hexdigest())
    # user.save()
    # 便捷插入
    # User.objects.create(username='tom1', password=md5(b'123').hexdigest())

    # user = {'username':'xx','password':'asdasd'}
    # User.objects.create(**user)
    # 批量插入
    # User.objects.bulk_create([User(username='u1', password='p1'), User(username='u2', password='p1')])
    # 修改
    # user = User.objects.get(pk=1)
    # user.password = '233'
    # user.save()

    # 删除
    # try:
    #     user = User.objects.get(id=1)
    #     print(user, type(user))
    #     if user:
    #         user.delete()
    # except Exception as e:
    #     print(e)
    # 删除多条
    users = User.objects.filter(id__gte=2)
    print(users)
    users.delete()

    return HttpResponse('xxx')

7.3 查

7.3.1 过滤器

管理器的方法 返回类型 说明
模型类.objects.all() QuerySet 返回表中所有数据
模型类.objects.filter() QuerySet 返回符合条件的数据
模型类.objects.exclude() QuerySet 返回不符合条件的数据
模型类.objects.order_by() QuerySet 对查询结果集进行排序
模型类.objects.values() QuerySet 返回一个QuerySet,其中每个对象为一个字典
模型类.objects.values_list() QuerySet 和values()基本相同,但每个对象是一个元组
def filter(request):
    # 查询结果集 QuerySet
    # 1. all
    # select * from user
    allData = User.objects.all()
    # print(allData)
    # 2. first
    # data = data.first()
    # 3. filter
    # select * from user where id >= 5
    # data = data.filter(id__gte=5)
    # 4. exclude
    # data = allData.exclude(id__gte=25)
    # data2 = allData.filter(id__lt=25) # 等价
    # print(data,'-----',data2)
    # 5. order_by
    # data = allData.order_by('username')
    # print(data)
    # 6. 限制结果集 不支持负下标 从0 到 1
    # data = allData.order_by('id')[:2] # <QuerySet [<User: User object (21)>, <User: User object (22)>]>
    # 7. values
    # 返回所有字段
    # data = allData.values()
    # print(data)
    # 返回指定字段
    data = allData[0:1].values('username', 'password')
    print(data)  # <QuerySet [{'username': 'u1', 'password': 'p1'}]>
    for user in data:
        print(type(user), user)  # <class 'dict'> {'username': 'u1', 'password': 'p1'}
    # 8. reverse() 反序
    # data = allData.order_by('id')[:2].reverse()
    # 9. distinct 去重
    data2 = allData.values('password').distinct().order_by('password')[:10]
    return HttpResponse('xxx')

7.3.2 非过滤器

def not_filter(request):
    # 非过滤器方法
    # 1. get 只能返回一条记录,若记录不存在:DoesNotExist,若有多条:MultipleObjectsReturned
    # user = User.objects.get(id__gt=1)
    # 2. first last 返回一个模型对象,第一条和最后一条
    first_user = User.objects.first()
    last_user = User.objects.last()
    print(first_user, last_user)
    # 3. earliest 根据指定字段返回最早增加的记录
    # 4. latest 根据field字段返回最近增加记录
    # 5. exists 判断查询集是否有数据
    flag = User.objects.all().exists()
    print(flag)
    # 6. count 返回查询集中对象的数目
    count = User.objects.count()
    print(count)
    return HttpResponse('query')

7.3.3 统计查询

导入模块

  • 聚合函数
    • 使用aggregate()函数返回聚合函数的值 from django.db.models import Max
      • Avg
      • Count
      • Max maxAge=Students.stuObj.aggregate(Max(‘sAge’))
      • Min
      • Sum
  • F对象 from django.db.models import F,Q
    • 可以使用模型的A属性与B属性进行比较
      • Grades.objects.filter(gGirlNum__gt=F(‘gBoyNum’))
    • 支持F对象的算术运算
      • Grades.objects.filter(gGirlNum__gt=F(‘gBoyNum’)+20)
  • Q对象

    • 概述:过滤器的方法中的关键字参数,条件为And模式
    • 需求 进行or查询
    • 解决 使用Q对象

      • Students.stuObj.filter(Q(pklte=3)|Q(sAgegt=50))
      • Students.stuObj.filter(Q(pk__lte=3)) 只有一个Q对象,就是用于匹配
      • Students.stuObj.filter(~Q(pk__lte=3)) ~取反 ```python def count_statics(request):

        聚合查询:对多行查询结果的一列进行操作

        select count(*) from user

        User.objects.aggregate(Count(‘id’)) User.objects.aggregate(Max(‘id’)) User.objects.aggregate(Min(‘id’))

        分组统计

        select type,count(*) from user group by password

        res = User.objects.values(‘password’).annotate(Count(‘id’)).order_by(‘password’) print(res)

        查看生成的sql语句

        print(res.query)

      Q对象:构造逻辑或、逻辑非

      data = User.objects.filter(Q(idgt=30) | Q(usernameicontains=’张’)) data = User.objects.filter(~Q(sex=1)) # 不能处理null

      F对象:

      data = User.objects.filter(username=F(‘password’)) return HttpResponse(‘x’)

<a name="x4mVF"></a>
## 7.4 原生sql使用
<a name="rncZL"></a>
### 7.4.1 raw
```python
# 原生sql语句使用
def raw_sql(request):
    # 多表联合查询
    # data = User.objects.raw("select * from user,detail where user.id = detail.user_id")
    # print(list(data))

    # 防止sql注入
    tmp = input('用户名:')
    users = User.objects.raw("select * from users where username like %s", ['%' + tmp + '%'])
    print(list(users), users)
    print(users.query)
    return HttpResponse(serializers.serialize('json', list(users)))

7.4.2 自定义sql

def custom_sql(request):
    # with语句相当于cursor = connection.cursor() 和cursor.close()
    # with connection.cursor() as cursor:
    #     cursor.execute("select * from users")
    #     row = cursor.fetchone()
    #     print(row)
    # 返回列表套字典
    with connection.cursor() as cursor:
        cursor.execute("select * from users")
        columns = [col[0] for col in cursor.description]
        print(cursor.fetchall())
        res = [dict(zip(columns, row)) for row in cursor.fetchall()]
        print(res)
    return HttpResponse('x')

7.5 模型管理器

  • 自定义模型管理器实现特定查询
  • 在类中定义类方法来进行查询(推荐使用,方便)
  • 在自定义模型管理器中实例化方法
class UserManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(password__isnull=False)
    @classmethod
    def after(cls, date):
        return cls.objects.filter(createTime__gt=date)

class User(BaseModel):
    objects = Manager()
    userManager = UserManager()
    # 字段名:不能是Python的关键字,不能使用连续的下划线
    # db_column: 数据表中的字段名称
    id = models.AutoField(primary_key=True, db_column='id')
    # CharField类型必须指明长度max_length
    username = models.CharField(max_length=30, unique=True)
    password = models.CharField(max_length=128)

    class Meta:  # 元数据
        db_table = 'users'
        ordering = ['username']

    # def __str__(self):
    #     return self.username

    @classmethod
    def after(cls, date):
        return cls.objects.filter(createTime__gt=date)

7.6 模型对应关系

关系数据库表和表之间是有关联的,这种关联有三种类型:

  • 一对一
    • OneToOneField
    • 级联数据获取
      • 主获取从 隐性属性,默认是级联模型名字
      • 从获取主,显性属性,就是属性名字
  • 一对多
    • ForeignKey
    • 级联数据获取
      • 主获取从 隐性属性 级联模型_set
      • 从获取主 显性属性
  • 多对多
    • 级联数据获取
      • 主获取从 隐性属性
        • 使用属性,属性是一个Manager子类
      • 从获取主,和主获取从一样
  • on_delete
    • CASCADE默认,默认级联删除数据
    • PROTECT 保护模式,当从表中存在级联记录的时候,删除主表记录会抛出保护异常,从表中不存在级联数据的时候,是允许删除的
    • SET_XXX
      • NULL 外键字段本身必须允许为空
      • DEFAULT 外键字段本身有默认值
    • DO_NOTHING 什么也不做

      7.6.1 一对一

      一个学生有一个档案,一个档案属于一个学生,那么学生表和档案表就是一对一关系。学生表是主表,档案表是从表,从表中有一个外键和学生表关联,并且要求外键取值唯一。对应关键字为:OneToOneField

创建模型

# 创建模型
from django.db import models

class Student(models.Model):
    sno = models.CharField(max_length=6, primary_key=True)
    sname = models.CharField(max_length=100, null=False)
    ssex = models.CharField(max_length=2, default='男', null=True)
    sage = models.IntegerField(null=True)
    sclass = models.CharField(max_length=10, null=True)

    class Meta:
        db_table = 'student'

class Archives(models.Model):
    idcard = models.CharField(max_length=18, unique=True)
    address = models.CharField(max_length=200, null=True)
    # on_delete = models.CASCADE 级联删除,删除学生会连同档案一块删除
    student = models.OneToOneField(Student, on_delete=models.CASCADE)

    class Meta:
        db_table = 'archives'

操作

# 一对一
# 增加数据
def addstudent(request):
    Student.objects.create(sno="163212", sname='xxx', sage=14)
    return HttpResponse('add student successful')


def addarchives(request):
    stu = Student.objects.get(pk='163212')
    arc = Archives.objects.create(idcard='111111111111111111', student=stu)
    return HttpResponse('增加档案')


# 删除数据

def deletestudent(request):
    stu = Student.objects.get(pk='163212')
    stu.delete()
    return HttpResponse('删除成功')


# 正向查询 通过学生获取学生档案
def findstudent(request):
    stu = Student.objects.first()
    arc = stu.archives
    print(arc)
    return HttpResponse(arc)


# 反向查询 通过档案获取学生
def findarchives(request):
    arc = Archives.objects.first()
    stu = arc.student
    print(stu)
    return HttpResponse(stu)


# 跨关系查询
def lookup(request):
    # 根据档案查学生
    # student = Student.objects.get(archives__pk=2)
    student = Student.objects.get(archives__idcard='111111111111111111')
    print(student)
    # 根据学生查档案
    archives = Archives.objects.get(student__sno='163212')
    return HttpResponse(archives)

7.6.2 一对多

一个出版社可以出版多本书,一本书只能被一个出版社出版,出版社和图书表属于一对多,一对多一般将主表中的主键并到从表中做外键,在模型中使用ForeignKey表示多对一

创建模型

class Publisher(models.Model):
    pname = models.CharField(max_length=100, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'publisher'

class Book(models.Model):
    bname = models.CharField(max_length=200, blank=True, null=True)
    # ForeignKey 参数:参照的模型名
    # 多对一模型通过ForeignKey表示多对一
    # 如果Publisher定义在book之后,第一个参数应该用字符串'Publisher'
    publisher = models.ForeignKey('Publisher', on_delete=models.DO_NOTHING,
                                  db_column='pid',  # 表中字段名
                                  related_name='books' # 通过出版社查图书时使用的关系名
                                  , null=True)
    class Meta:
        managed = False
        db_table = 'book'

操作

def books(request):
    # 创建一个出版社
    # pub = Publisher(pname='清华出版社')
    # pub.save()

    # 创建出版社并创建图书
    # pub = Publisher.objects.get(pk=2)
    # pub.books.create(bname='韭菜的个人修养')
    # books = Book.objects.filter(pk__lt=5)
    # pub.books.bulk_create(list(books))

    # 创建book
    # book = Book(bname='草根谭')
    # book.save()

    # 创建book并关联
    # pub = Publisher.objects.get(pk=1)
    # 方式1
    # book = Book.objects.create(bname='离骚2', publisher=pub)
    # 方式2
    # book = Book.objects.get(pk=2)
    # book.publisher = pub

    # 删除和更新
    # pub = Publisher.objects.get(pk=1)
    # pub.books.all().delete()  # 删除出版社出版的所有图书
    # pub.books.all().update(bname='xxx')

    # 查询 使用外键增删改查效率较低
    pub = Publisher.objects.get(pk=2)
    # pub.books 是一个查询管理器对象 objects
    print(pub.books.all())

    # 由图书查出版社
    book = Book.objects.get(pk=5)
    print(book.publisher.pname)

    # 复杂查询
    pub = Publisher.objects.filter(books__bname='韭菜的个人修养')
    print(pub)
    book = Book.objects.filter(publisher__isnull=True)
    print(book)
    return HttpResponse('success')

7.6.3 多对多

一个买家可以购买多件商品,一件商品可以被多个买家购买,买家和商品之间构成多对多关系,多对多关系必然会生成一张中间表:买家-商品表,记录商品和买家的关系,该表包含商品表主键和买家表主键。

创建模型

from django.db import models

class Buyer(models.Model):
    bname = models.CharField(max_length=30)
    level = models.IntegerField(default=1)

    class Meta:
        db_table = 'buyer'


class Goods(models.Model):
    gname = models.CharField(max_length=100)
    price = models.FloatField()
    # buyer = models.ManyToManyField(Buyer)  # 这种写法会自动生成第三张表,但我们无法直接控制
    buyer = models.ManyToManyField(Buyer, through='Orders')

    def __str__(self):
        return self.gname + "  " + str(self.price)

    class Meta:
        db_table = 'goods'

# 手动创建中间表
class Orders(models.Model):
    buyer = models.ForeignKey(Buyer, on_delete=models.CASCADE, db_column='bid')
    # 若使用了related_name,则可以通过order.goods,若没有,可通过order.goods_set
    goods = models.ForeignKey('Goods', on_delete=models.CASCADE, db_column='gid',related_name='goods')
    num = models.IntegerField(default=1)

    class Meta:
        db_table = 'orders'

操作

def testBuyerAndGoods(request):
    # good = Goods(gname='商品1',price=12.00)
    # good.save()
    # buyer = Buyer(bname='zzz', level=1)
    # buyer.save()
    # 购买商品
    goods = Goods.objects.get(pk=random.randint(1, Goods.objects.count()))
    goods.buyer.add(Buyer.objects.get(pk=random.randint(1, Buyer.objects.count())))
    goods.save()
    # 生成订单
    # order = Orders(buyer=Buyer.objects.get(pk=random.randint(1, Buyer.objects.count())),
    #                goods=Goods.objects.get(pk=random.randint(1, Goods.objects.count())),
    #                num=3)
    # order.save()
    # 删除商品
    # buyer = Buyer.objects.get(pk=1)
    # buyer.goods_set.clear()  # 删除所有商品
    # buyer.goods_set.remove(Goods.objects.get(pk=2)) # 删除用户Id=4订单中指定商品
    # orders = Orders.objects.filter(buyer__pk=1, goods__id__lt=5)
    # print(orders)
    # orders.delete()
    # 正向查询
    # buyer = Buyer.objects.get(pk=13)
    # goods = buyer.goods_set.all()
    # 反向查询
    good = Goods.objects.get(pk=3)
    buyers = good.buyer.all()
    print(buyers)
    return HttpResponse('success')

7.7 模型继承

django中的数据库模块提供了一个非常不错的功能,就是支持Models的面向对象,可以在models中添加Meta,指定是否抽象,然后继承

class Animal(models.Model):
        xxx 
    class Meta:
            abstract = True/False
class Dog(Animal):
        xxx

默认模型是允许继承的,但是默认的继承处理方式不是很合理:

  • 默认的父类中定义的字段会存在父类的表中,子类的数据通用部分会存在父表中,子类特有数据会在子表中,子类通过外键进行级联
  • 默认方式执行效率较低

开发中,需要将父类抽象化,在元信息中使用abstract=True

  • 抽象化的父类不会在数据库生成表
  • 子类会将父类中的通用数据,复制到字表中

    7.7 分页

    ```shell from django.core.paginator import Paginator

class CustomPaginator(Paginator): def lst(self, page): return self.page(page).object_list

<a name="9y9YE"></a>
# 8 认证
<a name="MVTBu"></a>
## 8.1 cookie与session
<a name="P2Z63"></a>
### 8.1.1 cookie
```python
def home(request):
    # 获取cookies 中指定键值对
    username = request.COOKIES.get('username')
    print(username)
    return render(request, 'App02/index.html', locals())


def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = User.objects.filter(username=username, password=password).first()
        print(user.username)
        if user:
            response = redirect('/user/')
            # 设置过期时间
            future = datetime.now() + timedelta(days=3)
            # 将cookie写回客户端
            response.set_cookie('username', username, expires=future)
            # 设置salt加密存储cookie数据
            # response.set_signed_cookie()
            return response
    return render(request, 'App02/login.html')


def logout(request):
    res = redirect('/user/')
    res.delete_cookie('username')
    return res

8.1.2 cookie和session区别与联系

  • 区别:
    • session将数据存储于服务端,cookie存储在客户端
    • cookie存储在客户端不安全,session是存储服务端,客户端只存sessionid
    • cookie在客户端存储值有大小限制,大约几kb,session没有限制
  • 联系
    • session基于cookie

      8.1.3 session配置

      8.1.3.1 首先在settings.py中有如下配置(系统默认):

      INSTALLED_APPS = [
      'django.contrib.sessions',
      ]
      # 中间件
      MIDDLEWARE = [
      'django.contrib.sessions.middleware.SessionMiddleware',
      ]
      

      8.1.3.2 需要进行数据迁移,生成session使用的数据库表

      8.1.3.3 相关操作

      ```python from django.contrib.auth.hashers import make_password, check_password

session操作

def doregister(request): if request.method == ‘POST’: username = request.POST.get(‘username’) password = request.POST.get(‘password’)

    # md5 = hashlib.md5()
    # md5.update(password.encode())
    # password_md5 = md5.hexdigest()

    user = User(username=username,
                password=make_password(password, None, 'pbkdf2_sha256'),
                )
    try:
        user.save()
    except IntegrityError:
        return JsonResponse({
            'code': 0,
            'msg': '用户名重复',
        })
    except Exception as e:
        print(type(e))
    # 设置session
    request.session['username'] = username
    return JsonResponse({
        'code': 1,
        'msg': '注册成功',
    })
return render(request, 'App02/register.html')

def validate_password(request): if request.method == ‘POST’: username = request.POST.get(‘username’) password = request.POST.get(‘password’) if check_password(password, User.objects.get(username=username).password): return HttpResponse(‘success’) return HttpResponse(‘fail’)

def session(request):

# session获取
# username = request.session.get('username')
# 清空session
# request.session.clear() # 清除所有session键值对,不清空sessionid
request.session.flush()  # 清除所有session键值对,并清空sessionid和数据库对应记录

return JsonResponse('xxx', safe=False)

- session删除
   - clear() 清空所有session 但是不会将session表中的数据删除
   - flush() 清空所有,并删除表中的数据
   - logout() 退出登录,清除所有,并删除表中的数据
   - del req.session['key'] 删除某一个session值
- session过期时间
   - req.session.set_expiry(5)
<a name="QFvP8"></a>
## 8.2 路由保护
<a name="EnG63"></a>
### 8.2.1 使用装饰器
```python
# 装饰器,路由保护
def check_login(func):
    def inner(*args, **kwargs):
        if args[0].COOKIES.get('username'):
            return func(*args, **kwargs)
        else:
            return redirect('/user/login')

    return inner


@check_login
def list_article(request):
    return HttpResponse('articles')

8.3 表单Form

8.3.1 相关对象

  • Widget: 用来渲染成HTML元素的部件,如:forms.Textarea对应HTML中的