Django
01 初始化
1.1 安装Django
pip install django==2.1.0
# 查看第三方库
pip list
1.2 创建项目
django-admin startproject djangoStudy
1.3 启动项目
# 在manage.py 里打开
python manage.py runserver 0.0.0.0:8000
# 0.0.0.0 代表任意ip都可以访问
# 访问地址
127.0.0.1:8000
02 路由
2.1 创建视图
创建视图文件
# djangoStudy文件夹下创建视图文件
- djangoStudy
- views.py
创建视图函数
from django.http import HttpResponse
# 请求视图
def index(request):
# HttpResponse 响应路由
return HttpResponse('这是Django的第一节课')
# 里面可以直接放标签
return HttpResponse('<h1>222</h1>')
2.2 创建路由匹配
# djangoStudy/urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', views.index),
]
2.3 动态路由
路由设置
# djangoStudy/urls.py
from . import views
urlpatterns = [
path('getUrl/<urlParams>', views.getUrlParams),
]
视图设置
# djangoStudy/views.py
from django.http import HttpResponse
def getUrlParams(request,urlParams):
return HttpResponse('获取到的动态路由参数:{urlParams}'.format(urlParams=urlParams))
多个参数传参
# 路由
urlpatterns = [
# &分隔,可以多个
path('getUrl/<urlParams>&<second>', views.getUrlParams),
]
# 视图
def getUrlParams(request,urlParams,second):
return HttpResponse('获取参数:{urlParams},{second}'.format(urlParams=urlParams,second=second))
2.4 路径转换器
urlpatterns = [
# 也可以设置参数类型
# str 匹配除了(/)之外的非空字符串,默认
# int 正整数,包括0
# slug 字母、数字以及横杠、下划线组成的字符串
# 任何非空字符串,包含路径分隔符(/)
path('getUrl/<int:urlParams>', views.getUrlParams),
]
2.5 正则匹配路径
from django.urls import path,re_path
from . import views
urlpatterns = [
re_path(r'^index/$', views.index),
]
2.6 分路由
创建app
python manage.py startapp book
# 一个项目下有多个app
在book项目下添加urls.py文件
注册app
每次新增一个app都需要去 djangoStudy/setting.py 注册app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book', # 注册book
]
分路由视图 book/views.py 配置
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def appbook(request):
return HttpResponse('分路由appbook')
分路由 book/urls.py 配置
from django.urls import path, re_path
from . import views
urlpatterns = [
path('appbook/', views.appbook)
]
主路由 djangoStudy/urls.py 注册分路由
from django.urls import path, include
urlpatterns = [
path('book/', include('book.urls'))
]
2.7 kwargs路由参数
路由传参
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index,{'index':'2'}), # 字典格式
]
视图接收
from django.http import HttpResponse
def index(request,**kwargs):
print('接收的kwargs参数:'+kwargs.get('index')) # 接收的kwargs参数:2
return HttpResponse('这是Django的第一节课')
2.8 重定向
路由设置
from django.urls import path, include
from . import views
urlpatterns = [
# 第一种 路由重定向
path('direct/', views.direct),
# 第二种 名称重定向
path('test/', views.test,name='test'),
path('directname/', views.directname),
]
视图设置
from django.shortcuts import render,redirect,reverse
from django.http import HttpResponse
# 第一种
def direct(request):
# 重定向路由
return redirect('/index')
def directname(request):
# 重定向路由模版名称
return redirect(reverse('test'))
03 模版引擎
3.1 配置模版引擎
在主目录下创建templates文件夹
在djangoStudy/setting.py下配置templates
# djangoStudy/setting.py
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',
],
},
},
]
在templates下创建book的app文件夹,里面存放html模版文件
在templates/book下创建book_index.html
3.2 渲染模版文件
from django.shortcuts import render
from django.http import HttpResponse
from django.template.loader import get_template
# 第一种渲染方法
def index1(request):
# 前提book_index.html的存在
getTemplate = get_template('book/book_index.html')
rendHtml = getTemplate.render()
return HttpResponse(rendHtml)
# 第二种渲染方法
def index2(request):
return render(request, 'book/book_index.html')
3.3 模版变量
views.py 视图传参
def index2(request):
return render(request, 'book/book_index.html', context={'name': '宁爸爸', 'age': 18})
book_index.html 模版接收
<body>
<p>姓名:{{ name }}</p>
<p>年龄:{{ age }}</p>
</body>
</html>
3.4 过滤器
# 路由模版传递参数
def filter(request):
context = {
'enStr': 'i am boy',
'enBigStr': 'I AM BOY',
'cnStr': '我是男孩',
'intNum': 10,
'floatNum': 1.234,
'nowDate': datetime.now(),
'list': [1, 2, 3, 4],
'emptyValue': '',
'noneValue': None
}
return render(request, 'book/book_index.html', context=context)
常用过滤器
- add:字符串相加,数字相加,列表相加,如果失败返回一个空字符串
- defalut:值为false时,提供默认值
- default_if_none:值为None时使用默认值
- first:返回列表中第一个值
- last:返回列表中最后一个值
- date:格式化时间和日期
- time:格式化时间
- join:与python中的join一样用法
- length:返回字符串或者数组长度
- length_is:字符串或者数组长度是否指定的值
- upper:把英文变成大写
- lower:把英文变成小写
- truncatechars:字符超出规定长度…
- truncatewords:单词超出规定长度…
- capfirst:首字母大写
- slice:切割列表
- floatformat:浮点数格式化
例:
add相加:{{ intNum|add:10 }} <br/> {# 值:30 #}
defalut默认值:{{ emptyValue|default:'输出的默认值' }} <br/> {# 值:输出的默认值 #}
default_if_none默认值:{{ noneValue|default:'只有为None时输出默认值' }} <br/> {# 值:只有为None时输出默认值 #}
列表首值:{{ list|first }} <br/> {# 值:1 #}
列表尾值:{{ list|last }} <br/> {# 值:4 #}
join拼接:{{ list|join:'+' }} <br/> {# 值:1+2+3+4 #}
数组或字符串长度:{{ list|length }} <br/> {# 值:4 #}
是否是3个长度:{{ list|length_is:3 }} <br/> {# 值:False #}
英文变大写:{{ enStr|upper }} <br/> {# 值:I AM BOY #}
英文变小写:{{ enBigStr|lower }} <br/> {# 值:i am boy #}
字符超出2个隐藏:{{ cnStr|truncatechars:2 }} <br/> {# 值:我… #}
单词超出2个隐藏:{{ enStr|truncatewords:2 }} <br/> {# 值:i am … #}
英文首单词大写:{{ enStr|capfirst }} <br/> {# 值:I am boy #}
列表切割:{{ list|slice:'0:3' }} <br/> {# 值:[1, 2, 3] #}
浮点数,默认保留一位:{{ floatNum|floatformat }} <br/> {# 值:1.2 #}
浮点数保留2位:{{ floatNum|floatformat:'2' }} <br/> {# 值:1.23 #}
date 和 time过滤器格式
- Y:四位数年。例:1999
- y:两位数年。例:99
- m:两位数的月。例:01、09
- n:一位数的月。例:1、9
- d:两位数的日。例:01、09
- j:一位数的日。例:1、9
- g:12小时制的一位数的小时。例:1、9、12
- G:24小时制的一位数的小时。例:1、9、24
- h:12小时制的两位数的小时。例:01、09、12
- H:24小时制的两位数的小时。例:01、09、24
- i:分钟。从00-59
- s:秒。从00-59
修改setting.py配置,时间转为上海东八区
TIME_ZONE = 'Asia/Shanghai'
例:
默认日期:{{ nowDate|date }} <br/> {# 值:Nov. 9, 2019 #}
默认时间:{{ nowDate|time }} <br/> {# 值:10:36 p.m #}
年月日时分秒:{{ nowDate|date:'Y/m/d G:i:s' }} <br/> {# 值:2019/11/09 22:40:09 p.m #}
3.5 配置静态文件夹
创建静态文件夹static在主目录,及其他文件
static
├─css
├─book.css
├─images
├─1.jpg
└─js
├─book.js
setting.py配置静态文件夹
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
模版引入静态文件
<!-- 加载静态文件 -->
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!-- 引入静态文件 -->
<link rel="stylesheet" href="{% static 'css/book.css' %}">
</head>
<body>
<div class="box"></div>
<!-- 获取图片 -->
<img src="{% static 'images/1.jpg' %}" alt="" width="200" height="200">
</body>
</html>
3.6 模版标签
- if / elif / else 判断语句
- ifequal 判断是否相等
- ifnotequal 判断值不相等
{# 判断语句 #}
{% if intNum == 123 %}
if
{% elif intNum == 10 %}
elif
{% else %}
else
{% endif %}
{# 判断是否相等 #}
{% ifequal emptyValue '' %}
判断值相等,不用==
{% endifequal %}
{# 判断值不相等 #}
{% ifnotequal noneValue None %}
222
{% endifnotequal %}
{# 判断值不相等 #}
{% ifnotequal noneValue None %}
222
{% endifnotequal %}
- for…in… 遍历
- forloop.counter 当前迭代的次数,下标从0开始。1,2,3,4
- forloop.counter0 当前迭代的次数,下标从1开始。0,1,2,3
- forloop.revcounter 当前迭代的次数,下标从0开始,且下标从大到小。4,3,2,1
- forloop.revcounter0 当前迭代的次数,下标从1开始,且下标从大到小。3,2,1,0
- forloop.first 判断是否是第一次迭代
- forloop.last 判断是否是最后一次迭代
- forloop.parentloop 嵌套循环返回的是上一层的for
{#列表遍历#}
{% for i in list %}
{{ i }}
{% endfor %}
{% for i in list %}
{# 当前迭代的次数,下标从0开始。1,2,3,4 #}
{{ forloop.counter }}
{# 当前迭代的次数,下标从1开始。0,1,2,3, #}
{{ forloop.counter0 }}
{# 当前迭代的次数,下标从0开始,且下标从大到小。4,3,2,1 #}
{{ forloop.revcounter }}
{# 当前迭代的次数,下标从1开始,且下标从大到小。3,2,1,0 #}
{{ forloop.revcounter0 }}
{# 判断是否是第一次迭代 #}
{{ forloop.first }}
{# 判断是否是最后一次迭代 #}
{{ forloop.last }}
{% endfor %}
{# 嵌套循环 #}
{% for i in list %}
{% for j in list %}
{# 嵌套循环返回的是上一层的for #}
{% if forloop.parentloop.counter == 1 %}
2222
{% endif %}
{% endfor %}
{% endfor %}
- widthratio 自带运算(乘除)
{# 乘法 10*2/1 #}
{% widthratio 10 1 2 %}{# 结果:20 #}
{# 除法 10*2/2 #}
{% widthratio 10 2 2 %} {# 结果:10 #}
- url 跳转传参
{# 第一个参数为模版名称,第二个参数是传递的参数 #}
<a href="{% url 'getUrl' 'aabb' %}">222</a>
with 变量重命名
{{ enStr }} {# i am boy #}
{#重命名变量使用的区域#}
{% with enStr as new_name %}
{{ new_name }} {# i am boy #}
{% endwith %}
autoescape 关闭转义
{{ html }} {# 22 #}
{# 关闭转义区域 #}
{% autoescape off %}
{{ html }} {# <h1>22</h1> #}
{% endautoescape %}
3.7 模版继承
extends 继承模版
- block 继承修改区块
└─book
└─templates
└─base.html
└─book_test.html
基础模版base.html
<body>
{# content是块名称 #}
{% block content %}
2222222
{% endblock %}
</body>
extends 继承模版
index.html 继承 base.html
{% extends 'base.html' %}
重新定义content块的内容
{% extends 'base.html' %}
{# 重新定义content内容 #}
{% block content %}
新内容
{% endblock %}
继承并修改
{# 继承content并修改 #}
{% block content %}
{# block.super 继承上一个内容 #}
{{ block.super }}
新内容
{% endblock %}
include 引入模版
{# 引入模版,必须在block里 #}
{% block content %}
{% include 'test.html' %}
{% endblock %}
3.8 配置自定义模版
主目录下创建common的文件夹
└─common
将common添加到settings文件配置
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book',
'common', # 添加配置
]
在common里面创建文件夹templatetags,存放自定义标签和过滤器
└─common
└─templatetags
3.9 自定义过滤器
创建过滤器文件
└─common
└─templatetags
└─common_textras.py
定义自定义过滤器
from django import template
# 模版实例化对象
register = template.Library()
# isZore 为过滤器名字,不定义时,取ifZore函数名称
@register.filter('isZore')
def ifZore(num):
return num == 0
# 定义过滤器
@register.filter
def my_sort(list): # my_sort 为过滤器名字
return sorted(list)
# 接收参数的过滤器
@register.filter
def joinList(list,index):
return list[index]
使用自定义过滤器
{# 加载自定义过滤器 #}
{% load common_textras %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{{ list | my_sort }}
{{ 0 | isZore }}
{{ list | joinList:0 }}
</body>
</html>
3.10 自定义简单标签
接着在上面common_textras.py文件里写
# 获取时间的标签
import datetime
@register.simple_tag
def get_time():
for_string = '%Y-%m-%d %H:%M:%S'
return datetime.datetime.now().strftime(for_string)
# 加法,传参的自定义标签
@register.simple_tag
def add_val(num1,num2):
return num1+num2
使用自定义简单标签
<body>
{% get_time %} {# 值:2019-11-10 17:03:02 #}
{% add_val 1 2 %} {# 值:3 #}
</body>
开启上下文的自定义标签
视图中的参数
def filter(request):
context = { 'enStr': 'i am boy' }
return render(request, 'book/book_index.html', context=context)
开启接收上下文参数
@register.simple_tag(takes_context=True) # 允许使用上下文参数,默认关闭
def get_last_val(context):
enStr = context.get('enStr') # 获取上下文中的enStr参数
return enStr+' boy boy boy'
使用该自定义标签
<body>
{% get_last_val %} {# 值:i am boyboy boy boy #}
</body>
3.11 自定义包含标签
包含文件,在book/template/test.html
{% for i in printList %}
{{ i }}
{% endfor %}
注册自定义包含标签
# test.html为包含的模版
@register.inclusion_tag('test.html')
def show_list():
list = ['我','的','天']
# printList为包含模版里的参数
return { 'printList':list }
在template/book/book_index.html里插入模版
{# 加载自定义过滤器 #}
{% load common_textras %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{% show_list %}
</body>
</html>
传参
@register.inclusion_tag('test.html')
def for_val(val):
# printList为包含模版里的参数
return { 'printList':val }
<body>
{% show_list '123' %}
</body>
开启上下文
@register.inclusion_tag('test.html',takes_context=True)
def open_text(context):
enStr = context.get('enStr') # 获取上下文中的enStr参数
return { 'printList':enStr }
<body>
{% open_text %}
</body>
04 模型系统
4.1 连接数据库
在settings.py配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'ucan', # 使用的数据库
'USER': 'root', # 用户
'PASSWORD': '123456', # 密码
'HOST': '127.0.0.1', # mysql服务器的ip地址
'PORT': '3306' # 端口
}
}
安装模块
pip install pymysql
django用pymysql代替mysqldb连接数据库
# djangoStudy/djangoStudy/__init__.py
import pymysql
pymysql.install_as_MySQLdb() # 数据库连接器
启动服务器错误时:
使用python3.7+django2.2+pymysql时遇到这个错误
django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.2
解决 C:\Python37\Lib\site-packages\django\db\backends\mysql(python安装目录)打开base.py
# if version < (1, 3, 13):
# raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)
C:\Python37\lib\site-packages\django\db\backends\mysql\operations.py
# 打开此文件把146行的decode修改为encode
if query is not None:
query = query.encode(errors='replace')
return query
4.2 字段类型映射关系
varchar - CharField # 字符
int - IntegerField # 数值
longtext - TextField # 长文本
date - DateField # 日期
datetime - DateTieField # 日期时间
# 布尔值
boolean - BooleanField # 传入True /
False,值不可以为空
NullBooleanFild # 布尔值可以为空
EmailField #邮箱地址
例:模型类
class Article(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=20)
age = models.IntegerField()
offon = models.BooleanField(default=True)
content = models.TextField()
create_time = models.DateField(auto_now_add=True) # 第一次创建设置时间
update_datetime = models.DateTimeField(auto_now=True) # 自动更新修改时间
# 默认获取当前时间戳(毫秒)
date_publish = models.IntegerField(verbose_name="更新时间戳" , default=datetime.datetime.now)
使用时间时,不根据数据库时间,修改settings.py,
USE_TZ = False
4.3 Field的常用参数
primary_key # 是否为主键
unique # 是否为唯一
null # 是否为空,默认为False
blank # 等于True时form表单验证时可以为空,默认为False
defalut: # 设置默认值
DateField.auto_now # 每次修改都会更新当前时间
DateField.auto_now_add # 第一次添加,将时间设置进去
4.4 创建模型类
表映射关系: 模型类 = 表 / 属性 = 字段
在创建mysql_test的app,并注册
在mysql_test/models.py
from django.db import models
class User(models.Model):
id = models.AutoField(primary_key=True) # 主键 自增长
name = models.CharField(max_length=20) # 字符字段,长度20
创建一个映射文件(迁移)
表结构任何修改都要重新执行映射文件
# appname 为app名称,appname存在只映射这个app的文件
python manage.py makemigrations [appname]
将映射文件的映射数据真正提交到数据库(迁移)
python manage.py migrate [appname]
生产的表名称,app名称+模型类名称 = mysql_test_user
强制执行
python manage.py makemigrations --fake
python manage.py migrate --fake
规定打印:mysql_test/model.py
class User(models.Model):
id = models.AutoField(primary_key=True) # 主键 自增长
name = models.CharField(max_length=20) # 字符,max_length 长度20
city = models.CharField(max_length=30, default='广州') # default 默认值
# 打印
def __str__(self):
return 'name:{},city:{}'.format(self.name, self.city)
# views.py
# <QuerySet [<User: name:zzw,city:江门>, <User: name:caj,city:广州>, <User: name:ch,city:潮州>, <User: name:zzw,city:开平>]>
def select(request):
# 查询所有
all_data = User.objects.all()
print(all_data.__dict__) # 字典形式返回
print(all_data) # 规定打印返回
4.5 添加数据
在mysql_test/views.py
# 导入用户表
from .models import User
def add(request):
# 方法一
user_data = User(name='zzw', city='江门')
user_data.save() # 保存
# 方法二
user_data = User()
user_data.name = 'caj'
user_data.city = '广州'
user_data.save() # 保存
# 方法三,不需要保存
User.objects.create(name='ch', city='潮州')
# 方法四
# 重复值,不允许提交
# 不重复就提交
# 返回元祖
user_obj = User.objects.get_or_create(name='zzw', city='江门')
# instance 返回实例
# created_bolean 创建结果 true成功 false失败
instance,created_bolean = user_obj
return HttpResponse('添加数据')
4.6 查询数据
mysql_test/views.py
基础查询
from django.core import serializers
def select(request):
# 查询所有
all_data = User.objects.all()
re_data = ''
for item in all_data:
re_data += ' 姓名:{},城市:{}'.format(item.name, item.city)
# 单个查询
data = User.objects.get(id=1) # 查询条件
re_data = ' 姓名:{},城市:{}'.format(data.name, data.city)
# 单个查询转json
json_data = json.loads(serializers.serialize("json", [re_data])[1:-1])
# 查询满足条件的对象
filter_data = User.objects.filter(city='江门')
# 是否存在
User.objects.filter(city='江门').exists()
re_data = ''
for item in filter_data:
re_data += ' 姓名:{},城市:{}'.format(item.name, item.city)
return HttpResponse(re_data)
字段查询
# 返回values选项的字典
user = User.objects.values('id','city')
# 返回values选项的元祖
user = User.objects.values_list('id','city')
# 过滤字段,只返回id,city字段
user = User.objects.only('id','city').filter(city='江门')
# 非过滤字段
user = User.objects.defer('id','city').filter(city='江门')
条件筛选
User.objects.first() # 获取数据第一条
User.objects.last() # 获取数据最后一条
# 查询不满足条件的,相当于not操作
User.objects.exclude(name='zzw')
# 排序,传入需要排序的字段
User.objects.order_by('age') # 'age'为升序 '-age'为倒叙
# 查询结果转换为字典
User.objects.all().values()
# 获取查询结果数量
User.objects.all().count()
# 等于
User.objects.filter(name__exact='zzw') # 双下划线 [key]___exact
# 模糊查询
User.objects.filter(name__contains='z') # 双下划线 [key]___contains
# 以...开头
User.objects.filter(name__startswith='c') # 双下划线 [key]___startswith
# 以...结尾
User.objects.filter(name__endswith='w') # 双下划线 [key]___endswith
# 成员所属 包含[...]里面的
User.objects.filter(age__in=[1,18]) # 双下划线 [key]___in
# 比较符号
User.objects.filter(id__lt=3) # 小于
User.objects.filter(id__lte=4) # 小于等于
User.objects.filter(id__gt=4) # 大于
User.objects.filter(id__gte=4) # 大于等于
# 区间 [2,8]
User.objects.filter(id__range=(2,8))
外键关联
# 一个表里有两个外键
# 'tag','author' 两个外键关联表的字段
User.objects.select_related('tag','author').all()
排序
User.objects.all().order_by('id','time')
聚合查询
from django.db.models import Avg, Max, Min,Count
# 计算学生平均年龄, 返回字典。age和avg间是双下划线哦
Student.objects.all().aggregate(Avg('age'))
{ 'age__avg': 12 }
# 学生平均年龄,返回字典。all()不是必须的。
Student.objects.aggregate(Avg('age'))
{ 'age__avg: 12' }
# 计算学生总年龄, 返回字典。
Student.objects.aggregate(Sum('age'))
{ 'age__sum': 144 }
# 学生平均年龄, 设置字典的key
Student.objects.aggregate(average_age = Avg('age'))
{ 'average_age': 12 }
# 学生最大年龄,返回字典
Student.objects.aggregate(Max('age'))
{ 'age__max': 12 }
# 同时获取学生年龄均值, 最大值和最小值, 返回字典
Student.objects.aggregate(Avg('age‘), Max('age‘), Min('age‘))
{ 'age__avg': 12, 'age__max': 18, 'age__min': 6, }
# 根据Hobby反查学生最大年龄。查询字段student和age间有双下划线哦。
Hobby.objects.aggregate(Max('student__age'))
{ 'student__age__max': 12 }
分组查询
# 按学生分组,统计每个学生的爱好数量
Student.objects.annotate(Count('hobbies'))
返回的结果依然是Student查询集,只不过多了hobbies__count这个字段。如果你不喜欢这个默认名字,你当然可以对这个字段进行自定义从而使它变得更直观。
# 按学生分组,统计每个学生爱好数量,并自定义字段名
Student.objects.annotate(hobby_count_by_student=Count('hobbies'))
# 按爱好分组,再统计每组学生数量。
Hobby.objects.annotate(Count('student'))
# 按爱好分组,再统计每组学生最大年龄。
Hobby.objects.annotate(Max('student__age'))
Annotate与values()联用
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.values('name').annotate(Count('hobbies'))
你还可以使用values方法从annotate返回的数据集里提取你所需要的字段,如下所示:
# 按学生名字分组,统计每个学生的爱好数量。
Student.objects.annotate(hobby_count=Count('hobbies')).values('name', 'hobby_count')
————————————————
版权声明:本文为CSDN博主「大江狗」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42134789/article/details/84567365
4.7 修改数据
def update(request):
# 方法一 单改
data = User.objects.get(id=3) # 查询条件,有时间时间也会跟着修改
data.name = 'baba'
data.save()
# 方法二 可多个一起修改
User.objects.filter(id=3).update(name='mama')
return HttpResponse('修改成功')
4.8 删除数据
def delete(request):
#方法一
User.objects.get(id=1).delete()
# 方法二 多个删除
User.objects.filter(id=3).delete()
return HttpResponse('删除成功')
4.9 表关系
多对一关系
例:一个学院多个学生
# 学院表
class Department(models.Model):
d_id = models.AutoField(primary_key=True)
d_name = models.CharField(max_length=30)
# 学生表
class Student(models.Model):
s_id = models.AutoField(primary_key=True)
s_name = models.CharField(max_length=30)
# on_delete 级联删除
department = models.ForeignKey('Department', on_delete=models.CASCADE) # 外键关联学院表
# 表会自动创建department_id字段,关联学院表的主键
一对一关系
例:一个学生对应一个学生详情
# 学生详情表
class Student_detail(models.Model):
# OneToOneField 一对一关联
student = models.OneToOneField('Student', on_delete=models.CASCADE)
s_age = models.IntegerField()
note = models.TextField()
phone = models.IntegerField()
多对多关系
通过中间表,同时关联学院表与学生表
class Course(models.Model):
c_id = models.AutoField(primary_key=True)
c_name = models.CharField(max_length=30)
# ManyToManyField 多对多关联
student = models.ManyToManyField('Student')
级联删除
on_delete=models.CASCADE # 删除主键同时删除外键数据
on_delete=models.PROTECT, null=True # 如果有外键在引用主键,主键不允许删除
on_delete=models.SET_NULL,null=True # 删除主键同时把外键设为空
on_delete=models.SET_DEFAULT,default='默认值' # 删除主键同时把外键设为默认值
4.10 一对多
新增/修改
from .models import Department,Student
def test(request):
d1 = Department.objects.get(d_id=3)
s6 = Student.objects.get(s_id=4)
# 方法一: 学生添加/修改课程,添加/修改
d1.student_set.add(s6)
# 方法二:新增学生与外键
d1.student_set.create(s_name='超人')
return HttpResponse('成功')
查询
from .models import Department,Student
def test(request):
d1 = Department.objects.get(d_id=3)
s6 = Student.objects.get(s_id=4)
# 学生找课程
d_name = s6.department.d_name
# 课程找学生
print(d1.student_set.all()) # 多个学生
# 指定查询
print(d1.student_set.get(s_id=1))
return HttpResponse(d_name)
删除
def test(request):
d1 = Department.objects.get(d_id=3)
s6 = Student.objects.get(s_id=4)
d1.student_set.remove(s6) # 指定删除
d1.student_set.clear() # 全部删除
return HttpResponse('ok')
4.11 一对一
from .models import Department,Student,Student_detail
def test(request):
stu = Student_detail.objects.get(id=1)
s6 = Student.objects.get(s_id=5)
name = stu.student.s_name # 通过学生详情找学生
print(s6.student_detail) # 学生找详情,单个
return HttpResponse(name)
4.12 多对多
rom .models import Department,Student,Student_detail,Course
def test(request):
# 课程
c1 = Course.objects.get(c_id=1)
c2 = Course.objects.get(c_id=2)
c3 = Course.objects.get(c_id=3)
# 学生
s2 = Student.objects.get(s_id=2)
s3 = Student.objects.get(s_id=3)
s4 = Student.objects.get(s_id=4)
# 增加 - 学生报名课程
s2.course_set.add(c1,c3)
s3.course_set.add(c2)
s4.course_set.add(c3)
# 查询
print(s2.course_set.all()) # 查询学生报了的课程
print(c1.student.all()) # 查询哪里学生报名了c1
# 删除
s2.course_set.remove(c1) # 学生删除课程
c2.student.remove(s3) # 课程删除学生
s2.course_set.clear() # 学生删除所有课程
return HttpResponse('add')
4.13 聚合查询
from .models import Student_detail
from django.db.models import Count,Avg,Max,Min,Sum
def test(request):
rs = Student_detail.objects.all().aggregate(Avg('s_age')) # 平均年龄
rs = Student_detail.objects.all().aggregate(Max('s_age')) # 最大值
rs = Student_detail.objects.all().aggregate(Min('s_age')) # 最小值
rs = Student_detail.objects.all().aggregate(Sum('s_age')) # 求和
rs = Student_detail.objects.all().aggregate(Count('s_age')) # 求数量
# 学生按照学院分组
# values 以键值对的形式展示
res = Student.objects.values('department').annotate(Count('department'))
# 获取结果的数量
res = Student.objects.values('department').annotate(Count('department')).values('count')
print(rs)
return HttpResponse('add')
4.14 FQ查询
F查询:针对两个字段值的比较
Q查询:如果你需要执行更复杂的查询(例OR语句)
- 可以使用&(and)、|(or)操作符组合
- ~(not)操作符在Q对象前表示取反
from .models import Student_detail, Student
from django.db.models import F,Q
def test(request):
# 给学生详情的每个学生年龄+1
# 查询s_age值 + 1
Student_detail.objects.all().update(s_age=F('s_age') + 1)
# 或
rs = Student.objects.filter(Q(s_name='毛不易')|Q(s_name='超人'))
# 且
rs = Student.objects.filter(Q(s_name='毛不易') & Q(s_id=2))
# 取反
rs = Student.objects.filter(~Q(s_name='毛不易'))
return HttpResponse('add')
05 请求与相应
5.1 请求参数
def getparams(request):
print(request.path) # 请求url
print(request.method) # 请求方法
print(request.encoding) # 编码格式
return HttpResponse('分路由appbook')
5.2 表单防止跨域
<!-- action没写具体提交地址,用什么地址进来,就会到什么地址去 -->
<form action='' method='post'>
{% csrf_token %} {# 防止跨域请求 #}
<input type='text' name='a' />
<input type='text' name='b' />
<input type='submit' value='提交' />
</form>
5.3 获取参数
get()
- 根据键获取值,只能获取一个值
getlist()
- 根据键获取值,以列表的形式返回
def getParams(request):
# 根据请求类型判断要做的事
if request.method == 'GET':
return HttpResponse('GET')
elif request.method == 'POST':
# 获取post参数值
title = request.POST.get('title') # 获取post的title值
return HttpResponse('分路由appbook')
post参数获取
def post(self, request):
postBody = json.loads(request.body)
username = postBody.get("username") # 不存在返回默认值none,默认值可设置
password = postBody.get("password")
phone = postBody.get("phone")
获取表单值
cleaned_data # 就是读取表单返回的值,返回类型为字典dict型
email=cleaned_data['username'] # 读取name为username的表单值
5.4 类视图
from django.urls import path
from . import views
urlpatterns = [
path('test/', views.Test.as_view()),
]
from django.views import View
class Test(View):
def get(self,request):
return HttpResponse('GET')
def post(self, request):
# 获取post参数值
title = request.POST.get('title') # 获取post的title值
return HttpResponse(title)
5.5 文件上传
上传配置
settings.py
MEDIA_ROOT = os.path.join(BASE_DIR,'static/media')
# static/media 创建文件夹
# enctype 上传文件必须写的参数
<form method='post' action="" enctype='multipart/form-data'>
{% csrf_token %}
<input type='file' name='file' />
<input type='submit' value='上传' />
</form>
视图
from django.views import View
import os
from djangoStudy.settings import MEDIA_ROOT
class Upload(View):
def get(self, request):
return render(request, 'djangoStudy/index.html')
def post(self, request):
print('进来了')
f1 = request.FILES.get('file') # 获取上传的文件
print(f1.name) # 上传文件名字
f_name = os.path.join(MEDIA_ROOT, f1.name) # 拼接路径,创建文件
# 上传的文件是二进制
with open(f_name, 'wb') as f:
# 读取里面的文件,读一行写一行
for i in f1.chunks():
f.write(i) # 写入文件
return HttpResponse('ok')
5.6 session
设置session
- 无指定两星期后过期
- value是整数则会话将在value秒后过期
- 若value是一个imedelta对象,则在当前时间加上这个指定时间/日期过期
- 若value为9,关闭浏览器过期
def setsession(request):
request.session['username'] = 'username'
request.session.set_expiry(0)
return HttpResponse('setsession')
获取session
def getsession(request):
username = request.session.get('getsession','default') # 不存在去默认值default
return HttpResponse(username)
删除session并删除会话 / 退出登陆
def deletesession(request):
# 删除seccion并删除会话
request.session.flush()
return HttpResponse('deletesession')
5.7 表单验证
创建用户表
from django.db import models
# Create your models here.
class UserModel(models.Model):
username = models.CharField(max_length=30,unique=True)
password = models.CharField(max_length=100)
email = models.EmailField()
在book里窗户form.py并配置表单检测
from django import forms
# 验证表单
class RegForm(forms.Form):
username = forms.CharField(max_length=30,min_length=2)
password = forms.CharField(
max_length=30,
min_length=1,
widget=forms.PasswordInput(attrs={"placeholder":'请输入密码'}) # 密码为空
)
password_repaet = forms.CharField(max_length=30,min_length=1,widget=forms.PasswordInput(attrs={"placeholder":"请输入密码"}))
email = forms.EmailField()
注册路由
from django.urls import path
from . import views
urlpatterns = [
path('reg/', views.Register.as_view()),
]
添加模版
from django.views import View
from .models import UserModel
from .forms import RegForm
class Register(View):
def get(self, request):
form = RegForm()
return render(request, 'book/reg.html', context={'form': form})
def post(self, request):
form = RegForm(request.POST) # 获取表单数据
print(form.is_valid())
# 验证表单数据是否正确
if form.is_valid():
# 那数据
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
password_repaet = form.cleaned_data.get('password_repaet')
email = form.cleaned_data.get('email')
if password == password_repaet:
UserModel.objects.create(
username=username,
password=password,
email=email,
)
return HttpResponse('注册成功')
else:
return HttpResponse('两次密码不一样')
提交标题
<body>
<form action='' method="post">
{% csrf_token %}
<!-- 表单的实例 -->
{{ form.as_p }}
<input type="submit" value="注册"/>
</form>
</body>
5.8 表单传递参数
# views.py
# 添加参数request
form = forms.RegisterForm(data=dict_data, request=request)
# forms.py 添加继承
class LoginForm(forms.Form):
#...
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(LoginForm, self).__init__(*args, **kwargs)
5.9 ModelForm 继承模型表单
from django.core.validators import MinLengthValidator
from utils.models import ModelBase
class Articles(ModelBase):
"""
文章
"""
title = models.CharField(
max_length=150,
validators=[MinLengthValidator(1)],
verbose_name="标题",
help_text="标题"
)
digest = models.CharField(
max_length=200,
validators=[MinLengthValidator(1)],
verbose_name="摘要",
help_text="摘要"
)
content = models.TextField(
verbose_name="内容",
help_text="内容"
)
clicks = models.IntegerField(
default=0,
verbose_name="点击量",
help_text="点击量"
)
image_url = models.URLField(
default="",
verbose_name="图片url",
help_text="图片url"
)
tag = models.ForeignKey('Tags', on_delete=models.SET_NULL, null=True)
author = models.ForeignKey('users.Users', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['-update_time', '-id'] # 排序
db_table = "tb_article" # 表名
verbose_name = "文章"
verbose_name_plural = verbose_name # 显示的复数名称
def __str__(self):
return self.title
forms.py
from django import forms
from articles.models import Articles, Tags
# ModelForm 继承模型表单
class ArticlesForm(forms.ModelForm):
# 重写覆盖
image_url = forms.URLField(
label="文章图片url",
error_messages={"required": "文章图片url不行为空"}
)
# ModelChoiceField 选项框
tag = forms.ModelChoiceField(
# 选择的范围
queryset=Tags.objects.only('id').filter(is_delete=False),
error_messages={
"required": "文章标签id不能为空",
"invalid_choice": "文章标签id不存在"
}
)
class Meta:
# 与数据库模型关联
model = Articles
# 需要关联的字段
fields = ['title', 'digest', 'content', 'image_url', 'tag']
error_messages = {
'title': {
'max_length': "文章标题长度不能超过150",
'min_length': "文章标题长度大于1",
'required': "文章标题不能为空"
},
'digest': {
'max_length': "文章摘要长度不能超过150",
'min_length': "文章摘要长度大于1",
'required': "文章摘要不能为空"
},
'content': {
'required': "文章内容不能为空"
},
}
获取表单数据save前添加数据,加入数据库
def post(self, request):
try:
json_data = request.body.decode()
# 参数为空判断
if not json_data:
return result_json(msg="参数为空")
dict_data = json.loads(json_data)
except Exception as e:
logging.info("错误信息:\n{}".format(e))
return result_json(msg="参数错误")
# 表单验
form = forms.ArticlesForm(data=dict_data)
if form.is_valid():
# 获取表单验证完成的数据集
# commit不插入mysql数据库,当save时,为后续添加方便
article_instance = form.save(commit=False)
article_instance.author = request.user.id
# article_instance.author = request.user 形式二
article_instance.save()
return result_json(code=0, msg="提交成功")
else:
err_str = regular.get_err(form)
return result_json(msg=err_str)
06 中间键
6.1 生命周期
# 执行视图之前调用,在每个请求上调用,返回None或者HttpResponse对象
process_request(self,request)
# 调用视图之前被调用,在每个请求上调用,返回None或者HttpResponse对象
process_view(self,request,callback,callback_args,callback_kwargs)
# 在视图刚好执行完毕之后被调用,再每个请求上调用,返回实现了render方法的相应对象
process_template_response(self,request,response)
# 当视图抛出异常时调用,在每个请求上调用,返回一个HttpResponse对象
process_exception(self,request,exception)
# 所有相应返回浏览器之前被调用,在每个请求上调用,返回一个HttpResponse对象
process_response(self,request,response)
6.2 添加中间键
在主目录djangoStudy下创建mymiddleeware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
class MyException(MiddlewareMixin):
# 接收抛出的异常
def process_exception(self, request, exception):
return HttpResponse(exception)
setting.py 注册中间键
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',
'djangoStudy.mymiddleeware.MyException', # 注册:文件夹.文件.类名
]
6.3 中间键例
class UserMiddleware(object):
def __init__(self,get_resp):
self.get_resp = get_resp
# 用户调用函数时响应
def __call__(self, request):
username = request.session.get('username','xxx')
if username:
# 给类设置属性与值
setattr(request,'myuser',username)
print('这是到达视图函数之前的代码')
# 给视图返回请求的方法
response = self.get_resp(request)
print('这是响应之后的代码')
return response
模版可以直接调用
<body>
{{ request.myuser }} <!-- xxx -->
</body>
6.7 上下文管理器
所有页面都可以用此数据
在主目录djangoStudy下创建mycontext.py
def my_user(request):
username = request.session.get('username','不存在')
return {'myuser':username}
在settings.py模版中添加上下文管理器
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',
'djangoStudy.mycontext.my_user', # 注册
],
},
},
]
模版可以直接调用
<body>
{{ myuser }}
</body>
07 admin后台
7.1 后台管理配置
在mysql_test/url.py注册
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls), # 后台管理
]
7.2 创建管理员
python manage.py createsuperuser
7.3 切换中文模式
settings.py
LANGUAGE_CODE = 'en-us'
# 改为
LANGUAGE_CODE = 'zh-hans'
7.4 注册表到admin
把原来mysql_test里的model.py注册到admin.py
from django.contrib import admin
from .models import Course,Student_detail,Student,User
# Register your models here.
admin.site.register(Course)
admin.site.register(Student_detail)
admin.site.register(Student)
admin.site.register(User)
7.5 自定义管理页面
列表页属性
list_display #显示字段,可以点击列头进行排序
list_display_links # 可以点击修改
list_filter #过滤字段,过滤框会出现在右侧
search_fields #搜索字段,搜索框会出现在上册
list_per_page #分页,分页框会出现在下册
添加修改页属性,二选一
fields # 属性的先后顺序
fieldsets # 属性分组
例:admin.py
from django.contrib import admin
from .models import Course, Student_detail, Student, Department
class DepartmentAdmin(admin.ModelAdmin):
list_display = ['d_id', 'd_name'] # 显示的字段
list_display_links = ['d_id', 'd_name'] # 点击修改字段
list_per_page = 2 # 分页数量
admin.site.register(Department, DepartmentAdmin)
08 auth系统
8.1 auth数据表
-
User
User是auth模块中维护用户信息的关系模型(继承了models.Model),数据库中该表被命名为auth_user
-
Group
User对象中有一个名为group的多对多字段,多对对关系由auth_user_groups数据表维护。Group对象可以通过user_set反向查询用户组中的用户
-
Permission
Django的auth系统提供了模型级的权限控制,即可以检查用户是否对某个数据表拥有增(add),改(change),删(delete)权限
8.2 User模型常用属性与方法
-
属性
- user 用户名
- email 邮箱
- groups 多对多的组
- user_permissions 多对多的用户权限
- is_staff 是不是admin的管理员
- is_active 是否激活,判断该用户是否可用
- is_superuser 是否是超级用户
- last_login 上次登录时间
- date_joined 注册时间
- is_authenticated 是否验证通过
- is_anonymous 是否是匿名用户
-
方法
- set_password(now_password) 设置密码,传入原密码
- check_password(now_password) 检查密码
- has_perm(perm) 判断用户是否有某个权限
- has_perms(perm_list) 判断用户是否有权限列表中的某个列表
8.3 认证系统功能
- create_user 创建用户
- authenticate 验证登录
- login 保存用户登录状态
- is_authenticated 判断用户是否登录
- @login_required 判断用户是否登录的装饰器
创建用户
from django.contrib.auth.models import User,Group,Permission
User.objects.create_user(username=username,password=password)
验证登陆
from django.contrib.auth import login,logout,authenticate
user = authenticate(username=username,password=password) # 存在返回用户数据
保存登陆状态
login(request,user)
退出登陆
logout(request)
8.4 登陆权限设置
判断用户是否登录的装饰器
from django.contrib.auth.decorators import login_required
@login_required
def blog_index(request):
# 未登录报错
return render(request)
@login_required(name='dispatch')
class blog_index(Views):
# ...
未登录时返回指定的路径,在settings.py配置
LOGIN_URL = '/login'
8.5 博客权限
有无添加博客权限,无权限则跳转到指定路径
视图函数方法
from django.contrib.auth.decorators import permission_required
# app标签.权限
@permission_required('blog.add_blogmodel')
def blog_add(request):
if request.method == 'GET':
return render(request,'blog/demo_add.html')
类视图方法
方法一:
from django.contrib.auth.decorators import method_decorator,login_required
@method_decorator(login_required,name='post')# post提交方式
class Blog_add(View)
def get(self,request):
方法二:
from django.contrib.auth.decorators import method_decorator,permission_required
@method_decorator(permission_required('add_blogmodel'),name='post')# post提交方式
class Blog_add(View)
def get(self,request):
有无删除博客权限,无权限则跳转到指定路径
@permission_required('blog.delete_blogmodel')
def blog_delete(request,blog_id):
blog = BlogModel.objects.filter(id=blog_id)
if blog:
blog.delete()
# 删除完成返回列表
return redirect(reverse('blog_list'))
else:
return HttpResponse('不存在这条博客')
添加权限
from django.contrib.auth.models import User,Permission
def test1(request):
# 获取用户
zw = User.objects.filter(username='zhuwei').first()
# add_blogmodel 添加权限
# delete_blogmodel 删除权限
pems = Permission.objects.filter(codename='delete_blogmodel') # 获取权限
zw.user_permission.add(pems) # 添加权限
return HttpResponse('权限添加成功')
8.6 类与函数视图装饰器
from django.contrib.auth.decorators import method_decorator
def my_decorator(func):
def wrapper(request,*args,**kwargs):
print('这是定义装饰器')
print('判断用户是否登陆,是否有相关权限')
return func(request,*args,**kwargs)
return wrapper
# 类装饰
@method_decorator(my_decorator,name='dispatch')
class IndexView(View):
# ...
# 函数装饰器
@method_decorator(my_decorator)
def get(self,request):
8.7 判断权限
from django.contrib.auth.mixins import PermissionRequiredMixin
# 新闻标签
class NewsTagManage(PermissionRequiredMixin, View):
# 需要的权限
permission_required = ('news.view_tag', 'news.add_tag')
raise_exception = True # 是否规定报错
# 无权限响应
def handle_no_permission(self):
if self.request.method.lower() == 'get':
return super(NewsTagManage, self).handle_no_permission()
else:
return to_json_data(err_msg='没有权限')
09 分页
9.1 实例化
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
def page_test(request):
course = ['C++','python','java','php','C','C#','挖掘机','Go']
# 2分页的数量
p = Paginator(course,2) # 实例分页对象
return HttpResponse('ok')
9.2 分页属性
print(p.count) # 对象总数量
print(p.num_pages) # 总页数
print(p.page_range) # 页码范围 [1,2)
print(p.per_page) # 每一页显示的数量
# 获取第一页
page1 = p.page(1)
print(page1.object_list) # 页码里面的数据
print(page1.number) # 获取页码,第一页
9.3 分页方法
page1 = p.page(1)
print(page1.has_next()) # 查看当前页有没有下一页,返回布尔值
print(page1.has_previous()) # 查看当前页有没有上一页,返回布尔值
print(page1.has_other_pages()) # 查看当前页有没有上一页或下一页,返回布尔值
print(page1.next_page_number()) # 下一页的页码
print(page1.previous_page_number()) # 上一页的页码
print(page1.start_index()) # 当前页的第一个索引对象
print(page1.start_index()) # 当前页的最后一个个索引对象
9.4 实例
from django.core.paginator import Paginator,PageNotAnInteger,EmptyPage
def blog_list(request):
blog_list = User.objects.all()
p = Paginator(blog_list,3) # 分页实例化对象
page = request.GET.get('page')
try:
pages = p.page(page)
except PageNotAnInteger:
pages = p.page(1) # 值不对,默认第一页
except EmptyPage:
pages = p.page(p.num_pages) # 页码出错
return HttpResponse('title')