pyenv概念
- 环境切换模式
pyenv根据shell、项目目录、全局(global)三个环境定义,去调用不同环境的python版本和第三方包。
# shell:base38假设是我们新的环境名称
#该命令会同步在当前的shell窗口创建一个PYENV_VERSION的环境变量,作用域仅仅在当前的shell
pyenv shell base38
# 项目目录:某个项目需要特定的python版本
# 该命令会同步在项目目录下,创建一个.python-version的文件
pyenv local base38
# 或者 echo "base38" > .python-version
# 全局(global):
# 该命令会同步在pyenv的根目录(默认是~/.pyenv/)下创建一个version的文件
pyenv global base38
因此要在生产环境采用不同的python环境,这里推荐python路径采用:
/data/app/pyenv/shims/python
然后用.python-version文件来控制不同项目文件夹下的环境
升级和部署django-web项目:
在测试、生产等无网络环境进行安装和升级python版本,具体的流程和脚本如下:
升级环境
升级环境需要三个过程:开发机器上准备环境,打包pyenv进行迁移,启动web项目
解压、安装pyenv环境包
安装libcom文件
安装openssl 1.1
开发机器上准备环境
1安装pyenv环境包
# 新环境安装
sudo yum update
sudo yum install libffi-devel
sudo yum install openssl readline gcc patch readline-devel zlib-devel sqlite-devel openssl-devel bzip2-devel
sudo yum install mariadb-devel
#默认都已经上传了pyenv-master.zip,Python-3.8.1.tar.xz,pyenv-virtualenv-master.zip
# 安装pyenv
sudo su - app
mkdir -p /data/app
cd /data/app
sudo rm -r /data/app/pyenv
unzip pyenv-master.zip
mv pyenv-master pyenv
# 安装pyenv
echo 'export PATH=/data/app/pyenv/bin/:$PATH' >> ~/.bash_profile
echo 'export PYENV_ROOT=/data/app/pyenv' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
source ~/.bash_profile # 重启也会生效,接管Python
# 安装pyenv-virtualenv
unzip pyenv-virtualenv-master.zip
mv pyenv-virtualenv-master /data/app/pyenv/plugins/pyenv-virtualenv
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
source ~/.bash_profile
pyenv virtualenv --version
2 安装python环境
# 从零安装python3
mkdir -p pyenv/cache
cp Python-3.8.1.tar.xz pyenv/cache/Python-3.8.1.tar.xz
rm pyenv/plugins/python-build/share/python-build/3.8.1
echo 'install_package "Python-3.8.1" "/data/app/pyenv/cache/Python-3.8.1.tar.xz" ldflags_dirs standard verify_py38 ensurepip' >> pyenv/plugins/python-build/share/python-build/3.8.1
pyenv install 3.8.1
3 安装其它第三方库
# 安装其他包:
# pip 设置
mkdir -p ~/.pip
echo '[global]' >> ~/.pip/pip.conf
echo 'index-url = http://mirrors.tencentyun.com/pypi/simple' >> ~/.pip/pip.conf
echo 'trusted-host=mirrors.tencentyun.com' >> ~/.pip/pip.conf
pip install --upgrade pip
# 重要==>克隆基础包后,创建其他模块的所需环境
pyenv virtualenv 3.8.1 base38
pyenv versions
pyenv shell base38 #<=====此命令行需要在安装pyenv后长开或者文末的版本指定写到配置文件中
pip install -r requirements.txt
pip install matplotlib
pip install mmh3,filterpy
pip install peewee
4 配置开发环境的应用环境变量
# 配置应用的环境变量
echo 'base38' > /data/app/.python-version
# # 免修改代码的Python路径
# ln -s /data/app/pyenv/shims/python /data/app/python/bin/python
打包pyenv进行迁移
1 打包传输
# 新环境准备完毕!!!
# 迁移环境
# 打包
tar -zcvf pyenv.tar.gz pyenv
# 传输
scp pyenv.tar.gz app@x.x.x.x:/data/app/
2 测试生产等新环境的准备
# python3
sudo yum update
sudo yum install libffi-devel
sudo yum install openssl readline gcc patch readline-devel zlib-devel sqlite-devel openssl-devel bzip2-devel
sudo yum install mariadb-devel
sudo yum install -y xz-devel
# 安装pyenv
sudo su - app
mkdir -p /data/app
cd /data/app
tar -zxvf pyenv.tar.gz
echo 'export PATH=/data/app/pyenv/bin/:$PATH' >> ~/.bash_profile
echo 'export PYENV_ROOT=/data/app/pyenv' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bash_profile
source ~/.bash_profile # 重启也会生效,接管Python
pyenv versions
echo 'base38' > /data/app/.python-version
3 如果有多台机器需要安装pyenv迁移可以用下面的脚本:
#!/bin/bash
#同步uat环境
list="x.x.x.x y.y.y.y"
for remote_host in $list;
do
echo $remote_host run start ;
cd /data/app
scp pyenv.tar.gz $remote_host:/data/app
echo "ssh remote"
ssh $remote_host << remotessh
cd /data/app
tar -zxvf pyenv.tar.gz
echo 'export PATH=/data/app/pyenv/bin/:\$PATH' >> ~/.bash_profile
echo 'export PYENV_ROOT=/data/app/pyenv' >> ~/.bash_profile
echo 'eval "\$(pyenv init -)"' >> ~/.bash_profile
echo 'eval "\$(pyenv virtualenv-init -)"' >> ~/.bash_profile
source ~/.bash_profile # 重启也会生效,接管Python
pyenv versions
echo 'base38' > /data/app/.python-version
echo "install OK!"
exit
remotessh
echo "ssh remote closed"
echo $remote_host run stop ;
done
启动web项目
1、代码层面的python2-3的代码升级
2、安装一些新的依赖
升级过程中遇到的一些问题如下,可以进行参考
升级过程遇到的问题可以这里记录一下,后期我会再整理一下。
参考:参考不同版本的发行说明,可以知道改了什么,怎么修改。进去搜索对应的函数名,应该就知道了
https://docs.djangoproject.com/zh-hans/3.2/releases/格式如下
问题:
- 解决方案
问题:
No translation files found for default language zh-cn
- 解决方案
LANGUAGE_CODE = 'zh_cn'
LANGUAGE_CODE = 'zh-Hans' # - not _
zh-Hans是简体中文 zh-Hant是繁体中文
/data/app/pyenv/versions/3.8.1/envs/base38/lib/python3.8/site-packages/django/conf/locale
django的所有语言文件都在上面的路径中,请自行选择要使用的语言文件
问题:
- TypeError: __init__() missing 1 required positional argument: 'on_delete' happens when your foreign key field doesn't have on_delete argument.
- 解决方案
,on_delete=models.CASCADE
product = models.ForeignKey('product',db_column='product',db_constraint=False,on_delete=models.CASCADE)
As you know, from Django 2.0 on_delete is required.
you need to add the on_delete parameter.
A many-to-one relationship. Requires two positional arguments: the class to which the model is related and the on_delete option.
https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ForeignKey
monitor_mapped_name\curve_data 需要注意一下@benji
问题:
cannot import name 'available_attrs' from 'django.utils.decorators'
- 解决方案
去掉from django.utils.decorators import available_attrs
Since we expect apps to drop Python 2 compatibility we’re removing these APIs at this time.
If the @wraps() in your sample line is the standard functools.wraps() decorator, then you can just entirely remove assigned=available_attrs(...), because functools.WRAPPER_ASSIGNMENTS is the default value for assigned:
@wraps(view_func)
-> @wraps(view_func) otherwise, just use functools.WRAPPER_ASSIGNMENTS directly.
问题:
No module named 'django.utils.six'
- 解决方案
views.py 里面的 from django.utils import six 也需要改为 import six
from django.utils.six.moves.urllib.parse import urlparse
from six.moves.urllib.parse import urlparse
问题:
'Specifying a namespace in include() without providing an app_name '
url(r'^iea/', include('iea.urls', namespace="iea"))
- 解决方案
url(r'^iea/', include(('iea.urls', "iea"),namespace="iea"))
def include(arg, namespace=None):
urlconf_module, app_name = arg,arg就是那个元组,且给app_name赋值了
问题:
'WSGIRequest' object has no attribute 'user'
- 解决方案
将MIDDLEWARE_CLASSES改成MIDDLEWARE
这是由于Django版本的问题,在1.10之前,中间件的key为MIDDLEWARE_CLASSES;在1.10之后,中间件的key为MIDDLEWARE。
问题:
get_wsgi_application :InterceptIllegalUserMiddleware() takes no arguments
- 解决方案
class xxxxx(MiddlewareMixin): 继承MiddlewareMixin进行兼容
WSGI全称是Web Server Gateway Interface,其主要作用是Web服务器与Python Web应用程序或框架之间的建议标准接口,可以将WSGI协议分成三个组件Application,Server,Middleware和协议中传输的内容。
- 升级 Django 1.10 之前的中间件
class django.utils.deprecation.MiddlewareMixin¶
Django 提供了 django.utils.deprecation.MiddlewareMixin 来方便创建同时兼容 MIDDLEWARE 和旧的 MIDDLEWARE_CLASSES 的中间件类,并支持同步和异步请求。Django 所包含的所有中间件类都兼容这两种配置。
mixin 提供了一个 init() 方法,它需要一个 get_response 参数,并将其存储在 self.get_response 中。
call() 方法:
调用 self.process_request(request) (如果被定义过)。
调用 self.get_response(request) 来从后续的中间件和视图得到响应。
调用 self.process_response(request, response) (如果被定义过)。
返回响应。
如果和 MIDDLEWARE_CLASSES 一起使用,call() 方法将永远不会被使用;Django 会直接调用 process_request() 和 process_response() 。
在大多数情况下,从这个 Mixin 中继承就足以使一个旧式中间件与新系统兼容,并具有足够的向后兼容性。新的短路语义对现有中间件无害甚至有益。在少数情况下,中间件类可能需要一些改变来适应新的语义。
MIDDLEWARE 和 MIDDLEWARE_CLASSES 在使用上有些行为差异:这里需要测试是否有异常
MIDDLEWARE_CLASSES 下,每个中间件将始终调用它的 process_response 方法,即使早期的中间件通过从其 process_response 方法返回响应而短路。MIDDLEWARE 下,中间件行为更像洋葱:响应在输出时经过的层与在输入时看到请求的层相同。如果一个中间件短路,只有那个中间件和之前的中间件可以看到响应。
在 MIDDLEWARE_CLASSES 下,process_exception 应用于中间件 process_request 方法引发的异常。在 MIDDLEWARE 下,process_exception 只应用于视图引发的异常(或者从 TemplateResponse 的 render 方法引发的异常)。中间件引发的异常被转换为合适的 HTTP 响应,然后传递到下一个中间件。
MIDDLEWARE_CLASSES 下,如果 process_response 方法引发了异常,所有更早之前的中间件的 process_response 方法会被跳过,并一直返回 500 Internal Server Error 的 HTTP 响应(即使引发的异常是例如 Http404 )。在 MIDDLEWARE ,一个中间件引发的异常将立刻被转换为合适的 HTTP 响应,然后下一个中间件将看到响应。中间件不会因为中间件引发异常而被跳过。
问题:
django报错‘staticfiles‘ is not a registered tag library
- 解决方案
修改前端模板为{% load static %},在html文件中
因为在django3.x中这部分做了修改,对于:
{% load staticfiles %}
{% load static from staticfiles %}
{% load admin_static %}
问题:
VariableDoesNotExist: Failed lookup for key [request] in u'[{}]'
- 解决方案
t.render({"username":request.user.username,'next':''})
必须传入渲染的参数,login之前没传会报错