1.安装 pyenv https://github.com/pyenv/pyenv-installer
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
vim ~/.bashrc
export PATH="~/.pyenv/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
将其写入最后
source ~/.bashrc
echo $PATH #如果出现这个 /home/qulinx/.pyenv/shims 表示 pyenv 安装成功
pyenv update
cd ~
ls -al
.pyenv
pyenv install --list
pyenv versions #查看已经安装的Python版本
下载软件包 Python-2.7.9.tar.xz Python-3.6.4.tar.xz
mkdir -p ~/.pyenv/cache
把它放到 ~/.pyenv/cache
pyenv install 3.6.4 -v #-v查看安装过程
pyenv rehash
pyenv global 3.6.4
两个产品 同时使用Python3.6 但是 a产品 使用的是 django 或者flask 1.0 b产品使用的是 djang 或者flask 2.0 为了让这两个产品同时在一个服务器上 运行 需要 pyenv + virtualenv 结合来使用
pip install virtualenv
sudo pip install --upgrade virtualenv
使用virtualenv
1.创建项目目录
sudo mkdir -p ~/myproject/blog
2.切换到项目目录
cd ~/myproject/blog
3.创建虚拟环境
pyenv virtualenv python版本号 虚拟环境名字
4.pyenv activate 虚拟环境名字
(env36) qulinx@qulinx-virtual-machine: #表示当前位于env36环境下
在这里可以安装 django 指定的版本
pip install django==2.0.2
5.pyenv deactivate env36 切出虚拟环境
psm pip 默认从官网上下载软件 为了提高下载速度 psm 能够让pip从国内源下载
pip install psm
psm ls 列出所有国内源
psm show 查看当前用的哪一个
psm use douban 应用豆瓣 源
创建虚拟环境 首先需要创建 项目目录 如果说 不同的人创建项目目录 不在一个地方 不太好管理
需求 :所有的虚拟环境 统一在一个目录下
virtualenvwrapper 让管理虚拟环境 更加简单化
安装 virtualenvwrapper
pip install virtualenvwrapper
vim ~/.bashrc
将你的用户名替换
if [ -f /home/你的用户名/.pyenv/versions/3.6.4/bin/virtualenvwrapper.sh ]; then
export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/home/你的用户名/.pyenv/versions/3.6.4/bin/python
export VIRTUALENVWRAPPER_VIRTUALENV=/home/你的用户名/.pyenv/versions/3.6.4/bin/virtualenv
source /home/你的用户名/.pyenv/versions/3.6.4/bin/virtualenvwrapper.sh
fi
===================替换好的==========================================================
if [ -f /home/json/.pyenv/versions/3.6.4/bin/virtualenvwrapper.sh ]; then
export WORKON_HOME=$HOME/.virtualenvs
export VIRTUALENVWRAPPER_PYTHON=/home/json/.pyenv/versions/3.6.4/bin/python
export VIRTUALENVWRAPPER_VIRTUALENV=/home/json/.pyenv/versions/3.6.4/bin/virtualenv
source /home/json/.pyenv/versions/3.6.4/bin/virtualenvwrapper.sh
fi
======================================================================================
source ~/.bashrc
1.创建虚拟环境
mkvirtualenv 虚拟环境的名字
mkvirtualenv my_env
deactivate 退出虚拟环境
lsvirtualenv 列出所有的虚拟环境
rmvirtualenv 虚拟环境名字 #删除虚拟环境
workon 虚拟环境名字 #切换到指定的虚拟环境
workon 后面什么都不加 列出可以使用的 虚拟环境
workon 虚拟环境名字 然后 cdvirtualenv #切换到虚拟环境所在的目录
创建虚拟环境的时候指定 Python版本
mkvirtualenv --python=python路径 虚拟环境的名字
pip install virtualenvwrapper-win
mkvirtualenv --python=C:\Python\Python36\python.exe 虚拟环境的名字 创建虚拟环境并制定Python版本
deactivate 退出虚拟环境
workon 虚拟环境名字 #切换虚拟环境
lsvirtualenv #列出所有的虚拟环境
rmvirtualenv 虚拟环境名字 #删除制定的虚拟环境
cdvirtualenv 切换到虚拟环境所在的目录
C:\Users\neyo>workon my_env
(my_env) C:\Users\neyo>cdvirtualenv
(my_env) C:\1806env\my_env>
到这个目录下面就能知道 有哪些虚拟环境
我的电脑 属性 环境变量 系统变量
添加 WORKON_HOME
C:\1806env #你指定的目录
python 3.6 版本
安装 virtualwrapper
安装 django==2.0
pycharm professional 专业版
安装 mysql5.7
安装 pymysql
2003 发布第一个版本 2005年正式版本 Simon、andrian 目的是为了 节省开发者的时间
django版本 | Python版本 |
---|---|
1.8 | 2.7 3.2 3.3 3.4 3.5 |
1.9 1.10 | 2.7 3.4 3.5 |
1.11 | 2.7 3.4 3.5 3.6 |
2.0 | 3.4 3.5 3.6 |
2.1 | 3.5 3.6 3.7 |
https://docs.djangoproject.com/en/2.1/ 中文文档 +官网
http://python.jobbole.com/87814/ Python + Django 如何支撑了 7 亿月活用户的 Instagram?
https://www.v2ex.com/
https://segmentfault.com/
组成部分 url 又叫统一资源定位符 uniform resource locator
https://www.baidu.com:443/tag/小说/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=Python培训#3
https 访问协议 http https ftp
www.baidu.com 主机名/域名/ip地址
443 表示端口号
tag/小说 表示查找的路径 表示访问子目录
/?s 表示查询的字符串
#3 锚点 后台不用管 前台用来页面定位用
url 中所有的字符都是 ASCII 码 如果非 ASCII 比如中文 浏览器会先进行编码 然后 传输
windows cmd
cd 表示查看当前位于哪个目录下
可以创建一个目录 单独用来存放所有的项目
1.先进入虚拟环境 workon 虚拟环境名称
2.django-admin startproject 项目名称
3.进入项目所在的目录
4.启动项目 python manage.py runserver 在浏览器 访问 127.0.0.1:8000
ctrl+c 结束
file -newproject -django 选择项目安装目录 existing interpreter (这里要选择你创建的虚拟环境的目录 )
有上角 绿色按钮 启动 红色 按钮停止
编辑配置 里边 选择 single instance only 表示每次 只运行 一个实例 避免 以后多个实例 不知道哪一个真正生效
关闭项目
file->close project ->然后 出来所有项目的列表 ->点击x即可
让别人访问你的项目
1.windows 下面
python manage.py runserver 0.0.0.0:8000
然后 在settings.py下面 谁要访问你 你的ip地址
ALLOWED_HOSTS = ['10.11.53.58']
2.pycharm
editing configuration
host 改为 0.0.0.0 然后制定端口号
然后 在settings.py下面 谁要访问你 你的ip地址
ALLOWED_HOSTS = ['10.11.53.58']
记得重启
上面会出现一个问题 本地没法访问
只需要在
settings.py下面 谁要访问你 你的ip地址
ALLOWED_HOSTS = ['10.11.53.58','127.0.0.1']
重启即可
最开始 希望 不同的模块单独分开 需要创建 一个 Python package
django 根据功能进行了分层 比如 豆瓣中的 读书 电影 音乐 模块 所有跟模块相关的views 统一写在 该app下面的views.py中
python 提供了一个非常方便的命令
python manage.py startapp app名称
创建成功以后 里边自动创建了多个文件 比如 views.py admins.py models.py
我们需要访问 http://127.0.0.1:9000/movie/
1.服务器接收 浏览器的请求
2.先到 settings.py里边查看 ROOT_URLCONF = 'first.urls' 也就是 所有的url请求 都到 first 下面urls.py 下设置 找 urlspatterns 变量
3.到 first 下面urls.py 下
from movie import views #意思是 引入movie 下面的 views.py
path('movie',views.movie),
4.到 movie 下面 编辑 views.py
from django.http import HttpResponse
def movie(request):
return HttpResponse("电影首页")
127.0.0.1:9000 movie() 就是它的视图函数
from django.http import HttpResponse
def movie(request): #第一个参数必须是 request 这个参数绝对不能少
return HttpResponse("电影首页") #返回值必须是 django.http.response.HttpResponseBase 的一个子类对象
项目下面的 settings.py 第26行 DEBUG = True 默认开启
开启以后好处:
1.每次ctrl+s 以后会自动重启项目 不需要手动重启
2.代码冲出现bug 在浏览器和控制台会提示错误信息
3.在生产环境下面 我们要求关闭掉 debug
4.DEBUG = False 当debug为False 注意 F大写 下面的ip地址 必须设置
ALLOWED_HOSTS = ['127.0.0.1','10.11.53.58']
vim book应用下 views.py
from django.shortcuts import render #这个render 用来渲染模板 后期会学到
from django.http import HttpResponse
def book(request):
return HttpResponse("图书首页 多读书多看报少吃零食多睡觉")
def book_detail(request,book_id): #通过参数将book_id传递过去
text = "您要请求的图书id为:%s" % book_id
return HttpResponse(text)
def author_detail(request):
author_id = request.GET['id'] #用来接收 用户在url中传递过来的参数
text = '作者的id是:%s' % author_id
#http://127.0.0.1:9000/book/author/?id=5
return HttpResponse(text)
def book_detail(request,book_id,category_id): #传递两个参数的时候
text = "您要请求的图书id为:%s 图书类目id为: %s" % (book_id,category_id) 括号隔开
return HttpResponse(text)
vim 项目下 urls.py
from django.urls import path
from django.http import HttpResponse
from book import views
def index(request):
return HttpResponse('首页')
urlpatterns = [
path('',index),
path('book/',views.book),
path('book/detail//',views.book_detail),
path('book/detail//',views.book_detail)#使用<> 传递参数 记住 <里边的变量> 要跟应用views下面的 参数一样 跟上面的 book_id 一样
]
def publisher_detail(request,publisher_id):
text = '出版社的id是:%s' % publisher_id
return HttpResponse(text)
from django.urls import converters (鼠标放上去 ctrl+点击)
path('book/publisher/',views.publisher_detail)
path('book/publisher/',views.publisher_detail)
path('book/publisher/',views.publisher_detail)
path('book/publisher/',views.publisher_detail)
int 只能是一个或者多个整型数字
path 所有字符都能满足 dc9c10b6-b153-4a25-a102-44cc0e1eb5ea import uuid print(uuid.uuid4())
str 除了 /之外的都可以
slug '[-a-zA-Z0-9_]+'
当 <> 里边 不写 int str uuid 默认是 匹配str
如果项目越来越大 模块越来越多 我们需要在项目主目录下 urls 添加多行 path()
每个app 自己的url 自己管理
首选 需要在 应用 中创建一个新文件 urls.py
vim book/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('',views.book),
path('detail//',views.book_detail)
]
vim book/views.py
from django.shortcuts import render
from django.http import HttpResponse
def book(request):
return HttpResponse("图书首页")
# Create your views here.
vim 主目录下面的urls.py
from django.urls import path,include
urlpatterns = [
path('book/',include('book.urls')),
#这里边 book后面加了/ app 下面的urls.py 注意不要多加/ urls是拼接起来的
]
为什么要给url命名
因为url 是经常变化的 如果在代码中写死 可能会经常该代码 给 url 起个名字 以后使用的时候 对名字进行反转 获取到修改后的url 这时候就不需要写死代码
示例代码 :
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse("后台首页")
else:
login_url = reverse('front:login')
print(login_url)
# return redirect('/signup/') 这是写死的情况
#最好是个变量
return redirect(login_url)
在app 下面的 urls.py中 命名
多个app之间 可能产生相同的url 为了避免 reverse('url名字')产生混乱 可以使用命名空间来区分 定义命名空间只需要在 app 下 urls.py 下面 app_name = '新名字'
revser() 就变成了 reverse('新名字:url名字');
示例代码: cms 下 url
app_name = 'cms'
urlpatterns = [
path('',views.index,name='index'),
path('signup/',views.login,name='login') name这里是给url起名字
]
在主目录下面的urls.py下 命名
一个app 可以拥有多个实例 多个url 可以映射同一个 app 好比 公司有一套后台管理系统 a组 只能通过http://127.0.0.1:9000/cms1 b组只能通过 http://127.0.0.1:9000/cms2
主目录的 urls.py
from django.urls import path,include
urlpatterns = [
path('',include('front.urls')),
path('cms1/',include('cms.urls',namespace='cms1')),
path('cms2/',include('cms.urls',namespace='cms2')), #多个url 指向一个 app
]
views.py
#当你用cms1访问的时候 用cms1的命名空间
#cms2访问 用cms2的命名空间
#获取当前的命名空间
current_namespace = request.resolver_match.namespace
print(current_namespace)
urls = reverse("%s:login" % current_namespace)
return redirect(urls)
include(moudule,namespace=None)
* moudule 子url的模块 比如 cms.urls
* namespace 给每个实例七个名字
如果说 你在主目录下面的path 里边写了 namespace 实例命名空间 但是 没有写 应用命名空间 app_name
会报错
path('book/',include('book.urls',namespace='book')),
path('book/',include('book.urls','book')),
re_path 跟 path 作用一样 不同的是 re_path 写url的时候 可以使用正则表达式 功能更强大
写正则表达式的时候 推荐使用原声字符串
如果re_path 正则表达式定义变量 用 ()包裹起来 看成一个分组参数有名字 前面需要加 ?P ?P<参数名字 >后面跟正则表达式规则
urlpatterns = [
#r'' 表示原生字符串 python正则表达式中 加上r
re_path(r'^$',views.article),
#http://127.0.0.1/article/list/year
#在python中参数有名字 那么需要使用?P<参数名字>
re_path(r'^list/(?P2\d{3})/$',views.article_list),
re_path(r'^list/(?P\d{2})/$',views.article_list_month),
]
**如果不是特殊要求 (不得不使用正则表达式来解决 ) 能用path 就是用path re_path 容易把代码弄复杂 **
如果在reverse url的时候 需要添加参数(鼠标放到reverse上 CTRL+点击) 可以添加 kwargs 参数 到reverse 中 示例代码
def index(request):
username = request.GET.get('username')
if username:
return HttpResponse("首页")
else:
#login_url = reverse('login')
#打开首页 自动跳转到 http://127.0.0.1/detail/10/20
#detail_url = reverse('detail',kwargs={"book_id":10,"page":20})
#想要打开首页自动跳转到 http://127.0.0.1/login/?next=
#return redirect(detail_url)
#login_url = reverse('login',kwargs={"?next=":"haha"})
login_url = reverse('login')+"?next=/"
return redirect(login_url)
def login(request):
return HttpResponse("登录页")
# Create your views here.
def book_detail(request,book_id,page):
text = "您的图书id是:%s" % book_id
return HttpResponse(text)
如果需要添加 查询字符串的参数 比如 打开首页 http://127.0.0.1:9000 自动跳转到 http://127.0.0.1:9000/?next=/
这个时候 kwargs 参数不生效 只能
login_url = reverse('login')+"?next=/" 这种形式
打开糗事百科 默认查看第一页内容 用户可以选择其它页面
在使用 path 或者 re_path的时候 通过传递参数 打开首页 http://127.0.0.1:9000 显示默认内容
from django.urls import path
from . import views
urlpatterns = [
#path('admin/', admin.site.urls),
path('',views.benshan),
path('page//',views.benshan)
]
from django.http import HttpResponse
dongbeiF4 = [
'尼古拉斯赵四',
'刘能',
'宋小宝',
'小沈阳'
]
# def index(request):
# return HttpResponse(dongbeiF4[0])
def benshan(request,page=0):
return HttpResponse(dongbeiF4[page])
上几节课 我们 直接返回的是文本 生产环境 页面需要样式 为了让 页面漂亮 市场上 好多不错 模板系统
其中就有 DTL jinja2 django 默认自带 DTL
**DTL django template language ** 模板引擎
DTL 和 html的区别 :
DTL 是由特殊语法的html 会解析参数 让页面更加动态化 编译完成以后 生成一个普通的html文件 然后发给客户端
render_to_string 先找到 模板 (页面) 将模板编译以后将内容渲染成python字符串格式 然后通过 HttpResponse 类 包装成 一个 HttpResponse 对象 返回给 前台
from django.shortcuts import render
from django.http import HttpResponse
from django.template.loader import render_to_string
# Create your views here.
def index(request):
html = render_to_string(‘index.html’)
return HttpResponse(html )
#以上可能有点繁琐
from django.shortcuts import render
def index(request):
return render(request,'index.html')
settings.py 中有一个 TEMPLATES 配置
BASE_DIR 你的项目目录
'DIRS': [os.path.join(BASE_DIR, 'templates')] 项目目录下templates文件夹
也就是说 render render_to_string 会来这里 查找模板
#'DIRS': [r"C:\templates"] 可以自定义主 templates 文件夹位置
'APP_DIRS': True, 要记得 在 settings.py中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book' #加入你创建的应用 要不然找不到这个模板
]
APP_DIRS 为true 先到 app 应用下面的 templates 查找 没有 到 主目录下面
Flase 直到 主目录下面 templates文件下查找 页面
1.先到应用自己的目录下面的 templates
2.如果自己目录没有 到其它应用 templates 下
3.其它应用也没有 到主目录下面templates 下
4,全部没有 抛异常 templatedoesnotexists 异常
如果 为 false 不从应用下面找 直接从主目录下 templates下查找
顺序:
render(request,‘home.html’)
先到 DIRS 有就返回 没有 到自己的应用 templates下面 别忘了应用加入到 INSTALLED_APPS 如果有 返回
如果没有 到 其它已经安装的应用下面 templates文件夹下
都没有 抛异常 templatedoesnotexists 异常
1.在模板中使用变量 需要将其放入 {{变量 }}
# haha = {
# 'username':'兰陵笑笑生',
# 'age':18
# }
{{username }}
2.访问对象的属性 对象.属性名来访问
class Person(object):
def __init__(self,username):
self.username = username
qulinx = Person("秋林兄")
# haha ={
# 'person':qulinx
# }
{{person.username}}
3.访问字典对应的value值 字典.key 不能通过[]访问
contexts = {
# 'person':{
# 'username':'qulinx666',
# 'keys':'abc' 尽量不要使用 keys 作为key
#
# }
# }
{{person.username}}
4.访问 元组 或者列表
contexts = {
'person':[
'红楼梦',
'三国演义',
'西游记',
'水浒传',
]
}
{{person.0}} #红楼梦
所有的标签都是在{% 中 %}
if 判断条件 == != < <= > >= in not in is is not
if有关闭标签 {% endif %} 因为模板不像python 有缩进 必须要有关闭
elif
else
举例子
context = {
'age':18
}
return render(request,'index.html',context=context)
{% if age < 18 %}
您还没成年,不能大宝剑
{% elif age == 18 %}
您满18周岁了 可以进来了
{% else %}
虽然你成年了但是大宝剑不是想有就有的
{% endif %}
{% if '雷鸣兄' in Tokyo %}
雷鸣兄正在工作
{% else %}
雷鸣兄正在待命
{% endif %}
for in
类似于python 中的 for…in…
{% for book in books %} 正序遍历
{{ book }}
{% endfor %}
{% for book in books reversed%} 倒序遍历
{{ book }}
{% endfor %}
{% for book in books %}
{{ forloop.counter0 }}
{{ book.name }}
{{ book.author }}
{{ book.price }}
{% endfor %}
forloop.counter 当前循环的下标 以 1开始
forloop.counter0 当前循环的下标 以 0开始
forloop.revcounter
forloop.revcounter0
forloop.first 是否第一次遍历 第一行
forloop.last 是否是最后一次遍历 最后一行
forloop.parentloop 多个循环嵌套的情况下 这个表示上一个for
{% for book in books %}
{% if forloop.first %}
{% elif forloop.last %}
{% else %}
{% endif %}
{{ forloop.counter }}
{{ book.name }}
{{ book.author }}
{{ book.price }}
{% endfor %}
遍历的对象没有任何元素的情况下 用{%empty%} 去匹配 显示 默认内容
比如
{% for content in contents %}
- {{ content }}
{% empty %}
- 暂时没有任何人评论赶紧抢沙发
{% endfor %}
遍历字典 ‘items’ ‘keys’ ‘values’
'person':{
'username':'zelinx',
'sanwei':'18 80 100',
'height':'181cm',
}
{% for key in person.keys %}
- {{ key }}
{% endfor %}
{% for key in person.values %}
- {{ key }}
{% endfor %}
{% for key,value in person.items %}
{{ key }}
{{ value }}
{% endfor %}
自动转义 DTL 自动开启了转义 < < 比如
百度大叔
<a href='http://www.baidu.com'>百度大叔</a>
你要是不清楚自己相干什么的情况下 最好是 开启 自动转义 防止XSS漏洞攻击
如果变量完全信任 那么可以 关闭自动 转移
{% autoescape off %}
变量
{% endautoescape %}
{% autoescape on %}
{{ info }}
{% endautoescape %}
qianfeng
url 标签 因为 我们跳转的url 不能写死 最好是 通过反转的形式 跟 reverse 差不多
具体写法
读书
同城
电影
book city movie 都是 url的名字
path('book/',views.book,name='book'), 先根据 name 将url反转也就是 取出 /book/然后交给 index.html
path('movie/',views.movie,name='movie'),
path('city/',views.city,name='city'),
如果 跳转 需要添加参数
python:
path('book/detail///',views.book_detail,name='detail'),
DTL:
转发锦鲤转给属龙的人
传递参数
空格 隔开 即可
转发锦鲤转给属龙的人
加 ?next 查询字符串的形式
登陆
模板中想要定义变量 可以使用with语句
{# {{ people.0 }}
#}
{# {{ people.0 }}
#}
{# {{ people.0 }}
#}
这个方式 有点 麻烦
可以使用下面两种方法
{% with zelinx=people.0 %} 要求 =号前后 不能有 空格
{{ zelinx }}
{{ zelinx }}
{{ zelinx }}
{% endwith %}
{% with people.0 as qulinx %}
{{ qulinx }}
{{ qulinx }}
{{ qulinx }}
{% endwith %}
后期做项目 可能不止 DTL模板引擎 还有 https://github.com/aui/art-template 或者 vue verbatim 标签 能够让 两个模板引擎共存
项目过程中 模板引擎 可能不止django自带的 DTL 还有 VUE VUE也是{{ }} 为了让部分代码 使用vue 解析方法
可以对 这部分代码 用
{% verbatim %}
{{ 这里的代码用vue或者 art-template引擎 解析 }}
{% endverbatim %}
其它 用DTL来解析
{{ person.0 }} 这个输出的是 zhangsan
{% verbatim %}
{{ kangbazi }} 这个输出的 是 {{kangbazi}}
{% endverbatim %}
每个页面都包含 head 底部部分 如果说 N页面 全部在重新写一遍 有点浪费时间 如果修改 全部都要改
现在 一个办法是 把公共的部分抽离出来 其它页面只需要引入这个公共部分即可 修改的话 只需要修改公共部分一个文件即可
1.首先将相同的代码 比如 header footer 统统单独写到一个文件中 比如 header.html
其它页面只需要
{% include '文件的名字' %}
{% include '文件的名字' %}
如果想要把 原本只有 固定的页面显示的变量 在 其它页面也显示
{% include '文件的名字' with 变量名=‘值’%}
示例代码 :
{% include 'header.html' with username='haha'%}
这是公司信息
{% include 'footer.html' %}
模板继承
将所有相同部分的代码 放到一个base.html中
其它页面{%extends 'base.html'%}
如果想要 显示base.html中的 特殊内容
{{block.super}}
示例代码
base.html
Title
{% block t0 %}
今天下午五点开校委会 #除了这里不同 其它全部相同
{% endblock %}
index.html
{% extends 'base.html' %} #继承模板网页
{% block t0 %}
我是首页的代码
{{ block.super }}
继承于 base.html 特殊的内容(今天下午五点开校委会)
{% endblock %}
在 DTL中 使用 static 来加载 静态文件 如果想要使用 还必须 {% load static%}
加载静态文件的步骤如下:
1.确保 ‘django.contrib.staticfiles’ 已经加入到了 INSTALLED_APPS
2.STATIC_URL 确保setting.py中设置了 #127.0.0.1/static/1.jpg #在浏览器中请求 静态文件的url
3.首先在应用下面 新建一个 目录 叫 static 在static 目录下面 再创建一个 以 app 名字 命名的 目录
4.到 INSTALLED_APPS 注册 你的应用
当static 不跟 所有的应用挂钩 需要这么操作
settings.py 加入
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
static 跟 templates 同级
如果不想每次加载静态文件 都 {%load static %} 在 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',
],
'builtins':['django.templatetags.static'], #在这里加入
},
},
]
其实就是 函数 我们可以通过在 模板中定义函数 来实现 我们的 目的
在 DTL模板中 不支持 圆括号传递参数
{{greet('ee')}}
正确的语法是 :
{{value|函数名字:值}}
将值和参数转成整型以后进行相加 如果不能转成整型 将值和参数 进行拼接
{{value|add:参数}}
源代码 :
def add(value, arg):
"""Add the arg to the value."""
try:
return int(value) + int(arg)
except (ValueError, TypeError):
try:
return value + arg
except Exception:
return ''
示例代码:
context = {
'num1':4,
'num2':10,
}
{{ num1|add:num2}} 返回14
类似于 python中的replace 移除值中指定的字符串
{{value|cut:""}}
{{ "hello world"|cut:"hello" }} #返回的结果 world
{{value|date:""}}
{{ today|date:"Y-m-d h:i:s" }}
Y 四位数年份
m 两位数月份 1-9 01-09
n 1-9 前面不加0
d 两位数字的天
h 小时 12小时 0前缀
i 分钟 0前缀
s 秒 0前缀
H 小时 24小时制 0前缀
G 小时 前面没有 0前缀
{{value|default:"默认值"}}
判断 value 是否为真 如果真 显示 value的值 如果为假 显示默认值
为false的情况 [] "" None {}
{{value|default_if_none:"默认值"}}
只有 value 值为 None的时候 才显示默认值 如果 "" [] {} 认定为真 显示空字符串 等
context = {
'value':None
}
返回 列表 元组 字符串的第一个元素
{{value|first}}
返回 列表 元组 字符串的最后一个元素
{{value|last}}
使用四舍五入的方式 格式化 一个浮点类型
{{value|floatformat}} 默认保留一位小数点
3.00000 返回 3
3.1415 返回3.1
3.15 返回3.2 四舍五入 保留一位小数点
{{ value|floatformat:3 }} 保留3位小数点
将列表 元组 字符串 使用 符号进行拼接
{{value|join:"@"}}
列表 元组 字符串 字典的长度
{{value|length}}
所有的字符串 转成小写
{{value|lower}}
所有的字符串转成大写
{{value|upper}}
随机取出 列表 元组 字符串中的任意一值 点名器
{{value|random }}
{{ value|safe }}
标记 里边的字符串是安全的 你不用给我转义
类似于python中的切片操作
{{ value|slice:"0:"}}
从0开始截取到最后
{{ value|slice:"0::2"}}
context = {
'value':"0123456789"
}
输出02468
删除字符串中所有的 html标签
{{ value|striptags }}
context = {
'value':""
}
最后输出 alert('我的每一支笔都知道你的名字')
如果给定的字符串超过了 过滤器要求的长度 那么就会进行切割 然后拼接 … 作为省略号
{{value|truncatechars:5}} 5这里 合理是指 2个字符+3个点
context = {
'value':"小爷,欢迎下次光临
"
}
最后结果:
如果给定的字符串超过了 过滤器要求的长度 那么就会进行切割 然后拼接 … 作为省略号 跟上面的区别是 它不切割 html标签 只切割字符串
context = {
'value':"小爷,欢迎下次光临
"
}
{{ value|truncatechars_html:6 }}
最终结果
小爷,...
-------------------以上都是 DTL内置过滤器 相当于系统提前给你写好的函数 但是有时候还不能满足所有的需求 这个时候需要自定义 过滤器 也叫自定义函数 记住 过滤器最多两个参数 ----------
模板的过滤器必须放在 app中 然后在app下面创建一个 python包 这个包的名字 必须叫 templatetags 在包里再创建一个python文件
目录结构:
book
models.py
views.py
urls.py
templatetags
my_filter.py
到settings.py INSTALLED_APPS写上应用的名字
接下来 就是在my_filter.py写过滤器代码
过滤器最多两个参数 (一般为 value ,XXXX)
过滤器的第一个参数 永远是被过滤的那个参数 也就是 | 左边 那个 (value)
然后将其注册到 模板中
register = template.Library()
register.filter("kangbaba",kangbazi)
最后在页面上 要 {%load my_filter%}
from django import template
register = template.Library() #这里是产生一个注册对象
def kangbazi(value,word):
return value+word
register.filter("kangbazi",kangbazi) #将其注册进去
这是在页面上显示的名字 后面是自定义的名字
{% load my_filter %}
自定义过滤器 自定义函数
{{ value|kangbazi:"下次再来" }}
from django.shortcuts import render
def index(request):
context = {
'value':'qulinx'
}
return render(request,'index.html',context=context)
# Create your views here.
发表微博 或者 空间动态的时候 一般不会直接显示发布的精确时间 而是 刚刚 *分钟之前 *小时之前 *天前
超过这些时间 显示 年月日 时分秒
from django import template
from datetime import datetime
register = template.Library()
def kangbazi(value,word):
return value+word
register.filter("kangbaba",kangbazi)
def time_since(value):
'''
刚刚 1分钟以内
大于1分钟 小于1小时 多少分钟之前
大于1小时 小于 24小时 几个小时之前
大于24小时 小于30天 几天前
否则 显示具体时间 2018/09/13 10:12:13
'''
now = datetime.now() #获取当前的时间
timestamp = (now-value).total_seconds() #当前时间 减去 发表时间的 间隔秒数
if timestamp < 60:
return '刚刚'
elif timestamp>=60 and timestamp<= 3600:
minutes = int(timestamp/60)
return '%s分钟之前' % minutes
elif timestamp>=60*60 and timestamp<= 60*60*24:
hours = int(timestamp/60/60)
return '%s小时之前' % hours
elif timestamp>=60*60*24 and timestamp<= 60*60*24*30:
days = int(timestamp/60/60/24)
return '%s天之前' % days
else:
return value.strftime("%Y-%m-%d %H:%M")
register.filter("times",time_since)
times是在页面上显示的过滤器名称
views.py
from django.shortcuts import render
from datetime import datetime
def index(request):
context = {
'value':'qulinx',
'mytime':datetime(year=2018,month=9,day=13,hour=15,minute=56,second=10)
}
return render(request,'index.html',context=context)
index.html
{% load my_filter %}
自定义过滤器 自定义函数
{{ value|kangbaba:"下次再来" }}
{{ mytime|times }}
django 操作 mysql 底层还是python操作mysql 想操作没问题 必须安装扩展或者叫做 驱动程序
pymysql 纯python 写的 执行效率不如 MySQL-python 高
MySQL-python c语言写的 只支持puthon2 不支持python3
mysqlclient MySQL-python 的一个分支 修复了bug 支持了python3
综上考虑 选择 mysqlclient
https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 下载下来之后 放到 一个 目录下面
然后 pip install mysqlclient-1.3.13-cp36-cp36m-win_amd64.whl 即可
settings.py中 有一个
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'djangodb', #数据库的名字
'USER':'root', #用户名
'PASSWORD':'123456', #密码
'HOST':'127.0.0.1', #连接地址
'PORT':'3306' #端口号
}
}
from django.shortcuts import render
from django.db import connection
def index(request):
cursor = connection.cursor()
#cursor.execute("insert into book(name,author) values('三国演义','罗贯中'),('红楼梦','曹雪芹')")
cursor.execute("select * from book")
rows = cursor.fetchall()
for row in rows:
print(row)
#rows = cursor.fetone()
#rows = cursor.fetchmany(2)
return render(request,'index.html')
show engines;
myisam
1. frm 表结构文件
2. myd 数据文件
3. myi 索引文件
当 对 读 和插入要求 高的话 一般选择 myisam
myisam 支持全文索引
不支持事务
表锁
不支持外键
innodb
1.ibd 数据和索引 共享一个表空间
2.frm 表结构文件
如果对 数据 安全性要求很高 也就是 经常对数据 更新 和删除操作 要使用innodb
mysql5.6 以后 默认的引擎是 innodb
mysql5.7 以后 innodb 也支持全文索引
支持事务
行锁
支持外键
bookmanager
项目增大 sql语句越来越复杂 大量采用 原声sql语句 会出现以下问题 :
1.重复利用率不高 需要写多个重复的sql语句
2.好多程序员 容易忽略 安全问题容易导致 sql注入
3.如果哪天业务逻辑放生变化 我们需要修改多条sql语句 容易遗漏某些sql语句 导致报错
为了解决这个问题 就出现了 ORM object relational Mapping 对象关系映射 通过ORM 我们可以使用 类来操作mysql 不用写原生sql语句 把每个数据表映射为一个类 数据行映射为一个个实例 字段映射为属性
1.这样 减少重复利用的概率 看起来直观清晰
2. ORM归根到底 是 转化为原声sql语句去操作数据库 有人觉得 转化过程容易出现损耗 损耗不会超过5%
3.写复杂的查询 更简单
4.可以将ORM移植到 sqlite mysql等
创建 应用 并到 settings.py中 注册应用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book',
]
到应用下面 的 models.py
from django.db import models
#如果想要将普通的一个类 映射为 数据库中的ORM模型
# 必须继承 models.Model
class Book(models.Model):
#1.id int(11) 自增长
id = models.AutoField(primary_key=True)
#2.name varchar(100) not null
name = models.CharField(max_length=100,null=False)
#3.author varchar(100) not null
author = models.CharField(max_length=100,null=False)
#4.price float not null
price = models.FloatField(null=False,default=0)
接下来
python manage.py makemigrations #生成迁移脚本
Migrations for 'DBrom':
DBrom\migrations\0001_initial.py
- Create model Book
(my_env) c:\www\db\_intr_demo>python manage.py migrate #映射到数据库中
Operations to perform:
Apply all migrations: admin, auth, book, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying book.0001_initial... OK
Applying sessions.0001_initial... OK
settings.py中 INSTALLED_APPS 写了多少 就创建多少模型
from django.shortcuts import render
from .models import Book
from django.http import HttpResponse
# Create your views here.
def index(request):
#添加数据到数据库中
# book = Book(name="金瓶梅",author='兰陵笑笑生',price='1000')
# book.save()
#查询数据库
# book = Book.objects.get(pk=1)
# print(book)
# books = Book.objects.filter(name="金瓶梅").first()
# print(books.price)
#删除数据
# book = Book.objects.get(pk=1)
# book.delete()
#修改数据
book = Book.objects.get(pk=1)
book.price = 10.1
book.save()
return HttpResponse('图书添加成功')
orm 增删该查 扩展
book = Book.objects.filter(name="三国演义",desc='name').first()
books = Book.objects.order_by("-price") #从高到低进行排序
for book in books:
print(book.name)
return HttpResponse("图书保存成功")
books = Book.objects.order_by("price") #从低到高进行排序
for book in books:
print(book.name)
return HttpResponse("图书保存成功")
class User(models.Model):
username = models.CharField(max_length=20,null=False)
password = models.CharField(max_length=100)
class Article(models.Model):
title = models.CharField(max_length=100,null=False)
content = models.TextField()
author = models.ForeignKey("User",on_delete=models.CASCADE)
参数: on_delete=
1.on_delete=models.CASCADE 删除掉user表里边的 那么 article 与其关联的数据 一并删除
2.on_delete=models.PROTECT 受保护 不让你删除
3.on_delete=models.SET_NULL,null=True
4.on_delete=models.SET_DEFAULT,default=User.objects.get(pk=2) 关联的用户删除以后 这个字段会立马变成默认值
5.on_delete=models.DO_NOTHING
1 2 4 用的比较多
以上 这些 是 django级别 最终还要看数据库级别
表关系 :
==一对一==
==一对多==
==多对多==
一篇文章 一个作者写 一个作者可以写多篇文章 作者 和文章之间的关系 就是 一对多
def one_to_many_view(request):
# article = Article(title="红楼梦",content="12213阿达")
# category = Category.objects.first()
# author = FrontUser.objects.first()
# article.category = category
# article.author = author
#
# article.save()
# return HttpResponse("添加成功")
#获取某个分类下面的所有的文章
# category = Category.objects.first() #获取指定的分类
# articles = category.article_set.all() #自动的产生一个article_set
# for article in articles:
# print(article.title)
#将文章添加到指定的分类中
category = Category.objects.first() #先获取到分类
article = Article(title="ccc",content="hahaha") #创建文章
article.author = FrontUser.objects.filter(id="2").first() #选中作者
category.article_set.add(article,bulk=False)
return HttpResponse("添加成功")
一个用户只有一个身份证号 只有一个用户详情
在django 中 一对一是通过 models.OneToOneFiled
第一步 我们在 frontuser 里边 新添加了一个模型 叫用户详情
class FrontUser(models.Model):
name = models.CharField(max_length=100,null=False)
def __str__(self):
return "" % (self.id,self.name)
class UserExtension(models.Model):
school = models.CharField(max_length=100)
user = models.OneToOneField("FrontUser",on_delete=models.CASCADE)
def __str__(self):
return "" % (self.id,self.school,self.user.id)
2.在article 模型中 创建 控制器
def one_to_one(request):
user = FrontUser.objects.filter(id="2").first() 首先将 id为2的 用户查出来
extension = UserExtension(school="千锋") #然后给他添加详情
extension.user = user 赋值外键的内容
extension.save() #保存
return HttpResponse("添加成功")
一个文章 可以 拥有多个标签 一个标签可以被 多篇文章使用
class Article(models.Model):
title = models.CharField(max_length=100,null=False)
content = models.TextField()
category = models.ForeignKey("Category",on_delete=models.CASCADE,null=True)
author = models.ForeignKey("frontuser.FrontUser",on_delete=models.CASCADE)
class Tag(models.Model):
name = models.CharField(max_length=100,null=False)
article = models.ManyToManyField("Article")
views.py
def many_to_many(request):
# article = Article.objects.first() #获取第一篇文章
# tag = Tag(name="热门文章")
# tag.save() #先将标签保存
# #然后将标签添加到文章上面
# article.tag_set.add(tag)
tag =Tag.objects.get(pk=2)
article = Article.objects.get(pk=2)
tag.article.add(article)
return HttpResponse("添加成功")
>>> import pytz
>>> from datetime import datatime
Traceback (most recent call last):
File "", line 1, in
ImportError: cannot import name 'datatime'
>>> from datetime import datetime
>>> now = datetime.now() #这个时间是 navie 时间
>>> print(now)
2018-09-17 10:21:46.822158
>>> utc_timezone = pytz.timezone("UTC") #定义一个世界统一时间的 对象
>>> utc_now = now.astimezone(utc_timezone)#将当前的时间转为 UTC时间
>>> print(utc_now)
1.AutoField int类型 自动增长
id = models.BigAutoField(primary_key=True) 要么不写 自动创建一个主键 既然自定主键的字段 必须要加上 primary_key=True
2.BigAutoField 长整型 范围更大
3.BooleanField bool 类型
4.CharField 字符串类型 必须要指定 max_length
5.FloatField
6.DateFiled 日期
create_time = models.DateField(auto_now=True)
# auto_now 每一次更新 都会获取到当前的时间 更新时间
# auto_now_add 第一次添加的时候 获取当前时间 创建时间
7.DateTimeFiled 日期加时间
create_time = models.DateTimeField(auto_now=True)
8.TimeFiled 时间
create_time = models.DateTimeField(auto_now=True)
9.EmailFailed
email = model.EmailFiled(max_length=100,null=False)
在数据库中 会以 varchar的形式 但是 后期 django 自带的表单验证类库 会识别你这个字段是否是 email 要求用户只能输入要 邮箱格式
10.TextFiled
文章内容 用varchar char 不够用 这个时候 可以使用 TextFiled
signature = models.TextField()
11.整型
num = models.IntegerField() 整型
num1 = models.BigIntegerField() 长整型
num2 = models.PositiveIntegerField() 正整型
num3 = models.SmallIntegerField() 小整型
num4 = models.PositiveSmallIntegerField()正小整型
12.URLFiled
存放url的字符串
13 .UUIDField
只能存放 uuid 类型
username = models.CharField(max_length=100,null=True)
age = models.IntegerField(null=True,db_column='author_age',default=0)
create_time = models.DateTimeField(default=now)
tele = models.CharField(max_length=100,unique=True,null=True)
from django.views.decorators.http import require_http_methods
from django.views.decorators.http import require_GET,require_POST,require_safe
#@require_http_methods(['POST','GET']) 这样post和get都能请求过来
#@require_http_methods(['POST']) 这样的话只能 POST请求过来 ==@require_POST
@require_http_methods(['GET']) 这样的话只能 GET请求过来 ==@require_GET
def index(request):
#首页用来展示数据
#一般用get请求 展示数据
articles = Article.objects.all()
return render(request,'index.html',context={"articles":articles})
django在接收到http请求以后 会根据请求携带的参数 及 报文 创建 一个 WSGIRequest对象 这个对象会作为视图函数的第一个参数传递给 视图函数 也就是经常看到的request
WSGIRequest 属性
from django.http import HttpResponse
import csv
def index(request):
response = HttpResponse(content_type='text/csv') #告诉浏览器这是一个 csv 格式的文件 不是html 如果不指定 就是html
response['Content-Disposition']="attechment;filename='abc.csv'"# 在 response 添加一个 'Content-Disposition'头 这样的话 是为了告诉他 该怎么样处理这个csv文件 attechment 说明这是一个附件 这样浏览器就不会显示它 而是下载 filename='abc.csv' 是指定附件的名字
writer = csv.writer(response) writer 将 内容写进respone
writer.writerow(['username','age','height'])
writer.writerow(['qulinx', '18', '181cm'])
return response
def template_csv_view(request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = "attechment;filename='linzhen.csv'"
context = {
'rows':[
['username','age','height','weight'],
['zelinx',18,'181cm','90kg']
]
}
template= loader.get_template('python1806.txt')
csv_template = template.render(context)
response.content = csv_template
return response
StreamingHttpResponse 在处理大文件的时候 不会因为 服务器响应时间长而超时 这个类不继承于 HttpResponse 类似于 大视频 边看边下载
def large_csv_view(request):
# response = HttpResponse(content_type='text/csv')
# response['Content-Disposition'] = "attechment;filename='linzhen.csv'"
# writer = csv.writer(response)
# for row in range(0,1000000):
# writer.writerow(['Row {}'.format(row),'{}'.format(row)])
# return response
response = StreamingHttpResponse(content_type='text/csv')
response['Content-Disposition'] = "attechment;filename='kangbazi.csv'"
rows = ("第{},{}\n".format(row,row) for row in range(0,10000000))
response.streaming_content=rows
return response
在写视图的时候,Django
除了使用函数作为视图,也可以使用类作为视图。使用类视图可以使用类的一些特性,比如继承等。
django.views.generic.base.View是主要的类视图,所有的类视图都是继承自他。如果我们写自己的类视图,也可以继承自他。然后再根据当前请求的method
,来实现不同的方法。比如这个视图只能使用get
的方式来请求,那么就可以在这个类中定义get(self,request,*args,**kwargs)
方法。以此类推,如果只需要实现post
方法,那么就只需要在类中实现post(self,request,*args,**kwargs)
。示例代码如下:
from django.views import View
def index(request):
return HttpResponse("")
class BookDetailView(View):
def get(self,request,*args,**kwargs):
return render(request,'detail.html')
类视图写完后,还应该在urls.py
中进行映射,映射的时候就需要调用View
的类方法as_view()
来进行转换。示例代码如下:
urlpatterns = [
path("",views.index)
path("detail//" ,views.BookDetailView.as_view(),name='detail')
]
除了get
方法,View
还支持以下方法['get','post','put','patch','delete','head','options','trace']
。
如果用户访问了View
中没有定义的方法。比如你的类视图只支持get
方法,而出现了post
方法,那么就会把这个请求转发给http_method_not_allowed(request,*args,**kwargs)
。示例代码如下:
class AddBookView(View):
def post(self,request,*args,**kwargs):
return HttpResponse("书籍添加成功!")
def http_method_not_allowed(self, request, *args, **kwargs):
return HttpResponse("您当前采用的method是:%s,本视图只支持使用post请求!" % request.method)
urls.py
中的映射如下:
path("addbook/",views.AddBookView.as_view(),name='add_book')
如果你在浏览器中访问addbook/
,因为浏览器访问采用的是get
方法,而addbook
只支持post
方法,因此以上视图会返回您当前采用的method
是:GET
,本视图只支持使用post
请求!。
其实不管是get
请求还是post
请求,都会走dispatch(request,*args,**kwargs)
方法,所以如果实现这个方法,将能够对所有请求都处理到。
django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。在这个类中,有两个属性是经常需要用到的,一个是template_name
,这个属性是用来存储模版的路径,TemplateView
会自动的渲染这个变量指向的模版。另外一个是get_context_data
,这个方法是用来返回上下文数据的,也就是在给模版传的参数的。示例代码如下:
from django.views.generic.base import TemplateView
class HomePageView(TemplateView):
template_name = "home.html"
def get_context_data(self, **kwargs):
context = {
"phone":"123456"
}
return context
{{phone}}
在urls.py
中的映射代码如下:
from django.urls import path
from myapp.views import HomePageView
urlpatterns = [
path('', HomePageView.as_view(), name='home'),
]
如果在模版中不需要传递任何参数,那么可以直接只在urls.py
中使用TemplateView
来渲染模版。示例代码如下:
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path('about/', TemplateView.as_view(template_name="about.html")),
]
在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书列表等等。在Django
中可以使用ListView
来帮我们快速实现这种需求。示例代码如下:
from django.views.generic import TemplateView,ListView
class ArticleListView(ListView):
model = Article #你的模型
template_name = 'article_list.html' #渲染的页面
paginate_by = 10 #每页显示多少条
context_object_name = 'articles' #在页面上 进行 遍历 for article in articles
ordering = 'id' #结果根据什么进行排序
page_kwarg = 'p' #http://127.0.0.1:9000/article/?p=2
def get_context_data(self, **kwargs):#获取上下文的信息 分页的相关信息
context = super(ArticleListView, self).get_context_data(**kwargs)
print(context.count)
print(context)
"""
{'paginator': ,
'page_obj': , 'is_paginated': True,
"""
return context
def get_queryset(self):
return Article.objects.filter(id__lte=89)
对以上代码进行解释:
ArticleListView
是继承自ListView
。model
:重写model
类属性,指定这个列表是给哪个模型的。template_name
:指定这个列表的模板。paginate_by
:指定这个列表一页中展示多少条数据。context_object_name
:指定这个列表模型在模板中的参数名称。ordering
:指定这个列表的排序方式。page_kwarg
:获取第几页的数据的参数名称。默认是page
。get_context_data
:获取上下文的数据。get_queryset
:如果你提取数据的时候,并不是要把所有数据都返回,那么你可以重写这个方法。将一些不需要展示的数据给过滤掉。注意:Paginator
和Page
类都是用来做分页的。他们在Django
中的路径为django.core.paginator.Paginator
和django.core.paginator.Page
。以下对这两个类的常用属性和方法做解释:
paginator.count
:总共有多少条数据。paginator.num_pages
:总共有多少页。paginator.page_range
:页面的区间。比如有三页,那么就range(1,4)
。class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
paginate_by = 20
context_object_name = 'articles'
ordering = 'create_time'
page_kwarg = 'p'
def get_context_data(self,**kwargs):
#要获取上下文的数据 需要继承于父类
context = super(ArticleListView,self).get_context_data(*kwargs)
print("="*30)
print(context)
print("=" * 30)
paginator = context.get('paginator') #获取context中的 paginator
print(paginator.count) #151条数据
print(paginator.num_pages) #8 分 8页
print(paginator.page_range) #range(1, 9) 包含第一页 不包含第九页
page_obj.has_next
:是否还有下一页。page_obj.has_previous
:是否还有上一页。page_obj.next_page_number
:下一页的页码。page_obj.previous_page_number
:上一页的页码。page_obj.number
:当前页。page_obj.start_index
:当前这一页的第一条数据的索引值。page_obj.end_index
:当前这一页的最后一条数据的索引值。class ArticleListView(ListView):
model = Article
template_name = 'article_list.html'
paginate_by = 20
context_object_name = 'articles'
ordering = 'create_time'
page_kwarg = 'p'
def get_context_data(self,**kwargs):
#要获取上下文的数据 需要继承于父类
context = super(ArticleListView,self).get_context_data(*kwargs)
page_obj = context.get('page_obj') 从 context 中 获取 page_obj
#http://127.0.0.1:9000/article/list/?p=7
print(page_obj.has_next()) 是否还有下一页 True
print(page_obj.has_previous()) 是否还有上一页 True
print(page_obj.next_page_number()) 下一页的页码 8
def jsonresponse_view(request):
person = {
'username':'kangbazi1806',
'age':18,
'height':'181cm'
}
person_str = json.dumps(person)
#将数据dump转化为 json字符串
response = HttpResponse(person_str, content_type='application/json')
# 封装成 Response返回给浏览器 并告诉浏览器 这是 json 类型
return response #
最后返回 response
以上的可以换成
def jsonresponse_view(request):
person = {
'username':'kangbazi1806',
'age':18,
'height':'181cm'
}
response = JsonResponse(person,safe=False) JsonResponse 只能转化字典类型的 非字典类型 必须加上 safe=False
return response
301
301
302
404 找不到目标url
403 你没有权限访问相关的数据
405 请求方法不允许 限制请求的过程中 只允许你 get请求 但是 你很调皮 非得 post传过去 这个时候报405错误
400 请求的参数有错误
500 服务器内部错误 代码有 bug
502 一般是 服务器部署错误 比如 nginx启动 但是 uwsgi 有无 没法完成正常的请求
生产环境 也就是 线上 上线以后 会把debug 关闭 settings.py
DEBUG = False ALLOWED_HOSTS = ['127.0.0.1']
常见的错误 比如 404 500 直接在 templates 下面 新建 404.html 500.html如果出现 404 500 错误 会自动的显示这个页面的内容
其它错误 比如 400 403
专门 定义一个 app 名字叫 errors
在errors 下面 新建一个 templates 下面再建一个 errors 里边 创建 页面 400.html或者 403.html 502.html
在errors 应用下面
vim views.py
from django.shortcuts import render
def view_400(request):
return render(request,'errors/400.html')
# Create your views here.
def view_403(request):
return render(request,'errors/403.html')
def view_502(request):
return render(request,'errors/502.html')
vim urls.py
from django.urls import path
from . import views
app_name = 'errors'
urlpatterns = [
path('400.html',views.view_400,name='400'),
path('403.html',views.view_403,name='403'),
path('502.html',views.view_502,name='502'),
]
在 项目总的 urls.py 下面
path('errors/',include('errors.urls'))
其它 应用 比如 front 或者 book 或者其它 通过redirect 重定向
def index(request):
if not request.GET.get('username'):
#400请求参数有错误
return redirect(reverse('errors:502'))
return HttpResponse("首页")
单纯从前端的html
来说,表单是用来提交数据给服务器的,不管后台的服务器用的是Django
还是PHP
语言还是其他语言。只要把input
标签放在form
标签中,然后再添加一个提交按钮,那么以后点击提交按钮,就可以将input
标签中对应的值提交给服务器了。
Django
中的表单丰富了传统的HTML
语言中的表单。在Django
中的表单,主要做以下两件事:
在讲解Django
表单的具体每部分的细节之前。我们首先先来看下整体的使用流程。这里以一个做一个留言板为例。首先我们在后台服务器定义一个表单类,继承自django.forms.Form
。示例代码如下:
# forms.py
class MessageBoardForm(forms.Form):
title = forms.CharField(max_length=3,label='标题',min_length=2,error_messages={"min_length":'标题字符段不符合要求!'})
content = forms.CharField(widget=forms.Textarea,label='内容')
email = forms.EmailField(label='邮箱')
reply = forms.BooleanField(required=False,label='回复')
然后在视图中,根据是GET
还是POST
请求来做相应的操作。如果是GET
请求,那么返回一个空的表单,如果是POST
请求,那么将提交上来的数据进行校验。示例代码如下:
# views.py
class IndexView(View):
def get(self,request):
form = MessageBoardForm() #实例化一个表单对象
return render(request,'index.html',{'form':form})
def post(self,request):
form = MessageBoardForm(request.POST)
if form.is_valid(): #这里是为了验证 froms.py中的规则
title = form.cleaned_data.get('title')
content = form.cleaned_data.get('content')
email = form.cleaned_data.get('email')
reply = form.cleaned_data.get('reply')
return HttpResponse('success')
else:
print(form.errors)
return HttpResponse('fail')
在使用GET
请求的时候,我们传了一个form
给模板,那么以后模板就可以使用form
来生成一个表单的html
代码。在使用POST
请求的时候,我们根据前端上传上来的数据,构建一个新的表单,这个表单是用来验证数据是否合法的,如果数据都验证通过了,那么我们可以通过cleaned_data
来获取相应的数据。在模板中渲染表单的HTML
代码如下:
<form action="" method="post">
<table>
{{form.as_table}}
<tr>
<td>td>
<td><input type="submit" value="提交">td>
tr>
table>
form>
我们在最外面给了一个form
标签,然后在里面使用了table
标签来进行美化,在使用form
对象渲染的时候,使用的是table
的方式,当然还可以使用ul
的方式(as_ul
),也可以使用p
标签的方式(as_p
),并且在后面我们还加上了一个提交按钮。这样就可以生成一个表单了
使用Field
可以是对数据验证的第一步。你期望这个提交上来的数据是什么类型,那么就使用什么类型的Field
。
CharField:
用来接收文本。
参数:
EmailField:
用来接收邮件,会自动验证邮件是否合法。
错误信息的key:required
、invalid
。
FloatField:
用来接收浮点类型,并且如果验证通过后,会将这个字段的值转换为浮点类型。
参数:
错误信息的key:required
、invalid
、max_value
、min_value
。
IntegerField:
用来接收整形,并且验证通过后,会将这个字段的值转换为整形。
参数:
错误信息的key:required
、invalid
、max_value
、min_value
。
URLField:
用来接收url
格式的字符串。
错误信息的key:required
、invalid
。
在验证某个字段的时候,可以传递一个validators
参数用来指定验证器,进一步对数据进行过滤。验证器有很多,但是很多验证器我们其实已经通过这个Field
或者一些参数就可以指定了。比如EmailValidator
,我们可以通过EmailField
来指定,比如MaxValueValidator
,我们可以通过max_value
参数来指定。以下是一些常用的验证器:
MaxValueValidator
:验证最大值。
MinValueValidator
:验证最小值。
MinLengthValidator
:验证最小长度。
MaxLengthValidator
:验证最大长度。
EmailValidator
:验证是否是邮箱格式。
URLValidator
:验证是否是URL
格式。
RegexValidator
:如果还需要更加复杂的验证,那么我们可以通过正则表达式的验证器:
RegexValidator
。比如现在要验证手机号码是否合格,那么我们可以通过以下代码实现:
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='请输入正确格式的手机号码!')])
注意变量的统一
forms.py
telephone = forms.CharField(validators=[validators.RegexValidator(r'1[3456789]\d{9}',message="请输入正确的手机号")],error_messages={"required":"请输入您的手机号"})
views.py
def post(self,request):
form = MyForm(request.POST)
if form.is_valid():
telephone = form.cleaned_data.get('telephone')
return HttpResponse("成功")
else:
print(form.errors.get_json_data())
return HttpResponse('fail')
index.html
三个telephone 必须要 统一
有时候对一个字段验证,不是一个长度,一个正则表达式能够写清楚的,还需要一些其他复杂的逻辑,那么我们可以对某个字段,进行自定义的验证。比如在注册的表单验证中,我们想要验证手机号码是否已经被注册过了,那么这时候就需要在数据库中进行判断才知道。对某个字段进行自定义的验证方式是,定义一个方法,这个方法的名字定义规则是:clean_fieldname
。如果验证失败,那么就抛出一个验证错误。比如要验证用户表中手机号码之前是否在数据库中存在,那么可以通过以下代码实现:
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='请输入正确格式的手机号码!')])
def clean_telephone(self):
telephone = self.cleaned_data.get('telephone')
exists = User.objects.filter(telephone=telephone).exists()
if exists:
raise forms.ValidationError("手机号码已经存在!")
return telephone
以上是对某个字段进行验证,如果验证数据的时候,需要针对多个字段进行验证,那么可以重写clean
方法。比如要在注册的时候,要判断提交的两个密码是否相等。那么可以使用以下代码来完成:
class MyForm(forms.Form):
telephone = forms.CharField(validators=[validators.RegexValidator("1[345678]\d{9}",message='请输入正确格式的手机号码!')])
pwd1 = forms.CharField(max_length=12)
pwd2 = forms.CharField(max_length=12)
def clean(self):
cleaned_data = super().clean()
pwd1 = cleaned_data.get('pwd1')
pwd2 = cleaned_data.get('pwd2')
if pwd1 != pwd2:
raise forms.ValidationError('两个密码不一致!')
文件上传是网站开发中非常常见的功能。这里详细讲述如何在Django
中实现文件的上传功能。
form
标签,然后在这个form
标签中指定enctype="multipart/form-data"
,不然就不能上传文件。form
标签中添加一个input
标签,然后指定input
标签的name
,以及type="file"
。以上两步的示例代码如下:
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="myfile">
form>
后端的主要工作是接收文件。然后存储文件。接收文件的方式跟接收POST
的方式是一样的,只不过是通过FILES
来实现。示例代码如下:
def save_file(file):
with open('somefile.txt','wb') as fp:
for chunk in file.chunks():
fp.write(chunk)
def index(request):
if request.method == 'GET':
form = MyForm()
return render(request,'index.html',{'form':form})
else:
myfile = request.FILES.get('myfile')
save_file(myfile)
return HttpResponse('success')
以上代码通过request.FILES
接收到文件后,再写入到指定的地方。这样就可以完成一个文件的上传功能了。
在定义模型的时候,我们可以给存储文件的字段指定为FileField
,这个Field
可以传递一个upload_to
参数,用来指定上传上来的文件保存到哪里。比如我们让他保存到项目的files
文件夹下,那么示例代码如下:
# models.py
from django.db import models
class Upfile_modles(models.Model):
title=models.CharField(max_length=100)
content=models.CharField(max_length=100)
thumbnail=models.FileField(upload_to='upfiles/%Y/%m/%d/')
class Meta:
db_table = 'Upfile'
#forms.py
from django import forms
from .models import Upfile_modles
class Upfile_forms(forms.ModelForm):
class Meta:
model=Upfile_modles
fields='__all__'
error_messages = { # 错误信息
"thumbnail": {
# 'invalid_extension':'请上传正确格式的文件'
'required': '请上传正确格式的图片'
}
}
# views.py
from django.shortcuts import render,HttpResponse
from django.views import View
from .forms import Upfile_forms
class Upload_file(View):
def get(self,request):
return render(request,'Upload_file.html')
def post(self,request):
form=Upfile_forms(request.POST,request.FILES)
if form.is_valid():
form.save()
return HttpResponse('成功了 成功')
else:
print(form.errors.get_json_data())
return HttpResponse('失败了')
def Upload_file_def(request):
if request.method=="GET":
return render(request, 'Upload_file.html')
elif request.method=="POST":
form=Upfile_forms(request.POST,request.FILES)
if form.is_valid():
form.save()
return HttpResponse("成功了,上传")
else:
print(form.errors.get_json_data())
return HttpResponse("上传失败")
调用完form.save()方法,就会把文件保存到
files`下面,并且会将这个文件的路径存储到数据库中。
#在seetings.py最下面添加上
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
#在models.py
thumbnail = models.FileField()#默认在主目录:media
thumbnail = models.FileField(upload_to="files/") #默认在主目录 media/files
#未在在seetings.py最下面添加上
thumbnail = models.FileField(upload_to="files/")#默认在主目录:files
然后我们可以在urls.py
中添加MEDIA_ROOT
目录下的访问路径。示例代码如下:
http://127.0.0.1:9000/media/upfiles/django%E9%85%8D%E7%BD%AE.txt从数据库中读取文件的url 然后在页面上能访问
#在主settings.py中 写入
MEDIA_ROOT = os.path.join(BASE_DIR,'media')
MEDIA_URL = '/media/'
#然后在主urls.py中 映射 的
from django.contrib import admin
from django.urls import path,include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('doc/',include('Uploading_documents.urls'),name='doc'),
path('img/',include('Uploading_image.urls'),name='img' ),
]+ static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT) #全局路径能访问上传的文件
如果我们同时指定MEDIA_ROOT
和upload_to
,那么会将文件上传到MEDIA_ROOT
下的upload_to
文件夹中。示例代码如下:
# models.py
from django.db import models
class Upfile_modles(models.Model):
title=models.CharField(max_length=100)
content=models.CharField(max_length=100)
thumbnail=models.FileField(upload_to='upfiles/%Y/%m/%d/')
class Meta:
db_table = 'Upfile'
如果想要限制上传的文件的拓展名,那么我们就需要用到表单来进行限制。我们可以使用普通的Form
表单,也可以使用ModelForm
,直接从模型中读取字段。示例代码如下:
# models.py
from django.db import models
from django.core import validators
class Upfile_modles(models.Model):
title=models.CharField(max_length=100)
content=models.CharField(max_length=100)
thumbnail=models.FileField(upload_to='upfiles/%Y/%m/%d/',validators=[validators.FileExtensionValidator(['txt','pdf'])])
class Meta:
db_table = 'Upfile'
上传图片跟上传普通文件是一样的。只不过是上传图片的时候Django
会判断上传的文件是否是图片的格式(除了判断后缀名,还会判断是否是可用的图片)。如果不是,那么就会验证失败。我们首先先来定义一个包含ImageField
的模型。示例代码如下:
#前端页面和上传文件相同
# models.py
from django.db import models
class upfile_img(models.Model):
title=models.CharField(max_length=100)
content=models.CharField(max_length=100)
thumbnail=models.ImageField(upload_to="img/%Y/%m/%d")
class Meta:
db_table='upimg'
#forms.py
from django import forms
from .models import upfile_img
class upimg_form(forms.ModelForm):
class Meta:
model=upfile_img
fields="__all__"
error_messages={
"thumbnail":{
'invalid_image':"请上传图片"
}
}
# views.py
from django.shortcuts import render,HttpResponse
from django.views import View
from .forms import upimg_form
class Upfile_img(View):
def get(self,request):
return render(request,'Upload_img.html')
def post(self,request,*args,**kwargs):
form=upimg_form(request.POST,request.FILES)
if form.is_valid():
form.save()
return HttpResponse("成功")
else:
print(form.errors.get_json_data())
return HttpResponse("失败")
注意:使用ImageField,必须要先安装Pillow库:pip install pillow
cookie
的出现就是为了解决这个问题,第一次登录后服务器返回一些数据(cookie)给浏览器,然后浏览器保存在本地,当该用户发送第二次请求的时候,就会自动的把上次请求存储的cookie
数据自动的携带给服务器,服务器通过浏览器携带的数据就能判断当前用户是哪个了。cookie
存储的数据量有限,不同的浏览器有不同的存储大小,但一般不超过4KB。因此使用cookie
只能存储一些小量的数据。cookie
是存储在本地浏览器,session
是一个思路、一个概念、一个服务器存储授权信息的解决方案,不同的服务器,不同的框架,不同的语言有不同的实现。虽然实现不一样,但是他们的目的都是服务器为了方便存储数据的。session
的出现,是为了解决cookie
存储数据不安全的问题的。web
开发发展至今,cookie
和session
的使用已经出现了一些非常成熟的方案。在如今的市场或者企业里,一般有两种存储方式:
cookie
存储一个sessionid
,然后具体的数据则是保存在session
中。如果用户已经登录,则服务器会在cookie
中保存一个sessionid
,下次再次请求的时候,会把该sessionid
携带上来,服务器根据sessionid
在session
库中获取用户的session
数据。就能知道该用户到底是谁,以及之前保存的一些状态信息。这种专业术语叫做server side session
。Django
把session
信息默认存储到数据库中,当然也可以存储到其他地方,比如缓存中,文件系统中等。存储在服务器的数据会更加的安全,不容易被窃取。但存储在服务器也有一定的弊端,就是会占用服务器的资源,但现在服务器已经发展至今,一些session
信息还是绰绰有余的。session
数据加密,然后存储在cookie
中。这种专业术语叫做client side session
。flask
框架默认采用的就是这种方式,但是也可以替换成其他形式。设置cookie
是设置值给浏览器的。因此我们需要通过response
的对象来设置,设置cookie
可以通过response.set_cookie
来设置,这个方法的相关参数如下:
def set_cookie(self, key, value='', max_age=None, expires=None, path='/',
domain=None, secure=False, httponly=False):
key
:这个cookie
的key
。value
:这个cookie
的value
。max_age
:最长的生命周期。单位是秒。expires
:过期时间。跟max_age
是类似的,只不过这个参数需要传递一个具体的日期,比如datetime
或者是符合日期格式的字符串。如果同时设置了expires
和max_age
,那么将会使用expires
的值作为过期时间。默认是 navie时间 不知道位于哪个时区 可以将其转为aware时间 from django.utils.timezone import make_aware
expire_time = datetime(year=2018,month=9,day=25,hour=20,minute=30,second=0)
expire_time1 = make_aware(expire_time)
如果max_age
和 expires
同时存在 以 expires为准
user_id=kangbazi1806; expires=Tue, 25-Sep-2018 20:30:00 GMT; Max-Age=480381; Path=/cms/
path
:对域名下哪个路径有效。默认是对域名下所有路径都有效。 上面 只对 /cms/路径有效
domain
:针对哪个域名有效。默认是针对主域名下都有效,如果只要针对某个子域名才有效,那么可以设置这个属性.
secure
:是否是安全的,如果设置为True
,那么只能在https
协议下才可用。默认 secure
为false 也就是 http协议能用
httponly
:默认是False
。如果为True
,那么在客户端不能通过JavaScript
进行操作。
通过delete_cookie
即可删除cookie
。实际上删除cookie
就是将指定的cookie
的值设置为空的字符串,然后使用将他的过期时间设置为0
,也就是浏览器关闭后就过期。
获取浏览器发送过来的cookie
信息。可以通过request.COOKIES
来或者。这个对象是一个字典类型。比如获取所有的cookie
,那么示例代码如下:
cookies = request.COOKIES
for cookie_key,cookie_value in cookies.items():
print(cookie_key,cookie_value)
django
中的session
默认情况下是存储在服务器的数据库中的,在表中会根据sessionid
来提取指定的session
数据,然后再把这个sessionid
放到cookie
中发送给浏览器存储,浏览器下次在向服务器发送请求的时候会自动的把所有cookie
信息都发送给服务器,服务器再从cookie
中获取sessionid
,然后再从数据库中获取session
数据。但是我们在操作session
的时候,这些细节压根就不用管。我们只需要通过request.session
即可操作。示例代码如下:
def index(request):
request.session.get('username')
return HttpResponse('index')
def session_view(request):
request.session['username'] = "kangbazi1806" #如果登录 验证通过 同时将信息存到session中 一份
request.session['userid'] = '1806'
# username = request.session.pop("username")
# userid = request.session.pop("userid")
request.session.clear()
request.session.flush()
username = request.session.get("username")
print(username)
# expire = datetime(year=2018,month=9,day=25,hour=20,minute=30,second=0)
# expires = make_aware(expire)
# request.session.set_expiry(expires)
return HttpResponse("session view")
session
常用的方法如下:
get
:用来从session
中获取指定值。pop
:从session
中删除一个值。keys
:从session
中获取所有的键。items
:从session
中获取所有的值。clear
:清除当前这个用户的session
数据。flush
:删除session
并且删除在浏览器中存储的session_id
,一般在注销的时候用得比较多。set_expiry(value)
:设置过期时间。
0
:代表只要浏览器关闭,session
就会过期。None
:会使用全局的session
配置。在settings.py
中可以设置SESSION_COOKIE_AGE
来配置全局的过期时间。默认是1209600
秒,也就是2周的时间。clear_expired
:清除过期的session
。Django
并不会清除过期的session
,需要定期手动的清理,或者是在终端,使用命令行python manage.py clearsessions
来清除过期的session
。最后一行 加入 SESSION_ENGINE = “django.contrib.sessions.backends.cache”
默认情况下,session
数据是存储到数据库中的。当然也可以将session
数据存储到其他地方。可以通过设置SESSION_ENGINE
来更改session
的存储位置,这个可以配置为以下几种方案:
django.contrib.sessions.backends.db
:使用数据库。默认就是这种方案。django_sessiondjango.contrib.sessions.backends.file
:使用文件来存储session。django.contrib.sessions.backends.cache
:使用缓存来存储session。想要将数据存储到缓存中,前提是你必须要在settings.py
中配置好CACHES
,并且是需要使用Memcached
,而不能使用纯内存作为缓存。django.contrib.sessions.backends.cached_db
:在存储数据的时候,会将数据先存到缓存中,再存到数据库中。这样就可以保证万一缓存系统出现问题,session数据也不会丢失。在获取数据的时候,会先从缓存中获取,如果缓存中没有,那么就会从数据库中获取。django.contrib.sessions.backends.signed_cookies
:将session
信息加密后存储到浏览器的cookie
中。这种方式要注意安全,建议设置SESSION_COOKIE_HTTPONLY=True
,那么在浏览器中不能通过js
来操作session
数据,并且还需要对settings.py
中的SECRET_KEY
进行保密,因为一旦别人知道这个SECRET_KEY
,那么就可以进行解密。另外还有就是在cookie
中,存储的数据不能超过4k
。上下文处理器是可以返回一些数据,在全局模板中都可以使用。比如登录后的用户信息,在很多页面中都需要使用,那么我们可以放在上下文处理器中,就没有必要在每个视图函数中都返回这个对象。
在settings.TEMPLATES.OPTIONS.context_processors
中,有许多内置的上下文处理器。这些上下文处理器的作用如下:
django.template.context_processors.debug
:增加一个debug
和sql_queries
变量。在模板中可以通过他来查看到一些数据库查询。
现在 settings.py中设置 INTERNAL_IPS = ['127.0.0.1']
在页面 上 {{debug}} 或者 {{sql_queries}}
首页 True ::::[{'sql': 'SELECT "front_user"."id", "front_user"."username", "front_user"."password", "front_user"."telephone" FROM "front_user"', 'time': '0.001'}]
django.template.context_processors.request
:增加一个request
变量。这个request
变量也就是在视图函数的第一个参数。
django.contrib.auth.context_processors.auth
:Django
有内置的用户系统,这个上下文处理器会增加一个user
对象。
django.contrib.messages.context_processors.messages
:增加一个messages
变量。
level的四个级别 info debug Warnning erro
如果想让 错误信息直接呈现在页面上
views.py
from django.contrib import messages #先引入
class LoginView(View):
def get(self,request):
return render(request,'login.html')
def post(self,request):
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = User.objects.filter(username=username,password=password).first()
if user:
request.session['user_id'] = user.id #登录成功以后 将这个用户的id存到session
return redirect(reverse('index'))
else:
print("用户名或者密码错误")
# messages.add_message(request,messages.INFO,"用户名或者密码错误")
messages.info(request, "用户名或者密码错误") #因为内置上下文处理器 上面方式效果一样 这是 用户名或者密码错误级别
return redirect(reverse('login'))
外边是 用户名 密码 不能少于6位 错误级别 这个最开始是交给 forms.py来做的
所以需要在里边 写一个 方法
else:
# errors = form.errors.get_json_data()
# #'username': [{'message': '用户名不能少于6位', 'code': 'min_length'}], 'password': [{'message': '密码不能少于6位', 'code': 'min_length'}]}
# for messages1 in errors.values():
# #print(messages1)
# #[{'message': '用户名不能少于6位', 'code': 'min_length'}]
# #[{'message': '密码不能少于6位', 'code': 'min_length'}]
# for messages_dict in messages1:
# for key,value in messages_dict.items():
# if key == "message":
# print(value)
erros = form.get_errors() #这里调用的是 下面forms.py中的方法
for erro in erros:
messages.info(request,erro)
return redirect(reverse('login'))
forms.py 最终我们只想输出 用户名不能少于 6位用户名不能少于6位
def get_errors(self):
new_error = []
errors = self.errors.get_json_data()
# 'username': [{'message': '用户名不能少于6位', 'code': 'min_length'}], 'password': [{'message': '密码不能少于6位', 'code': 'min_length'}]}
for messages1 in errors.values():
# print(messages1)
# [{'message': '用户名不能少于6位', 'code': 'min_length'}]
# [{'message': '用户名不能少于6位', 'code': 'min_length'}]
for messages_dict in messages1:
for key, value in messages_dict.items():
if key == "message":
new_error.append(value)
return new_error
在 login.html 上
{% for message in messages %}
- {{ message }}
{% endfor %}
django.template.context_processors.media:在模板中可以读取 MEDIA_URL。比如想要在模板中使用上传的文件,那么这时候就需要使用settings.py中设置的MEDIA_URL来拼接url。示例代码如下:
1.在 settings.py OPTIONS 中 写入 'django.template.context_processors.media',
然后制定
MEDIA_ROOT = os.path.join(BASE_DIR,'medias')
MEDIA_URL = '/medias/'
urls.py 映射
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('', views.index,name='index'),
]+ static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
<img src="{{MEDIA_URL}}aa.png" />
django.template.context_processors.static
:在模板中可以使用STATIC_URL
。
django.template.context_processors.csrf
:在模板中可以使用csrf_token
变量来生成一个csrf token
。
如果表单 post请求 在 form标签里边 加入下面两个 两个效果都可以
{# #}
{% csrf_token %}
如果 不是 表单
可以在 head 上面 加一个
{% block head %}
{% endblock %}
base页面上 得有
{% block head %}
{% endblock %}
有时候我们想要返回自己的数据。那么这时候我们可以自定义上下文处理器。自定义上下文处理器的步骤如下:
你可以根据这个上下文处理器是属于哪个app
,然后在这个app
中创建一个文件专门用来存储上下文处理器。比如context_processors.py
。(或者是你也可以专门创建一个Python包
,用来存储所有的上下文处理器。没讲到)
在你定义的上下文处理器文件中,定义一个函数,这个函数只有一个request参数。这个函数中处理完自己的逻辑后,把需要返回给模板的数据,通过字典的形式返回。如果不需要返回任何数据,那么也必须返回一个空的字典。示例代码如下:
在settings.py下 让 系统识别你写的 context_processors.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',
'front.context_processors.front_user' #这一步就是让context_processors 生效
],
},
},
]
from .models import User
def front_user(request):
userid = request.session.get('user_id') #从session中读取user_id
context = {}
if userid:
try:
user = User.objects.get(pk=userid)
context['front_user'] = user
except:
pass
return context
在 页面上
{% if front_user %}
<li><a href="#">欢迎你,{{ front_user.username }}</a></li>
<li><a href="#">注销</a></li>
{% else %}
<li><a href="{% url 'login' %}">登录</a></li>
<li><a href="{% url 'register' %}">注册</a></li>
{% endif %}
memcached
之前是danga
的一个项目,最早是为LiveJournal服务的,当初设计师为了加速LiveJournal访问速度而开发的,后来被很多大型项目采用。官网是www.danga.com
或者是memcached.org
。Memcached
是一个高性能的分布式的内存对象缓存系统,全世界有不少公司采用这个缓存项目来构建大负载的网站,来分担数据库的压力。Memcached
是通过在内存里维护一个统一的巨大的hash表,memcached
能存储各种各样的数据,包括图像、视频、文件、以及数据库检索的结果等。简单的说就是将数据调用到内存中,然后从内存中读取,从而大大提高读取速度。Memcached
:存储验证码(图形验证码、短信验证码)、登录session等所有不是至关重要的数据。memcached
:windows:
memcached.exe -d install
。memcached.exe -d start
。linux(ubuntu):
安装:sudo apt install memcached
启动:
cd /usr/local/memcached/bin
./memcached -d start
可能出现的问题:
pthreadGC2.dll
文件:将pthreadGC2.dll
文件拷贝到windows/System32
.启动memcached
:
-d
:这个参数是让memcached
在后台运行。-m
:指定占用多少内存。以M
为单位,默认为64M
。-p
:指定占用的端口。默认端口是11211
。-l
:别的机器可以通过哪个ip地址连接到我这台服务器。如果是通过service memcached start
的方式,那么只能通过本机连接。如果想要让别的机器连接,就必须设置-l 0.0.0.0
。如果想要使用以上参数来指定一些配置信息,那么不能使用service memcached start
,而应该使用/usr/bin/memcached
的方式来运行。比如/usr/bin/memcached -u memcache -m 1024 -p 11222 start
。
telnet
操作memcached
:telnet ip地址 [11211]
添加数据:
set
:
语法:
set key flas(是否压缩) timeout value_length
value
示例:
set username 0 60 7
zhiliao
add
:
语法:
add key flas(0) timeout value_length
value
示例:
add username 0 60 7
xiaotuo
set
和add
的区别:add
是只负责添加数据,不会去修改数据。如果添加的数据的key
已经存在了,则添加失败,如果添加的key
不存在,则添加成功。而set
不同,如果memcached
中不存在相同的key
,则进行添加,如果存在,则替换。
获取数据:
语法:
get key
示例:
get username
删除数据:
语法:
delete key
示例:
delete username
flush_all
:删除memcached
中的所有数据。
查看memcached
的当前状态:
stats
。python
操作memcached
:安装:python-memcached
:pip install python-memcached
。
建立连接:
import memcache
mc = memcache.Client(['127.0.0.1:11211','192.168.174.130:11211'],debug=True)
设置数据:
mc.set('username','hello world',time=60*5)
mc.set_multi({'email':'[email protected]','telphone':'111111'},time=60*5)
获取数据:
mc.get('telphone')
删除数据:
mc.delete('email')
自增长:
mc.incr('read_count')
自减少:
mc.decr('read_count')
memcached
的操作不需要任何用户名和密码,只需要知道memcached
服务器的ip地址和端口号即可。因此memcached
使用的时候尤其要注意他的安全性。这里提供两种安全的解决方案。分别来进行讲解:
-l
参数设置为只有本地可以连接:这种方式,就只能通过本机才能连接,别的机器都不能访问,可以达到最好的安全性。11211
端口,外面也不能访问。 ufw enable # 开启防火墙
ufw disable # 关闭防火墙
ufw default deny # 防火墙以禁止的方式打开,默认是关闭那些没有开启的端口
ufw deny 端口号 # 关闭某个端口
ufw allow 端口号 # 开启某个端口
首先需要在settings.py
中配置好缓存:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
}
}
如果想要使用多台机器,那么可以在LOCATION
指定多个连接,示例代码如下:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': [
'172.19.26.240:11211',
'172.19.26.242:11211',
]
}
}
配置好memcached
的缓存后,以后在代码中就可以使用以下代码来操作memcached
了:
from django.core.cache import cache
def index(request):
cache.set('abc','zhiliao',60)
print(cache.get('abc'))
response = HttpResponse('index')
return response
需要注意的是,django
在存储数据到memcached
中的时候,不会将指定的key
存储进去,而是会对key
进行一些处理。比如会加一个前缀,会加一个版本号。如果想要自己加前缀,那么可以在settings.CACHES
中添加KEY_FUNCTION
参数:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
'LOCATION': '127.0.0.1:11211',
'KEY_FUNCTION': lambda key,prefix_key,version:"django:%s"%key
}
}
nvm->node.js -> npm
nvm(Node Version Manager)
是一个用来管理node
版本的工具。我们之所以需要使用node
,是因为我们需要使用node
中的npm(Node Package Manager)
,使用npm
的目的是为了能够方便的管理一些前端开发的包!nvm
的安装非常简单,步骤如下:
nvm
的安装包:https://github.com/coreybutler/nvm-windows/releases
。我的电脑->属性->高级系统设置->环境变量->系统环境变量->Path
下新建一个,把nvm
所处的路径填入进去即可!cmd
,然后输入nvm
,如果没有提示没有找不到这个命令。说明已经安装成功!Mac
或者Linux
安装nvm
请看这里:https://github.com/creationix/nvm
。也要记得配置环境变量。nvm
常用命令:
nvm install node
:默认安装最新版的node.js
。nvm i == nvm install。nvm install [version]
:安装指定版本的node.js
。nvm use [version]
:使用某个版本的node
。nvm list
:列出当前安装了哪些版本的node
。nvm uninstall [version]
:卸载指定版本的node
。nvm node_mirror [url]
:设置nvm
的镜像。nvm npm_mirror [url]
:设置npm
的镜像。安装完nvm
后,我们就可以通过nvm
来安装node
了。这里我们安装6.4.0
版本的的node.js
就可以。因为最新版的node.js
的npm
是5.0
的,上面还有很多坑。安装命令如下:
nvm install 6.4.0
如果你的网络够快,那以上命令在稍等片刻之后会安装成功。如果你的网速很慢,那以上命令可能会发生超时。因为node
的服务器地址是https://nodejs.org/dist/
,这个域名的服务器是在国外。因此会比较慢。因此我们可以设置一下nvm
的源。
nvm node_mirror https://npm.taobao.org/mirrors/node/
nvm npm_mirror https://npm.taobao.org/mirrors/npm/
npm(Node Package Manager)
在安装node
的时候就会自动的安装了。当时前提条件是你需要设置当前的node
的版本:nvm use 6.4.0
。然后就可以使用npm
了.
关于npm
常用命令以及用法,请看下文。
安装包分为全局安装和本地安装。全局安装是安装在当前node
环境中,在可以在cmd中当作命令使用。而本地安装是安装在当前项目中,只有当前这个项目能使用,并且可以通过require引用。安装的方式只有-g
参数的区别:npm install express
npm install express # 本地安装(在打算上线的系统文件主目录里安装)
npm install express -g # 全局安装(接着上面的安装)
./node_modules
下(运行 npm 命令时所在的目录),如果没有node_modules
目录,会在当前执行npm
命令的目录下生成node_modules
目录。require()
来引入本地安装的包。/usr/local
下或者你node
的安装目录。npm uninstall [package]
npm update [package]
npm search [package]
npm install -g cnpm --registry=https://registry.npm.taobao.org那么以后就可以使用cnpm来安装包了!
cnpm install
update
uninstall
search
1》npm install express
2》npm install -g cnpm --registry=https://registry.npm.taobao.org(非必须,使用国内源,加速)
3》npm install gulp --save-dev #本地项目中安装glup
4》npm init #命令在本地生成一个`package.json`文件
5》cnpm install #自动安装`package.json`下`devDependencies`中指定的依赖包
前端优化:
1.减少http请求
2.雪碧图 并且压缩图片
3.合并css js文件并且压缩
前端我们使用gulp
来自动化开发流程。配置好gulp
后,可以自动给我们处理好一些工作。比如写完css
后,要压缩成.min.css
,写完js
后,要做混淆和压缩,图片压缩等。这些工作都可以让gulp
帮我们完成。
使用npm init
命令在本地生成一个package.json
文件,package.json
是用来记录你当前这个项目依赖了哪些包,以后别人拿到你这个项目后,不需要你的node_modules
文件夹(因为node_moduels中的包实在太庞大了)。只需要执行npm install
命令,即会自动安装package.json
下devDependencies
中指定的依赖包。
gulp
的安装非常简单,只要使用npm
命令安装即可。但是因为gulp
需要作为命令行的方式运行,因此需要在安装在系统级别的目录中。
npm install gulp -g
因为在本地需要使用require
的方式gulp
。因此也需要在本地安装一份:
切换到项目目录
npm install gulp --save-dev
以上的--save-dev
是将安装的包的添加到package.json
下的devDependencies
依赖中。以后通过npm install
即可自动安装。devDependencies
这个是用来记录开发环境下使用的包,如果想要记录生产环境下使用的包,那么在安装包的时候使用npm install xx --save
就会记录到package.json
下的dependencies
中,dependencies
是专门用来记录生产环境下的依赖包的!
要使用gulp
来流程化我们的开发工作。首先需要在项目的根目录下创建一个gulpfile.js
文件。然后在gulpfile.js
中填入以下代码:
var gulp = require("gulp") #引入gulp
gulp.task("kangbazi",function () {
console.log('hello world');
});
这里对代码进行一一解释:
通过require
语句引用已经安装的第三方依赖包。这个require
只能是引用当前项目的,不能引用全局下的。require
语法是node.js
独有的,只能在node.js
环境下使用。
gulp.task
是用来创建一个任务。gulp.task
的第一个参数是命令的名字,第二个参数是一个函数,就是执行这个命令的时候会做什么事情,都是写在这个里面的。
写完以上代码后,以后如果想要执行greet
命令,那么只需要进入到项目所在的路径,然后终端使用
gulp kangbazi
即可执行。
gulp
只是提供一个框架给我们。如果我们想要实现一些更加复杂的功能,比如css
压缩,那么我们还需要安装一下gulp-cssnano
插件。gulp
相关的插件安装也是通过npm
命令安装,安装方式跟其他包是一模一样的(gulp插件本身就是一个普通的包)。
对css
文件的处理,需要做的事情就是压缩,然后再将压缩后的文件放到指定目录下(不要和原来css文件重合了)!这里我们使用gulp-cssnano
来处理这个工作:
npm install gulp-cssnano --save-dev
然后在gulpfile.js
中写入以下代码:
var gulp = require("gulp")
var cssnano = require("gulp-cssnano")
// 定义一个处理css文件改动的任务
gulp.task("css",function () {
gulp.src("./css/*.css")
.pipe(cssnano())
.pipe(gulp.dest("./css/dist/"))
});
以上对代码进行详细解释:
gulp.task
:创建一个css
处理的任务。gulp.src
:找到当前css
目录下所有以.css
结尾的css
文件。pipe
:管道方法。将上一个方法的返回结果传给另外一个处理器。比如以上的cssnano
。gulp.dest
:将处理完后的文件,放到指定的目录下。不要放在和原文件相同的目录,以免产生冲突,也不方便管理。像以上任务,压缩完css
文件后,最好是给他添加一个.min.css
的后缀,这样一眼就能知道这个是经过压缩后的文件。这时候我们就需要使用gulp-rename
来修改了。当然首先也需要安装npm install gulp-rename --save-dev
。示例代码如下:
var gulp = require("gulp")
var cssnano = require("gulp-cssnano")
var rename = require("gulp-rename")
gulp.task("css",function () {
gulp.src("./css/*.css")
.pipe(cssnano())
.pipe(rename({"suffix":".min"}))
.pipe(gulp.dest("./css/dist/"))
});
在上述代码中,我们增加了一行.pipe(rename({"suffix":".min"}))
,这个我们就是使用rename
方法,并且传递一个对象参数,指定修改名字的规则为添加一个.min
后缀名。这个gulp-rename
还有其他的指定文件名的方式,比如可以在文件名前加个前缀等。更多的教程可以看这个:https://www.npmjs.com/package/gulp-rename
。
处理js
文件,我们需要使用到gulp-uglify
插件。安装命令如下:
npm install gulp-uglify --save-dev
安装完后,我们就可以对js
文件进行处理了。示例代码如下:
var gulp = require("gulp")
var rename = require("gulp-rename")
var uglify = require('gulp-uglify');
gulp.task('script',function(){
gulp.src("./js/*.js")
.pipe(uglify())
.pipe(rename({suffix:'.min'})) #这个是 压缩后 自动在 css前面加min
.pipe(gulp.dest('js/')); #压缩以后放到指定的目录下面
});
这里就是增加了一个.pipe(uglify())
的处理,对js
文件进行压缩和丑化(修改变量名)等处理。更多关于gulp-uglify
的教程。请看:https://github.com/mishoo/UglifyJS2#minify-options
。
在网页开发中,为了加快网页的渲染速度,有时候我们会将多个文件压缩成一个文件,从而减少请求的次数。要拼接文件,我们需要用到gulp-concat
插件。安装命令如下:
npm install gulp-concat --save-dev
比如我们现在有一个nav.js
文件用来控制导航条的。有一个index.js
文件用来控制首页整体内容的。那么我们可以使用以下代码将这两个文件合并成一个文件:
var gulp = require('gulp');
var concat = require('gulp-concat');
var uglify = require('gulp-uglify');
gulp.task('vendorjs',function(){
gulp.src([
'./js/nav.js',
'./js/index.js' #指定目标文件进行压缩
])
.pipe(concat('index.min.js'))
.pipe(uglify())
.pipe(gulp.dest('dist/js/'));
});
图片是限制网站加载速度的一个主要原因。图片越大,从网站上下载所花费的时间越长。因此对于一些图片,我们可以采取无损压缩,即在不改变图片质量的基础之上进行压缩。在gulp
中我们可以通过gulp-imagemin
来帮我们实现。安装命令如下:
npm install gulp-imagemin --save-dev
压缩图片也是一个比较大的工作量,对于一些已经压缩过的图片,我们就没必要再重复压缩了。这时候我们可以使用gulp-cache
来缓存那些压缩过的图片。安装命令如下:
npm install gulp-cache --save-dev
两个插件结合使用的代码如下:
var imagemin = require('gulp-imagemin');
var cache = require('gulp-cache');
gulp.task('image',function(){
gulp.src("./images/*.*")
.pipe(cache(imagemin()))
.pipe(gulp.dest('dist/images/'));
});
以上所有的任务,我们都是需要手动的在终端去执行。这样很不方便我们开发。最好的方式就是我修改了代码后,gulp
会自动的执行相应的任务。这个工作我们可以使用gulp
内置的watch
方法帮我们完成:
var gulp = require("gulp")
var cssnano = require("gulp-cssnano")
var rename = require("gulp-rename")
// 定义一个处理css文件改动的任务
gulp.task("css",function () {
gulp.src("./css/*.css")
.pipe(cssnano())
.pipe(rename({"suffix":".min"}))
.pipe(gulp.dest("./css/dist/"))
.pipe(connect.reload())
});
// 定义一个监听的任务
gulp.task("watch",function () {
// 监听所有的css文件,然后执行css这个任务
gulp.watch("./css/*.css",['css'])
});
以后只要在终端执行gulp watch
命令即可自动监听所有的css
文件,然后自动执行css
的任务,完成相应的工作。
以上我们实现了更改一些css
文件后,可以自动执行处理css
的任务。但是我们还是需要手动的去刷新浏览器,才能看到修改后的效果。有什么办法能在修改完代码后,自动的刷新浏览器呢。答案是使用browser-sync
。browser-sync
安装的命令如下:
cnpm install browser-sync --save-dev
browser-sync
使用的示例代码如下:
var gulp = require("gulp")
var cssnano = require("gulp-cssnano")
var rename = require("gulp-rename")
var bs = require("browser-sync").create()
gulp.task("bs",function () {
bs.init({
'server': {
'baseDir': './'
}
});
});
// 定义一个处理css文件改动的任务
gulp.task("css",function () {
gulp.src("./css/*.css")
.pipe(cssnano())
.pipe(rename({"suffix":".min"}))
.pipe(gulp.dest("./css/dist/"))
.pipe(bs.stream())
});
// 定义一个监听的任务
gulp.task("watch",function () {
gulp.watch("./css/*.css",['css'])
});
// 执行gulp server开启服务器
gulp.task("server",['bs','watch'])
以上我们创建了一个bs
的任务,这个任务会开启一个3000
端口,以后我们在访问html
页面的时候,就需要通过http://127.0.0.1:3000
的方式来访问了。然后接下来我们还定义了一个server
任务。这个任务会去执行bs
和watch
任务,只要修改了css
文件,那么就会执行css
的任务,然后就会自动刷新浏览器。
browser-sync
更多的教程请参考:http://www.browsersync.cn/docs/gulp/
。
众所周知,css
不是一门编程语言。他没法像js
和python
那样拥有逻辑处理的能力,甚至导入其他的css
文件中的样式都做不到。而Sass
就是为了解决css
的这些问题。他它允许你使用变量、嵌套规则、 mixins
、导入等众多功能,并且完全兼容css
语法。Sass
文件不能直接被网页所识别,写完Sass
后,还需要专门的工具转化为css
才能使用。
Sass
文件有两种后缀名,一个是scss
,一个是sass
。不同的后缀名,相应的语法也不一样。这里我们使用scss
的后缀名。包括后面讲到的Sass
语法,也都是scss
的后缀名的语法。
将Sass
文件转换为css
文件的工具有很多。这里我们就使用之前讲过的gulp
来实现。这里我们需要使用gulp-sass
插件来帮我们完成。安装方式非常简单:npm install gulp-sass --save-dev
。那么处理sass
的代码如下:
var gulp = require("gulp");
var sass = require("gulp-sass");
// 处理css的任务
gulp.task('css',function () {
gulp.src(path.css + '*.scss')
.pipe(sass().on("error",sass.logError))
.pipe(cssnano())
.pipe(rename({"suffix":".min"}))
.pipe(gulp.dest(path.css_dist))
});
支持/* comment */
和// 注释
两种方式。
Sass
语法允许嵌套。比如#main
下有一个类为.header
,那么我们可以写成以下的形式:
#main{
background: #ccc;
.header{
width: 20px;
height: 20px;
}
}
这样写起来更加的直观。一看就知道.header
是在#main
下的。
&
)有时候,在嵌套的子选择器中,需要使用父选择器,那么这时候可以通过&
来表示。示例代码如下:
a{
font-weight: bold;
text-decoration: none;
&:hover{
color: #888;
}
}
是的,你没听错。在Sass
中可以定义变量。对于一些比较常用的值,我们可以通过变量存储起来,以后想要使用的时候就直接用就可以了。定义变量使用$
符号。示例代码如下:
$mainWidth: 980px;
#main{
width: $mainWidth;
}
在Sass
中支持运算。比如现在有一个容器总宽度是900
,要在里面平均放三个盒子,那么我们可以通过变量来设置他们的宽度。示例代码如下:
$mainWidth: 900px;
.box{
width: $mainWidth/3;
}
在css
中@import
只能导入css
文件,而且对网站性能有很大的影响。而Sass
中的@import
则是完全实现了一套自己的机制。他可以直接将指定文件的代码拷贝到导入的地方。示例代码如下:
@import "init.scss";
有时候我们一个选择器中,可能会需要另外一个选择器的样式,那么我们就可以通过extend
来直接将指定选择器的样式加入进来。示例代码如下:
.error{
background-color: #fdd;
border: 1px solid #f00;
}
.serious-error{
@extend .error;
border-width: 3px;
}
有时候一段样式代码。我们可能要用很多地方。那么我们可以把他定义i成mixin
。需要用的时候就直接引用就可以了。示例代码如下:
@mixin large-text {
font: {
family: Arial;
size: 20px;
weight: bold;
}
color: #ff0000;
}
如果其他地方想要使用这个mixin
的时候,可以通过@include
来包含进来。示例代码如下:
.page-title {
@include large-text;
padding: 4px;
margin-top: 10px;
}
@mixin
也可以使用参数。示例代码如下:
@mixin sexy-border($color, $width) {
border: {
color: $color;
width: $width;
style: dashed;
}
}
那么以后在include
的时候,就需要传递参数了。示例代码如下:
p {
@include sexy-border(blue, 1px);
}
更详细的教程可以参考:http://sass.bootcss.com/docs/sass-reference/
。
User
模型是这个框架的核心部分。他的完整的路径是在django.contrib.auth.models.User
。以下对这个User
对象做一个简单了解:
内置的User
模型拥有以下的字段:
username
: 用户名。150个字符以内。可以包含数字和英文字符,以及_
、@
、+
、.
和-
字符。不能为空,且必须唯一!first_name
:歪果仁的first_name
,在30个字符以内。可以为空。last_name
:歪果仁的last_name
,在150个字符以内。可以为空。email
:邮箱。可以为空。password
:密码。经过哈希过后的密码。groups
:分组。一个用户可以属于多个分组,一个分组可以拥有多个用户。groups
这个字段是跟Group
的一个多对多的关系。user_permissions
:权限。一个用户可以拥有多个权限,一个权限可以被多个用户所有用。和Permission
属于一种多对多的关系。is_staff
:是否可以进入到admin
的站点。代表是否是员工。is_active
:是否是可用的。对于一些想要删除账号的数据,我们设置这个值为False
就可以了,而不是真正的从数据库中删除。is_superuser
:是否是超级管理员。如果是超级管理员,那么拥有整个网站的所有权限。last_login
:上次登录的时间。date_joined
:账号创建的时间。通过create_user
方法可以快速的创建用户。这个方法必须要传递username
、email
、password
。示例代码如下:
#views.py
from django.contrib.auth.models import User
user = User.objects.create_user('zhiliao','[email protected]','111111')
# 此时user对象已经存储到数据库中了。当然你还可以继续使用user对象进行一些修改
user.last_name = 'abc'
user.save()
创建超级用户有两种方式。第一种是使用代码的方式。用代码创建超级用户跟创建普通用户非常的类似,只不过是使用create_superuser
。示例代码如下:
#views.py
from django.contrib.auth.models import User
User.objects.create_superuser('admin','[email protected]','111111')
也可以通过命令行的方式。命令如下:
python manage.py createsuperuser
后面就会提示你输入用户名、邮箱以及密码。
因为密码是需要经过加密后才能存储进去的。所以如果想要修改密码,不能直接修改password
字段,而需要通过调用set_password
来达到修改密码的目的。示例代码如下:
#views.py
from django.contrib.auth.models import User
user = User.objects.get(pk=1)
user.set_password('新的密码')
user.save() #修改密码 别忘了 保存
Django
的验证系统已经帮我们实现了登录验证的功能。通过django.contrib.auth.authenticate
即可实现。这个方法只能通过username
和password
来进行验证。示例代码如下:
views.py
from django.contrib.auth import authenticate
user = authenticate(username='zhiliao', password='111111')
# 如果验证通过了,那么就会返回一个user对象。可以全局使用,类似于自定义上下文处理器
if user is not None:
# 执行验证通过后的代码
else:
# 执行验证没有通过的代码。
Django
内置的User
模型虽然已经足够强大了。但是有时候还是不能满足我们的需求。比如在验证用户登录的时候,他用的是用户名作为验证,而我们有时候需要通过手机号码或者邮箱来进行验证。还有比如我们想要增加一些新的字段。那么这时候我们就需要扩展用户模型了。扩展用户模型有多种方式。这里我们来一一讨论下。
如果你对Django
提供的字段,以及验证的方法都比较满意,没有什么需要改的。但是只是需要在他原有的基础之上增加一些操作的方法。那么建议使用这种方式。示例代码如下:
class Person(User):
telephone = models.CharField(max_length=11) #代理模型不能创建字段 这一步肯定报错
Person.objects.all() #跟
User.objects.all() #效果一样
class Meta:
proxy = True
@classmethod #这个叫做类方法
def get_blacklist(self):
return self.objects.filter(is_active=False)
views.py
def proxy_view(request):
# blacklists= Person.get_blacklist()#获取黑名单
# for blacklist in blacklists:
# print(blacklist.username)
# users = Person.objects.all()#获取所有用户
# for user in users:
# print(user.username)
stafflists = Person.get_stafflist()
for stafflist in stafflists:
print(stafflist.username)
return HttpResponse("成功")
在以上,我们定义了一个Person
类,让他继承自User
,并且在Meta
中设置proxy=True
,说明这个只是User
的一个代理模型。他并不会影响原来User
模型在数据库中表的结构。以后如果你想方便的获取所有黑名单的人,那么你就可以通过Person.get_blacklist()
就可以获取到。并且User.objects.all()
和Person.objects.all()
其实是等价的。因为他们都是从User
这个模型中获取所有的数据。
如果你对用户验证方法authenticate
没有其他要求,就是使用username
和password
即可完成。但是想要在原来模型的基础之上添加新的字段,那么可以使用一对一外键的方式。示例代码如下:User模型默认的验证是通过username和 password
#models.py
from django.contrib.auth.models import User
from django.db import models
from django.dispatch import receiver
from django.db.models.signals import post_save
class UserExtension(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,related_name='extension')
birthday = models.DateField(null=True,blank=True)
school = models.CharField(max_length=100) #别忘了映射(python menger.py XXX)
#reveiver 表示收到信号 如果信号是 User发过来的 save
@receiver(post_save,sender=User)
def create_user_extension(sender,instance,created,**kwargs):
if created:
UserExtension.objects.create(user=instance) #新建
else:
instance.extension.save() #删除和修改
#views.py
#传统的 authenticate 只能验证 username 和password 返回的是 user
#自定义方法 需要传两个参数 第一个参数 表示 telephone 第二个参数 密码
def my_authenticate(telephone,password):
user = User.objects.filter(extension__telephone=telephone).first()
#根据你传过来的 telephone从 User关联的表中查询 返回第一条结果
if user: #如果有
is_true = user.check_password(password)
if is_true:
return user
else:
return None
def one_view(request):
# user = User.objects.create_user(username="dingkou",email="[email protected]",password="sh1806")
# user.extension.telephone = '13888888888'
# user.save()
telephone = request.GET.get('telephone')
password = request.GET.get('password')
user = my_authenticate(telephone,password) #接收完用户的提交之后 然后 调用自定义的方法
if user:
print("验证成功 %s" % user.username)
else:
print("验证失败")
return HttpResponse("一对一扩展模型")
以上定义一个UserExtension
的模型,并且让她和User
模型进行一对一的绑定,以后我们新增的字段,就添加到UserExtension
上。并且还写了一个接受保存模型的信号处理方法,只要是User
调用了save
方法,那么就会创建一个UserExtension
和User
进行绑定。
AbstractUser
:对于authenticate
不满意,并且不想要修改原来User
对象上的一些字段,但是想要增加一些字段,那么这时候可以直接继承自django.contrib.auth.models.AbstractUser
,其实这个类也是django.contrib.auth.models.User
的父类。比如我们想要在原来User
模型的基础之上添加一个telephone
和school
字段。示例代码如下:
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
telephone = models.CharField(max_length=11,unique=True)
school = models.CharField(max_length=100)
# 指定telephone作为USERNAME_FIELD,以后使用authenticate
# 函数验证的时候,就可以根据telephone来验证
# 而不是原来的username
USERNAME_FIELD = 'telephone'
REQUIRED_FIELDS = []
# 重新定义Manager对象,在创建user的时候使用telephone和
# password,而不是使用username和password
objects = UserManager()
原来的UserManager 验证的是 def _create_user(self, username, email, password, **extra_fields):
authenticate只验证 用户名和密码 现在验证 手机号
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self,telephone,password,**extra_fields): #这个受保护的 只能类内部使用
if not telephone:
raise ValueError("请填入手机号码!")
user = self.model(telephone=telephone,*extra_fields)
user.set_password(password)
user.save()
return user
def create_user(self,telephone,password,**extra_fields): #创建普通用户
extra_fields.setdefault('is_superuser',False)
return self._create_user(telephone,password)
def create_superuser(self,telephone,password,**extra_fields): #创建超级用户
extra_fields['is_superuser'] = True
return self._create_user(telephone,password)
然后再在settings
中配置好AUTH_USER_MODEL=youapp.User
。
这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate
前就先定义好。
AbstractBaseUser
模型:如果你想修改默认的验证方式,并且对于原来User
模型上的一些字段不想要,那么可以自定义一个模型,然后继承自AbstractBaseUser
,再添加你想要的字段。这种方式会比较麻烦,最好是确定自己对Django
比较了解才推荐使用。步骤如下:
创建模型。示例代码如下:
class User(AbstractBaseUser,PermissionsMixin):
email = models.EmailField(unique=True)
username = models.CharField(max_length=150)
telephone = models.CharField(max_length=11,unique=True)
is_active = models.BooleanField(default=True)
USERNAME_FIELD = 'telephone'
REQUIRED_FIELDS = []
objects = UserManager()
def get_full_name(self):
return self.username
def get_short_name(self):
return self.username
其中password
和last_login
是在AbstractBaseUser
中已经添加好了的,我们直接继承就可以了。然后我们再添加我们想要的字段。比如email
、username
、telephone
等。这样就可以实现自己想要的字段了。但是因为我们重写了User
,所以应该尽可能的模拟User
模型:
USERNAME_FIELD
:用来描述User
模型名字字段的字符串,作为唯一的标识。如果没有修改,那么会使用USERNAME
来作为唯一字段。REQUIRED_FIELDS
:一个字段名列表,用于当通过createsuperuser
管理命令创建一个用户时的提示。is_active
:一个布尔值,用于标识用户当前是否可用。get_full_name()
:获取完整的名字。get_short_name()
:一个比较简短的用户名。重新定义UserManager
:我们还需要定义自己的UserManager
,因为默认的UserManager
在创建用户的时候使用的是username
和password
,那么我们要替换成telephone
。示例代码如下:
class UserManager(BaseUserManager):
use_in_migrations = True
def _create_user(self,telephone,password,**extra_fields):
if not telephone:
raise ValueError("请填入手机号码!")
user = self.model(telephone=telephone,*extra_fields)
user.set_password(password)
user.save()
return user
def create_user(self,telephone,password,**extra_fields):
extra_fields.setdefault('is_superuser',False)
return self._create_user(telephone,password)
def create_superuser(self,telephone,password,**extra_fields):
extra_fields['is_superuser'] = True
return self._create_user(telephone,password)
在创建了新的User
模型后,还需要在settings
中配置好。配置AUTH_USER_MODEL='appname.User'
。
如何使用这个自定义的模型:比如以后我们有一个Article
模型,需要通过外键引用这个User
模型,那么可以通过以下两种方式引用。
第一种就是直接将User
导入到当前文件中。示例代码如下:
from django.db import models
from myauth.models import User
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
这种方式是可以行得通的。但是为了更好的使用性,建议还是将User
抽象出来,使用settings.AUTH_USER_MODEL
来表示。示例代码如下:
from django.db import models
from django.conf import settings
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
这种方式因为破坏了原来User模型的表结构,所以必须要在第一次migrate
前就先定义好。
1.使用django 自带的用户系统
2.需要重新定制
3.前后台使用同一个user系统
#自定义user模型
1.单独创建一个应用app 用来存放 用户操作系统 xiaofanzhuoauth
2.全部重写 AbstactBaseUser
3.UserManager 需要重新写
4.在settings.py中设置 AUTH_USER_MODEL = "应用.User"
5.映射到数据库中
6.自增长的id 使用 shortuuid 前提需要 pip install django-shortuuidfield
# 登录逻辑
1.先用我们准备好的那个 登录页面 测试
然后 后期 前后都用一个登录页面
2.因为前后都用一个user系统
前台ajax 提交过来 提交过来 是 json 数据
3.所有的登录逻辑全部写在 xiaofanzhuoauth
在使用authenticate
进行验证后,如果验证通过了。那么会返回一个user
对象,拿到user
对象后,可以使用django.contrib.auth.login
进行登录。示例代码如下:
#views.py
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
注销,或者说退出登录。我们可以通过django.contrib.auth.logout
来实现。他会清理掉这个用户的session
数据以及user
对象。
def logout_view(request):
logout(request)
return redirect(reverse('news:index'))
有时候,某个视图函数是需要经过登录后才能访问的。那么我们可以通过django.contrib.auth.decorators.login_required
装饰器来实现。示例代码如下:
from django.contrib.auth.decorators import login_required
# 在验证失败后,会跳转到/login/这个url页面
@login_required(login_url="/login/")
def profile(request):
return HttpResponse("登錄成功以後才可以查看的個人中心")
def my_login(request):
if request.method == 'GET':
return render(request,'login.html')
else:
forms = LoginForm(request.POST)
if forms.is_valid():
telephone = forms.cleaned_data.get("telephone")
password = forms.cleaned_data.get("password")
remember = forms.cleaned_data.get("remember")
user = authenticate(request,username=telephone,password=password)
if user:
login(request,user)
if remember:
request.session.set_expiry(None)
else:
request.session.set_expiry(0)
next_url = request.GET.get('next')#判断后边是否有next
if next_url:
return redirect(next_url) #如果有跳转到 next下面
else:
return HttpResponse("登錄成功")
else:
return HttpResponse("用戶名或者密碼錯誤")
else:
print(forms.errors.get_json_data())
return redirect(reverse('login'))
def my_logout(request):
logout(request) 把请求传给 logout 就是退出登录
return HttpResponse("成功退出登錄")
#这个装饰器为了 想要查看 个人中心 必须先登录
#login_url="/login/" 如果你没有登录 先要跳转到登录页面 但是 跳转的登录地址 不是咱们想要的 如果指定登录的地址 必须加上 login_url="/login/
@login_required(login_url="/login/")
def profile(request):
return HttpResponse("登錄成功以後才可以查看的個人中心")
1.使用django 自带的用户系统
2.需要重新定制
3.前后台使用同一个user系统
#自定义user模型
1.单独创建一个应用app 用来存放 用户操作系统 xiaofanzhuoauth
2.全部重写 AbstactBaseUser
3.UserManager 需要重新写
4.在settings.py中设置 AUTH_USER_MODEL = "应用.User"
5.映射到数据库中
6.自增长的id 使用 shortuuid 前提需要 pip install django-shortuuidfield
# 登录逻辑
1.先用我们准备好的那个 登录页面 测试
然后 后期 前后都用一个登录页面
2.因为前后都用一个user系统
前台ajax 提交过来 提交过来 是 json 数据
3.所有的登录逻辑全部写在 xiaofanzhuoauth
如果验证失败了,那么有一些错误信息是我们需要传给前端的。这时候我们可以通过以下属性来获取:
form.errors
:这个属性获取的错误信息是一个包含了html标签的错误信息。
form.errors.get_json_data()
:这个方法获取到的是一个字典类型的错误信息。将某个字段的名字作为key,错误信息作为值的一个字典。
form.as_json()
:这个方法是将form.get_json_data()返回的字典dump成json格式的字符串,方便进行传输。
上述方法获取的字段的错误值,都是一个比较复杂的数据。比如以下:
{'username': [{'message': 'Enter a valid URL.', 'code': 'invalid'}, {'message': 'Ensure this value has at most 4 characters (it has 22).', 'code': 'max_length'}]}
那么如果我只想把错误信息放在一个列表中,而不要再放在一个字典中。这时候我们可以定义一个方法,把这个数据重新整理一份。实例代码如下:
class MyForm(forms.Form):
username = forms.URLField(max_length=4)
def get_errors(self):
errors = self.errors.get_json_data()
new_errors = {}
for key,message_dicts in errors.items():
messages = []
for message in message_dicts:
messages.append(message['message'])
new_errors[key] = messages
return new_errors
这样就可以把某个字段所有的错误信息直接放在这个列表中。
丢给前端一个地址 地址里边是 json数据 前端将json转为 js 然后 显示在页面上
前端通过接口地址 提交数据 给 后台完成数据的增删改查
GET 查
PUT 改
POST 增
DELETE 删除
我们需要判断 用户过来的请求方式 :
Django
中内置了权限的功能。他的权限都是针对表或者说是模型级别的。比如对某个模型上的数据是否可以进行增删改查操作。他不能针对数据级别
的,比如对某个表中的某条数据能否进行增删改查操作(如果要实现数据级别的,考虑使用django-guardian
)。创建完一个模型后,针对这个模型默认就有三种权限,分别是增/删/改/。可以在执行完migrate
命令后,查看数据库中的auth_permission
表中的所有权限。
如何获取settings.py中的 AUTH_USER_MODEL = 'news.User'
其中的codename
表示的是权限的名字。name
表示的是这个权限的作用。
如果我们想要增加新的权限,比如查看某个模型的权限,那么我们可以在定义模型的时候在Meta
中定义好。示例代码如下:
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey(get_user_model(),on_delete=models.CASCADE)
class Meta:
permissions = (
('view_article','can view article'),
)
权限都是django.contrib.auth.Permission
的实例。这个模型包含三个字段,name
、codename
以及content_type
,其中的content_type
表示这个permission
是属于哪个app
下的哪个models
。用Permission
模型创建权限的代码如下:
from django.contrib.auth.models import Permission,ContentType
from .models import Article
def add_permissions(request):
contenttype = ContentType.objects.get_for_model(Article)
#告诉 这是哪个app下面的 哪个model 的权限
permission = Permission.objects.create(codename="black_list",name="文章拉黑",content_type=contenttype)
return HttpResponse("添加权限成功")
def do_permissions(request):
user = User.objects.filter(pk=2).first()
contenttype = ContentType.objects.get_for_model(Article)
#Article模型 content_type为7 前边获取到了 7
#给用户添加 content_type为7 的所有权限
permissions = Permission.objects.filter(content_type=contenttype)
for permission in permissions:
print(permission)
user.user_permissions.set(permissions)
#user.user_permissions.clear()
#user.user_permissions.add(permissions[0],permissions[1])
#user.user_permissions.remove(permissions[0],permissions[1])
if request.user.has_perm('news.view_article'):
print("拥有权限")
else:
print("没有权限")
return HttpResponse("添加权限成功")
权限本身只是一个数据,必须和用户进行绑定,才能起到作用。User
模型和权限之间的管理,可以通过以下几种方式来管理:news_user_user_permissions 其实就是这个表的操作
myuser.user_permissions.set(permission_list)
:直接给定一个权限的列表。myuser.user_permissions.add(permission,permission,...)
:一个个添加权限。myuser.user_permissions.remove(permission,permission,...)
:一个个删除权限。myuser.user_permissions.clear()
:清除权限。myuser.has_perm('.')
:判断是否拥有某个权限。权限参数是一个字符串,格式是app_name.codename
。myuser.get_all_permissons()
:获取所有的权限。使用django.contrib.auth.decorators.permission_required
可以非常方便的检查用户是否拥有这个权限,如果拥有,那么就可以进入到指定的视图函数中,如果不拥有,那么就会报一个400
错误。示例代码如下:
from django.contrib.auth.decorators import permission_required
@permission_required(['news.delete_article'],login_url="/login/",raise_exception=True)
def my_view(request):
...
def add_article(request):
if request.user.is_authenticated:
print("已经登录")
if request.user.has_perm("news.view_article"):
return HttpResponse("这是添加文章的页面")
else:
return HttpResponse("您没有访问权限",status=403)
else:
return redirect(reverse('login'))
return HttpResponse("这是添加文章的页面")
权限有很多,一个模型就有最少三个权限,如果一些用户拥有相同的权限,那么每次都要重复添加。这时候分组就可以帮我们解决这种问题了,我们可以把一些权限归类,然后添加到某个分组中,之后再把和把需要赋予这些权限的用户添加到这个分组中,就比较好管理了。分组我们使用的是django.contrib.auth.models.Group
模型, 每个用户组拥有id
和name
两个字段,该模型在数据库被映射为auth_group
数据表。
Group.object.create(group_name)
:创建分组。group.permissions
:某个分组上的权限。多对多的关系。
group.permissions.add
:添加权限。group.permissions.remove
:移除权限。group.permissions.clear
:清除所有权限。user.get_group_permissions()
:获取用户所属组的权限。user.groups
:某个用户上的所有分组。多对多的关系。在settings.TEMPLATES.OPTIONS.context_processors
下,因为添加了django.contrib.auth.context_processors.auth
上下文处理器,因此在模板中可以直接通过perms
来获取用户的所有权限。
(2018.9.28)
通过urls—views—调用生成图片方法—实现验证码保存为图片—通过html文件将图片放置指定位置—通过js、css实现同台动态效果(点击更行验证码)。
在项目中,单独存放一些通用功能,所以生成图片验证方式放在通用的python文件,
import random
# pip install Pillow
# Image:是一个画板(context),ImageDraw:是一个画笔, ImageFont:画笔的字体
from PIL import Image,ImageDraw,ImageFont
import time
import os
import string
# Captcha验证码
class Captcha(object):
# 把一些常量抽取成类属性
#字体的位置(将字体文件verdana.ttf放置在此py文件同一级目录)
font_path = os.path.join(os.path.dirname(__file__),'verdana.ttf')
# font_path = 'utils/captcha/verdana.ttf'
#生成几位数的验证码
number = 4
#生成验证码图片的宽度和高度
size = (100,40)
#背景颜色,默认为白色 RGB(Re,Green,Blue)
bgcolor = (0,0,0)
#随机字体颜色
random.seed(int(time.time()))
fontcolor = (random.randint(200,255),random.randint(100,255),random.randint(100,255))
# 验证码字体大小
fontsize = 20
#随机干扰线颜色。
linecolor = (random.randint(0,250),random.randint(0,255),random.randint(0,250))
# 是否要加入干扰线
draw_line = True
# 是否绘制干扰点
draw_point = True
# 加入干扰线的条数
line_number = 3
SOURCE = list(string.ascii_letters) #随机生成字符串 a-zA-Z
for index in range(0, 10):
SOURCE.append(str(index))
#用来随机生成一个字符串(包括英文和数字)
# 定义成类方法,然后是私有的,对象在外面不能直接调用
@classmethod
def gene_text(cls):
return ''.join(random.sample(cls.SOURCE,cls.number))#number是生成验证码的位数
#用来绘制干扰线
@classmethod
def __gene_line(cls,draw,width,height):
begin = (random.randint(0, width), random.randint(0, height))
end = (random.randint(0, width), random.randint(0, height))
draw.line([begin, end], fill = cls.linecolor)
# 用来绘制干扰点
@classmethod
def __gene_points(cls,draw,point_chance,width,height):
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
#生成验证码
@classmethod
def gene_code(cls):
width,height = cls.size #宽和高
image = Image.new('RGBA',(width,height),cls.bgcolor) #创建画板
font = ImageFont.truetype(cls.font_path,cls.fontsize) #验证码的字体
draw = ImageDraw.Draw(image) #创建画笔
text = cls.gene_text() #生成字符串
font_width, font_height = font.getsize(text)
draw.text(((width - font_width) / 2, (height - font_height) / 2),text,font= font,fill=cls.fontcolor) #填充字符串
#填充到上面 draw的 image中
# 如果需要绘制干扰线
if cls.draw_line:
# 遍历line_number次,就是画line_number根线条
for x in range(0,cls.line_number):
cls.__gene_line(draw,width,height)
# 如果需要绘制噪点
if cls.draw_point:
cls.__gene_points(draw,10,width,height)
return (text,image)
通过视图来调用函数执行生成图片验证的相关操作。其本质是将验证码保存为一张图片,并同时将验证码中的字符暂时保存在缓存中,以便于之后验证提交的验证码。
#views.py
def img_captcha(request):
#第一步 实例化对象 调用
text,image = Captcha.gene_code()
#因为 图片不能直接放到 HttpResponse 直接返回
out = BytesIO() #可以先放到管道中 BytesIO相当于一个管道 存放图片流数据
image.save(out,'png')#调用image save方法 将图片流数据保存在BytesIO中 指定图片为png
out.seek(0) #将指针回0
response = HttpResponse(content_type="image/png") #指定返回内容的类型
response.write(out.read()) #从管道中读取数据 放到 HttpResponse中
#这个地方如果 管道指针 不回 0 它会从最后的位置开始往后读 会反回空
#所以上一步将指针回0
response['Content-length'] = out.tell() #也就是返回指针的位置
cache.set(text.lower(),text.lower(),300) #把图形验证码放到缓存中
return response
对于以上操作是将生成的验证码添加生干扰点以及干扰线并保存为png的图片,其路径为:"{% url ‘xiaofanzhuoauth:img_captcha’ %}",通过js实现绑定监听,一旦点击图片,发出请求,请求地址为验证码生成图片的地址,从而实现验证码的更新操作。
{# 在html文件中使用方式 #}
<div class="input-group">
<div class="short-input-group">
div>
<div class="input-group-addon">
<img src="{% url 'xiaofanzhuoauth:img_captcha' %}" alt="" class="img_captcha" style="cursor: pointer">
div>
div>
var img_captcha = $(".img_captcha")
img_captcha.click(function () {
img_captcha.attr("src","/account/img_captcha"+"?num="+Math.random())
})#类似于请求后面带随机参数,后台认定它将是不同的请求,从而达到无缓存目而且更新验证码,
(2018.9.28)
短信验证码服务商有很多。这里我们选择一个阿里通信来作为短信服务平台。通过这个平台,中小企业及开发者可以在最短的时间内实现短信验证码发送、短信服务提醒、语音验证码、语音服务通知、IVR及呼叫中心、码号、后向流量、隐私保护相关的能力,实现互联网电信化。
https://help.aliyun.com/document_detail/59210.html
在右上角的头像下,选择AccessKey:
https://dysms.console.aliyun.com/dysms.htm?spm=5176.8195934.907839.sms8.3a574183gWSBsQ#/domestic/text/sign
https://dysms.console.aliyun.com/dysms.htm?spm=5176.8195934.907839.sms8.3a574183gWSBsQ#/domestic/text/sign
模板内容 :
您的验证码为:${code}
在这里下载Python
的SDK
。
在这里查看文档。
下载完SDK
后,然后进入到目录中,再进入到你的django
开发的虚拟环境中,然后执行命令python setup.py install
即可安装SDK
。
在下载的SDK
文件中,有一个demo_sms_send.py
的文件,修改里面相关的参数,然后再修改const.py
中的AccessKey
和SecretKey
为之前获取的参数。运行这个文件就可以发送成功了。
# const.py文件
# ACCESS_KEY_ID/ACCESS_KEY_SECRET 根据实际申请的账号信息进行替换
ACCESS_KEY_ID = "你的用户的AccessKey"
ACCESS_KEY_SECRET = "你的用户的Secretkey"
然后修改demo_sms_send.py
的代码:
import sys
from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest
from aliyunsdkdysmsapi.request.v20170525 import QuerySendDetailsRequest
from aliyunsdkcore.client import AcsClient
import uuid
from aliyunsdkcore.profile import region_provider
from aliyunsdkcore.http import method_type as MT
from aliyunsdkcore.http import format_type as FT
import const
import json
"""
短信业务调用接口示例,版本号:v20170525
Created on 2017-06-12
"""
# 注意:不要更改
REGION = "cn-hangzhou"
PRODUCT_NAME = "Dysmsapi"
DOMAIN = "dysmsapi.aliyuncs.com"
acs_client = AcsClient(const.ACCESS_KEY_ID, const.ACCESS_KEY_SECRET, REGION)
region_provider.add_endpoint(PRODUCT_NAME, REGION, DOMAIN)
def send_sms(business_id, phone_numbers, sign_name, template_code, template_param=None):
smsRequest = SendSmsRequest.SendSmsRequest()
# 申请的短信模板编码,必填
smsRequest.set_TemplateCode(template_code)
# 短信模板变量参数
if template_param is not None:
smsRequest.set_TemplateParam(template_param)
# 设置业务请求流水号,必填。
smsRequest.set_OutId(business_id)
# 短信签名
smsRequest.set_SignName(sign_name)
# 数据提交方式
# smsRequest.set_method(MT.POST)
# 数据提交格式
# smsRequest.set_accept_format(FT.JSON)
# 短信发送的号码列表,必填。
smsRequest.set_PhoneNumbers(phone_numbers)
# 调用短信发送接口,返回json
smsResponse = acs_client.do_action_with_exception(smsRequest)
# TODO 业务处理
return smsResponse
if __name__ == '__main__':
__business_id = uuid.uuid1()
#print(__business_id)
params = {
'code': 1234
}
#params = u'{"name":"wqb","code":"12345678","address":"bz","phone":"13000000000"}'
print(send_sms(__business_id, "你的手机号", "应用名称", "你申请的模板", json.dumps(params)))
在html文件中点击控件,触发js绑定的事件,判断是否有号码,再通过ajax.get请求实现views.py中的方法,在views.py的方法中生成验证码,并将其保存到缓存中,调用之前安装的sms模块方法,传递参数(验证码),实现发送带有验证码的短信给用户,并返回结果,views.py的方法返回结果,js根据返回结果执行相应的操作。
<div class="input-group">
<div class="short-input-group">
<input type="text" class="form-control" name="sms_captcha" placeholder="短信验证码">
div>
<div class="input-group-addon">
<span class="sms-captcha-btn">发送验证码span>
div>
div>
function sms() {
var smsCaptcha = $(".sms-captcha-btn");
var telephoneInput = $(".signup-group input[name='telephone']");
smsCaptcha.click(function () {
var telephone = telephoneInput.val();
if(!telephone){messageBox.showInfo('请输入手机号码!'); }
xfzajax.get({
'url': '/account/sms_captcha/',
'data':{'telephone': telephone},
'success': function (result) {
if(result['code'] == 200){
messageBox.showSuccess('短信验证码发送成功!');
smsCaptcha.addClass('disabled');
var count = 60;
smsCaptcha.unbind('click');
var timer = setInterval(function () {
smsCaptcha.text(count+'s');
count -= 1;
if(count <= 0){
clearInterval(timer);
smsCaptcha.removeClass('disabled');
smsCaptcha.text('发送验证码');
sms();
}
},1000);
}
},
'fail': function (error) {console.log(error);}
});
});
}
from django.core.cache import cache
from utils.aliyunsdk import demo_sms_send
def sms_captcha(request):
#http://127.0.0.1:9000/sms_captcha?telephone=13777777777
telephone = request.GET.get('telephone') #接收手机号
code = Captcha.gene_text() #调用图片验证的生成随机的字符方法
cache.set(telephone, code, 5 * 60) #然后放到了缓存中
print('短信验证码:', code)
result = demo_sms_send.send_sms(telephone,code)#发送给用户
return restful.success()
import sys
from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest
from aliyunsdkdysmsapi.request.v20170525 import QuerySendDetailsRequest
from aliyunsdkcore.client import AcsClient
import uuid
from aliyunsdkcore.profile import region_provider
from aliyunsdkcore.http import method_type as MT
from aliyunsdkcore.http import format_type as FT
import json
"""
短信业务调用接口示例,版本号:v20170525
Created on 2017-06-12
"""
ACCESS_KEY_ID = "你的key"
ACCESS_KEY_SECRET = "你的密钥"
# 注意:不要更改
REGION = "cn-hangzhou"
PRODUCT_NAME = "Dysmsapi"
DOMAIN = "dysmsapi.aliyuncs.com"
acs_client = AcsClient(ACCESS_KEY_ID, ACCESS_KEY_SECRET, REGION)
region_provider.add_endpoint(PRODUCT_NAME, REGION, DOMAIN)
def send_sms(phone_numbers,code):
business_id = uuid.uuid1()
sign_name = '小饭桌管理平台'
template_code = 'SMS_146806055'
template_param = json.dumps({"code":code})
smsRequest = SendSmsRequest.SendSmsRequest()
# 申请的短信模板编码,必填
smsRequest.set_TemplateCode(template_code)
# 短信模板变量参数
if template_param is not None:
smsRequest.set_TemplateParam(template_param)
# 设置业务请求流水号,必填。
smsRequest.set_OutId(business_id)
# 短信签名
smsRequest.set_SignName(sign_name)
# 短信发送的号码列表,必填。
smsRequest.set_PhoneNumbers(phone_numbers)
# 调用短信发送接口,返回json
smsResponse = acs_client.do_action_with_exception(smsRequest)
return smsResponse
Git 是世界上最牛的 分布式版本管理工具 没有之一
推荐学习网站 : https://backlog.com/git-tutorial/cn/
https 和 ssh 的区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O73haUzm-1592464959733)(F:/Python%E5%AD%A6%E4%B9%A0/python%E4%B8%8A%E8%AF%BE%E8%AF%BE%E4%BB%B6/%E7%AC%AC3%E9%98%B6%E6%AE%B5%EF%BC%9Adjango%E6%A1%86%E6%9E%B6/2018.9.29/day35/%E7%AC%94%E8%AE%B0/Git%E6%B5%81%E7%A8%8Bpng.png)]
新建一个目录 然后 进入
git init 在当前目录创建一个代码库
git init 目录名 #创建一个目录并且将它设定为 代码库
git clone url
git config --list #显示当前的设置
git config -e --global #修改 配置文件
#设置代码提交的时候 用户的信息
git config --global user.name "gaohj"
git config --global user.email "[email protected]"
git add 文件名1 文件名2 #将文件添加到暂存区
git add . #.代表所有的文件
git add 目录名
git rm -f 删除工作区的文件 但是文件会保存到暂存区
git mv 原文件 名 新文件名 改名字
git commit -m "新增用户模型" #将暂存区下面所有的文件提交到本地仓库
git commit 文件名1 文件名2 -m "你的备注信息" #提交指定的文件到本地仓库
git commit -a 提交的是工作区从上次提交之后的变化的代码
git commit -v 提交的时候显示 所有的不同信息 也就是说 上次提交到这次提交 不同的地方
远处分支
本地分支 如果本地分支push到远程 远程才有远程分支
git branch #列出所有的本地分支
git branch -r #列出所有的远程分支
git branch -a #列出所有的本地和远程分支
git branch 分支名 #新建分支 但是你还是在原来的分支下面
git checkout 分支名 #切换到指定的分支 并且更新 工作区
git checkout - #切换到上一个分支
git branch -d python1806 #删除分支 注意这里是删除本地分支
git push origin --delete 分支名 #如果是https 克隆到本地仓库 需要 输入你的github 用户名和密码
如果创建了 两个分支 在其中一个分支下a写代码 另外一个分支b不写 a分支如果将这些代码 add 到暂存区 b分支还是能看到代码 只有当a分支将代码 commit到 本次仓库 这个时候 b就看不到 a分支下写的那个代码了
工作区 和暂存区 代码 可以被其它分支 看到
只有将代码 放入本地仓库 这个时候 其它分支看不到了
如果 已经到了本地仓库 其它分支相看代码 这个时候 就要 合并分支
合并分支
git merge 要合并的分支名字
合并分支之前 需要将分支 切换到 没有代码的那个分支
git status 显示有变更的文件
git log 当前分支下的代码版本历史
git diff 显示暂存区和工作区代码的区别是啥
git pull origin master #将制定远程分支下面的代码更新本地
git fetch --all #下载远程仓库下面所有的变动
git push origin --all #将本地仓库下面所有的变动 推送到远程仓库
增加 一个远程仓库的名字
git tag 列出所有的标签
git tag 标签名 在当前分支下面创建一个标签
git tag -d 标签名 删除指定的标签
git push origin 标签名 将标签提交到远程仓库
git push origin --tags 将所有的标签提交到远程仓库
git init 将当前目录初始化为本地仓库
git clone 将远程仓库的代码 同步到本地仓库
git pull origin 分支名 从指定的分支下面 拉取下来代码 到工作区
git add . 先把它加入到 暂存区
git commit -a -m "" 将其放入本地仓库
git push origin 分支名 将将本地仓库的代码 推送到远程仓库