- 1 Django简述
- 2 Django安装
- 3 创建项目
- Application definition
- 安装应用
- 中间件
- 根路由
- 模板配置
- 数据库配置
- Password validation
- https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators">https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
- Internationalization
- https://docs.djangoproject.com/en/3.1/topics/i18n/">https://docs.djangoproject.com/en/3.1/topics/i18n/
- 语言
- 时区
- 是否使用国际标准时间,改为False,数据库存储的时间和当前时间一致
- Static files (CSS, JavaScript, Images)
- https://docs.djangoproject.com/en/3.1/howto/static-files/">https://docs.djangoproject.com/en/3.1/howto/static-files/
- 普通文件
- STATICFILES_DIRS = [
- os.path.join(BASE_DIR, ‘static’)
- ]
- 上传文件目录
- 富文本
- celery
- 4.2 路由参数
- 5 视图
- 6 模板
- 实例化自定义过滤器注册对象
- name代表在模板中使用的过滤器名称
- 7 模型
- 聚合查询:对多行查询结果的一列进行操作
- select count(*) from user
- 分组统计
- select type,count(*) from user group by password
- 查看生成的sql语句
- Q对象:构造逻辑或、逻辑非
- F对象:
- 7.5 模型管理器
- 7.6 模型对应关系
- 7.7 模型继承
- 7.7 分页
- session操作
- 9 拓展
- settings.py
- celery
- 设置系统的环境配置用的是Django的
- 实例化celery,第一个参数应用名称必须给定
- 设置时区
- 指定celery的配置来源,用的是项目的配置文件settings.py
- 让celery自动去发现创建的任务(task)
- 需要在app目录下,新建一个叫tasks.py文件
- 异步发送邮件
- 获取异步任务的结果
- 9.5.6 定时任务和计划任务
1 Django简述
2 Django安装
2.1 环境要求
python版本:>= 3.6 Django版本:3.2
2.1.1 pip安装
pip install django==3.2
测试是否安装成功
python # 开始python
>>> import django
>>> django.get_version()
2.1.2 二进制安装
到django官网下载安装包 解压到目录下,打开虚拟开发环境,执行以下命令
python -m pip install .
3 创建项目
3.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
目录介绍:
- admin.py 站点配置
- models.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或其子类)
服务器接收到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
- 使用aggregate()函数返回聚合函数的值 from django.db.models import Max
- 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)
- 可以使用模型的A属性与B属性进行比较
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
创建模型
# 创建模型
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
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基于cookie
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中的