依赖

  • Python 3.6.3 [ Python (2.7, 3.2, 3.3, 3.4, 3.5, 3.6) 都可以 ]
  • Django 1.11.7 [ Django (1.10, 1.11, 2.0 alpha) 都可以 ]

安装、创建并配置虚拟环境

mkdir django_rest_framework && cd django_rest_framework
pyenv virtualenv 3.6.3 django_rest_framework
pyenv local django_rest_framework
  • 安装相关包
pip install django djangorestframework
pip install pygments  # 项目中会用到,提供代码高亮功能

Django项目初始化

  • 创建tutorial项目与snippets应用
django-admin startproject tutorial
cd tutorial/

python manage.py startapp snippets
  • snippets应用和rest_framework添加进settings.pyINSTALLED_APPS (如果Django版本小于1.9,则需要将snippets.apps.SnippetsConfig替换为snippets)
INSTALLED_APPS = (
    ...
    'rest_framework',
    'snippets.apps.SnippetsConfig',
)

创建Model

我们需要创建一个Snippet模型来存储代码片段。
* 修改snippets/models.py文件

from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

# 提取出了pygments支持的所有语言的词法分析程序
LEXERS = [item for item in get_all_lexers() if item[1]]
# 提取出了pygments支持的所有语言列表
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
# 提取出了pygments支持的所有格式化风格列表
STYLE_CHOICES = sorted((item, item) for item in get_all_styles())


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)  # 创建时间
    title = models.CharField(max_length=100, blank=True, default='')  # 标题
    code = models.TextField()  # 代码
    linenos = models.BooleanField(default=False)  # 是否显示行号
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)  # 语言
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)  # 格式化风格

    class Meta:
        ordering = ('created',)
  • 数据库迁移
python manage.py makemigrations snippets
python manage.py migrate

创建Serializer类

  • snippets文件夹内创建serializers.py文件
from rest_framework import serializers
from snippets.models import LANGUAGE_CHOICES
from snippets.models import Snippet
from snippets.models import STYLE_CHOICES


class SnippetSerializer(serializers.Serializer):
    id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(required=False, allow_blank=True, max_length=100)
    code = serializers.CharField(style={'base_template': 'textarea.html'})
    linenos = serializers.BooleanField(required=False)
    language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
    style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """
        return Snippet.objects.create(**validated_data)

    def update(self, instance, validated_data):
        """
        Update and return an existing `Snippet` instance, given the validated data.
        """
        instance.title = validated_data.get('title', instance.title)
        instance.code = validated_data.get('code', instance.code)
        instance.linenos = validated_data.get('linenos', instance.linenos)
        instance.language = validated_data.get('language', instance.language)
        instance.style = validated_data.get('style', instance.style)
        instance.save()
        return instance

使用Serializers类

  • 打开Django shell
python manage.py shell
  • 创建两个Serializers对象
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser

snippet = Snippet(code='foo = "bar"\n')
snippet.save()

snippet = Snippet(code='print "hello, world"\n')
snippet.save()

序列化

  • 序列化一个对象
In [11]: serializer = SnippetSerializer(snippet)

In [12]: serializer.data
Out[12]: 
ReturnDict([('id', 2),
            ('title', ''),
            ('code', 'print "hello, world"\n'),
            ('linenos', False),
            ('language', 'python'),
            ('style', 'friendly')])
  • 将data数据转换为json
In [13]: content = JSONRenderer().render(serializer.data)

In [14]: content
Out[14]: b'{"id":2,"title":"","code":"print \\"hello, world\\"\\n","linenos":false,"language":"python","style":"friendly"}'

反序列化

  • 将JSON数据转为Python原生数据类型(字典)
In [15]: from django.utils.six import BytesIO

In [16]: stream = BytesIO(content)

In [17]: data = JSONParser().parse(stream)

In [18]: data
Out[18]: 
{'code': 'print "hello, world"\n',
 'id': 2,
 'language': 'python',
 'linenos': False,
 'style': 'friendly',
 'title': ''}
  • 将Python原生数据类型(字典)转换为对象
In [19]: serializer = SnippetSerializer(data=data)

In [20]: serializer.is_valid()
Out[20]: True

In [21]: serializer.validated_data
Out[21]: 
OrderedDict([('title', ''),
             ('code', 'print "hello, world"'),
             ('linenos', False),
             ('language', 'python'),
             ('style', 'friendly')])

In [22]: serializer.save()
Out[22]: <Snippet: Snippet object>
  • 可以序列化查询集,只需要添加many=True参数
In [23]: serializer = SnippetSerializer(Snippet.objects.all(), many=True)

In [24]: serializer.data
Out[24]: [OrderedDict([('id', 1), ('title', ''), ('code', 'foo = "bar\n"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 2), ('title', ''), ('code', 'print "hello, world"\n'), ('linenos', False), ('language', 'python'), ('style', 'friendly')]), OrderedDict([('id', 3), ('title', ''), ('code', 'print "hello, world"'), ('linenos', False), ('language', 'python'), ('style', 'friendly')])]

使用ModelSerializer

刚才定义的SnippetSerializer类中的类中有很多是和之前在models.py定义的内容重复,其实我们还可以使SnippetSerializer类继承于ModelSerializer,来减少这种重复定义。

  • SnippetSerializer类重新定义,为如下内容:
class SnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = Snippet
        fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
  • 打开Django shell
python manage.py shell
  • 查看SnippetSerializer
In [1]: from snippets.serializers import SnippetSerializer

In [2]: serializer = SnippetSerializer()

In [3]: print(repr(serializer))
SnippetSerializer():
    id = IntegerField(label='ID', read_only=True)
    title = CharField(allow_blank=True, max_length=100, required=False)
    code = CharField(style={'base_template': 'textarea.html'})
    linenos = BooleanField(required=False)
    language = ChoiceField(choices=[('abap', 'ABAP'), ('abnf', 'ABNF'), ('ada', 'Ada'), ('adl', 'ADL'), ('agda', 'Agda'), ('aheui', 'Aheui'), ('ahk', 'autohotkey'),
 ...

至此我们发现,继承于ModelSerializer类,有两个非常大的作用:
1. 不用重复去定义类属性
2. 自动拥有create()update()方法

Serializer在Django views内的简单运用

  • 此处未使用REST framework的其它特性,仅使用常规的Django views
  • 此处的写法有很多漏洞,仅作为教程中测试展示使用,不能直接应用于生产环境

配置views

打开snippets/views.py
* 导入相关包

from django.shortcuts import render
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
  • 创建一个返回所有对象和对象增功能的API
@csrf_exempt
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'POST':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data, status=201)
        return JsonResponse(serializer.errors, status=400)
  • 创建一个对象查、改、删功能的API
@csrf_exempt
def snippet_detail(request, pk):
    """
    Retrieve, update or delete a code snippet.
    """
    try:
        snippet = Snippet.objects.get(pk=pk)
    except Snippet.DoesNotExist:
        return HttpResponse(status=404)

    if request.method == 'GET':
        serializer = SnippetSerializer(snippet)
        return JsonResponse(serializer.data)

    elif request.method == 'PUT':
        data = JSONParser().parse(request)
        serializer = SnippetSerializer(snippet, data=data)
        if serializer.is_valid():
            serializer.save()
            return JsonResponse(serializer.data)
        return JsonResponse(serializer.errors, status=400)

    elif request.method == 'DELETE':
        snippet.delete()
        return HttpResponse(status=204)

配置urls

  • 创建snippets/urls.py
from django.conf.urls import url
from snippets import views

urlpatterns = [
    url(r'^snippets/$', views.snippet_list),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.snippet_detail),
]
  • 配置项目根urls.py
from django.conf.urls import url
from django.conf.urls import include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('snippets.urls')),
]

测试接口

  • 启动服务
(django_rest_framework) [root@localhost tutorial]# python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).
November 20, 2017 - 23:01:29
Django version 1.11.7, using settings 'tutorial.settings'
Starting development server at https://127.0.0.1:8000/
Quit the server with CONTROL-C.
  • 打开另一个shell窗口
  • 安装httpie包,用于模拟http请求

pip install httpie
  • 发送请求,获取所有snippets对象
(django_rest_framework) [root@localhost tutorial]# http https://127.0.0.1:8000/snippets/
HTTP/1.0 200 OK
Content-Length: 352
Content-Type: application/json
Date: Mon, 20 Nov 2017 15:03:10 GMT
Server: WSGIServer/0.2 CPython/3.6.3
X-Frame-Options: SAMEORIGIN

[
    {
        "code": "foo = \"bar\n\"",
        "id": 1,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
    {
        "code": "print \"hello, world\"\n",
        "id": 2,
        "language": "python",
        "linenos": false,
        "style": "friendly",
        "title": ""
    },
]
  • 发送请求,获取主键为2的snippets对象
(django_rest_framework) [root@localhost tutorial]# http https://127.0.0.1:8000/snippets/2/
HTTP/1.0 200 OK
Content-Length: 119
Content-Type: application/json
Date: Mon, 20 Nov 2017 15:03:17 GMT
Server: WSGIServer/0.2 CPython/3.6.3
X-Frame-Options: SAMEORIGIN

{
    "code": "print \"hello, world\"\n",
    "id": 2,
    "language": "python",
    "linenos": false,
    "style": "friendly",
    "title": ""
}

关于

本人是初学Django REST framework,Django REST framework 学习纪要系列文章是我从官网文档学习后的初步消化成果,如有错误,欢迎指正。

学习用代码Github仓库:shelmingsong/django_rest_framework

本文参考的官网文档:Tutorial 1: Serialization

博客更新地址

环境

CentOS-7-x86_64-Minimal-1708

Pyenv介绍

  • 可以实现多版本Python并存
  • 使用插件后,可以为不同的项目创建不同的虚拟环境

Pyenv安装

  • 安装依赖
yum -y install gcc git zlib-devel bzip2 bzip2-devel readline-devel sqlite sqlite-devel openssl-devel xz xz-devel
  • 下载pyenv源代码
git clone git://github.com/yyuu/pyenv.git ~/.pyenv
  • 添加环境变量
cat << "EOF" >> ~/.bashrc
export PYENV_ROOT="${HOME}/.pyenv"

if [ -d "${PYENV_ROOT}" ]; then
  export PATH="${PYENV_ROOT}/bin:${PATH}"
  eval "$(pyenv init -)"
fi
EOF

source ~/.bashrc
  • Done

安装指定Python版本(以Python 3.6.3为例)

  • 从国内镜像源下载Python指定版本
wget https://mirrors.sohu.com/python/3.6.3/Python-3.6.3.tar.xz  -P ~/.pyenv/cache
  • 安装指定Python版本(-v表示显示安装过程,可省略)
pyenv install 3.6.3 -v
  • 切换pip镜像源为国内镜像
  1. pyenv在安装python的时候,已经自动将pip安装好了
mkdir ~/.pip

cat << "EOF" >> ~/.pip/pip.conf
[global]
timeout = 6000
index-url = https://pypi.douban.com/simple
trusted-host = pypi.douban.com
EOF
  • Done

Pyenv常用命令

  • 查询所有可以安装的版本
pyenv install --list
  • 安装指定版本
  1. 建议按照上面的步骤,先从国内镜像下载然后再安装,否则会非常慢甚至中断
pyenv install 3.6.3
  • 卸载指定版本
pyenv uninstall 2.7.13
  • 显示已安装的所有版本
  1. 最前面带*的表示当前生效的版本
pyenv versions
  • 显示当前生效的版本
pyenv version
  • 设置全局(整个系统生效)Python版本
pyenv global 3.6.3
  • 设置多个全局(整个系统生效)Python版本
  1. 后面的版本号排序有先后,在前表示默认版本
# 方案1
pyenv global 3.6.3 2.7.13

# 方案1效果如下
python --version
Python 3.6.3

python3.6 --version
Python 3.6.3

python2.7 --version
Python 2.7.13

# 方案2
pyenv global 2.7.13 3.6.3

# 方案2效果如下
python --version
Python 2.7.13

python3.6 --version
Python 3.6.3

python2.7 --version
Python 2.7.13
  • 设置局部(当前目录生效)Python版本
pyenv local 3.6.3
  • 设置多个局部(当前目录生效)Python版本
  1. 后面的版本号排序有先后,在前表示默认版本
# 方案1
pyenv local 3.6.3 2.7.13

# 方案1效果如下
python --version
Python 3.6.3

python3.6 --version
Python 3.6.3

python2.7 --version
Python 2.7.13

# 方案2
pyenv local 2.7.13 3.6.3

# 方案2效果如下
python --version
Python 2.7.13

python3.6 --version
Python 3.6.3

python2.7 --version
Python 2.7.13
  • 取消设置局部(当前目录生效)Python版本
pyenv local --unset

pyenv-virtualenv(Pyenv插件)介绍

  • 可以为不同的项目创建不同的虚拟环境
  • 设置环境变量后,可进入指定目录自动激活虚拟环境

pyenv-virtualenv安装

  • 下载pyenv-virtualenv源代码
git clone git://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

source ~/.bashrc
  • 添加环境变量(进入指定目录自动激活虚拟环境)
echo 'eval "$(pyenv virtualenv-init -)"' >> ~/.bashrc

source ~/.bashrc

pyenv-virtualenv常用命令

  • 基于指定版本创建虚拟环境(推荐)
pyenv virtualenv 3.6.3 venv_name
  • 基于当前版本创建虚拟环境(不推荐)
pyenv virtualenv venv_name
  • 设置当前目录的虚拟环境(推荐)
  1. 若按照之前的步骤设置了环境变量,则会在进入目录后自动激活虚拟环境
pyenv local venv_name
  • 取消设置当前目录的虚拟环境(推荐)
pyenv local --unset
  • 手动激活虚拟环境(不推荐)
pyenv activate venv_name
  • 手动停用虚拟环境(不推荐)
pyenv deactivate venv_name
  • 显示所有已创建的虚拟环境
  1. 不一定已在当前目录激活
  2. 一个虚拟环境会显示两条记录
pyenv virtualenvs

博客更新地址

环境

CentOS-7-x86_64-Minimal-1708

开发原因

  • 现有博客基于Hexo&Github,想搭建一个自己全栈开发的博客
  • 用于实验新技术
  • 阿里云买了得用起来

技术栈

  • 下述版本号为待定,但如果使用其它版本的话只会用更新的
  • 如有用到其它技术会及时更新下表
工具 版本 用途
Docker 17.06.2 开发环境容器化
Python 3.6.3 后端开发语言
Django 1.11.7 后端框架
Html/CSS/JS / 前端开发语言
jQuery 3.2.1 前端框架
Bootstrap 3.3.7 前端框架
Celery 4.1.0 异步任务
django-celery-beat 1.0.1 定时任务
Gunicorn 19.7.1 Web服务器
Nginx 1.12.2 负载均衡
MySQL 5.7.20 数据库
Redis 3.2 缓存&&消息队列
shell / 自动化部署脚本基础
expect 5.45-14 自动化部署脚本处理交互

开发工具

  • 如有用到其它工具会及时更新下表
工具 用途
Pycharm IDE
Navicat 数据库调试
xshell ssh本地虚拟机和远程服务器
xftp 与本地虚拟机和远程服务器传输文件
VMware Workstation 虚拟机
Postman 接口调试
Chrome 前端调试
SwitchHosts 切换hosts
Sublime 草稿
印象笔记/马克飞象 笔记
网易云音乐 安抚Debug时暴躁的心

代码仓库地址

https://github.com/shelmingsong/Blog

博客更新地址

环境

Ubuntu 16.04

Python 3.5.2

问题

现有一个生成1-5随机数的函数random_5(概率平均),如何将此转换为生成1-7随机数random_7的函数(概率平均),不得使用random模块

思路一(False)

直接将random_5生成的随机数乘以7/5后再通过round函数进行四舍五入后输出

random_5输出 random_7输出
1 1
2 3
3 4
4 6
5 7

特别糟糕的想法…

思路二(False)

生成两次random_5,将两者的值相加后减一(random_5 + random_5 - 1),得出随机数1-9,而后再做一次判断,将8和9剔除掉,只在返回1-7的时候输出

不过很快就发现,只有在两次random_5都返回1的时候,才会返回1,概率远小于返回其它六个数

又是一个糟糕的想法…

思路三(True)


* 通过之前的两种思路,可以总结出将random_5返回的数据经过各种处理后不筛选返回1-7是不可能实现的(不能说不可能,只是我个人能力有限,暂时还没想出方法),那必须采用思路二中的筛选方法。
* 确定要使用筛选方法之后,目标就变成了生成等概率的、值取值范围大于1-7的随机数。
* 相加和返回值乘系数的方法都尝试过了,那么是否可以将两者结合一下呢。
* 首先思考乘系数,random_5 * 2 - 1的返回结果是[1, 3, 5, 7, 9],想要等概率生成1-10的话,比如再将此返回值加上随机生成的0和1,也就是random.randint(0, 1)
* 因不允许使用random模块,所以我们必须想办法重复利用原有的random_5函数,random_5 * 3 - 2需要对应的random.randint(0, 2),也可以理解为random.randint(1, 3) - 1
* 此时,显而易见,random_5 * 5 - 4需要对应的random.randint(1, 5) - 1,也就是random_5 - 1,那么基本的函数体就出来了,就是

random_5 * 5 + random_5 - 5
  • 再将筛选的逻辑加进去,完整代码如下:
import random


# 原有生成1-5随机数的函数
def random_5():
    return random.randint(1, 5)


# 要获得的生成1~7随机数的函数
def random_7():
    while True:  # 避免没有返回值
        n = (random_5()-1)*5 + random_5()-1  # 生成0~24的随机数
        if n <= 20:
            return n % 7 + 1


if __name__ == '__main__':
    print(random_7())

进阶

刚才得出的核心代码,去掉常变量之后就变成了random_5 * 5 + random_5,看着很像二、八、十六进制转换为十进制的方法,也就是可以理解为五进制,通过这种思想我们可以得出一个通用的随机数转换方法,具体代码如下:

import random
import math


class RandomIJToRandomAB(object):
    """将原来i-j之间的随机数生成函数,转化为a-b之间的随机数生成函数"""
    def __init__(self, i, j):
        self.i = i
        self.j = j
        ij_dif = j-i

        if ij_dif <= 0:
            raise ValueError

        self.ij_dif = ij_dif

    def random_ij(self):
        return random.randint(0, self.ij_dif)

    def random_ab(self, a, b):
        ab_dif = b-a
        digit = int(math.ceil(math.log(ab_dif+1, self.ij_dif+1)))  # 获取差值(ab_dif)转换为ij_dif+1进制后的最大位数

        while True:  # 防止return None
            # 生成最大位数为digit的ij_dif+1进制随机数,而后转化为10进制
            random_res = 0
            for item in range(digit):
                random_res += self.random_ij() * pow(self.ij_dif+1, item)

            # 只取在区间范围内的随机数
            if random_res <= ab_dif:
                return random_res+a


if __name__ == '__main__':
    print('0~1随机生成函数转化为2~11随机生成函数')
    random_1 = RandomIJToRandomAB(0, 1)
    print(random_1.random_ab(2, 11))
    print('-'*50)

    print('1~5随机生成函数转化为1~7随机生成函数')
    random_1_5 = RandomIJToRandomAB(1, 5)
    print(random_1_5.random_ab(1, 7))
    print('-'*50)

    print('1~7随机生成函数转化为1~5随机生成函数')
    random_1_7 = RandomIJToRandomAB(1, 7)
    print(random_1_7.random_ab(1, 5))
    print('-'*50)

Github源码地址:

random5_to_random7:
https://github.com/shelmingsong/algorithm_practice/blob/master/31_random5_to_random7_20170824.py

random_ij_to_random_ab:
https://github.com/shelmingsong/algorithm_practice/blob/master/32_random_ij_to_random_ab_20170824.py

0 环境:

Ubuntu 16.04

1 准备工作

2 安装并配置虚拟环境(本机)

2.1 更新软件源

sudo apt-get update
sudo apt-get upgrade
  • update是更新软件列表
  • upgrade是对比本地软件版本和线上最新软件版本,然后升级

2.2 安装虚拟环境

sudo pip2 install virtualenv
  • virtualenv是虚拟环境安装包,创建虚拟环境可以更好地控制包的版本,包的版本不会因为2.1中的升级操作而升级,保证了项目的稳定性
  • 不同虚拟环境之间的运行环境相互独立,互不干扰
sudo pip2 install virtualenvwrapper
  • virtualenvwrappervirtualenv的扩展管理包,可以将所有的虚拟环境整合在一个目录下
  • 使用前需要先进行以下配置
  1. 创建虚拟环境管理目录
mkdir ~/.virtualenvs
  1. 打开.bashrc
sudo vi ~/.bashrc 
  1. .bashrc的末尾增加以下内容
export WORKON_HOME=$HOME/.virtualenvs  # 所有虚拟环境存储的目录
source /usr/local/bin/virtualenvwrapper.sh
  1. 启用配置文件
source ~/.bashrc

2.3 创建虚拟环境

mkvirtualenv django_blog
  • 创建虚拟环境需要联网
  • 创建完虚拟环境之后会自动进入虚拟环境,可以通过命令行前缀(下图红框)判断是否在虚拟环境内
  • 虚拟环境常用命令
# 创建虚拟环境
mkvirtualenv <name>

# 列出所有虚拟环境
workon TAB*2

# 进入虚拟环境
workon <name>

# 退出虚拟环境
deactivate

# 删除虚拟环境
rmvirtualenv <name>

2.4 安装django包

pip install django==1.8.2
  • 包后面加==用来选择包的版本

3 创建一个django演示项目(本机)

3.1 创建项目

django-admin startproject project_test
  • 项目建立在home目录或其子目录,以免权限问题带来的不便

3.2 创建应用

  • 进入项目目录并查看项目结构
cd project_test/
tree
  • 项目目前结构如下
  1. 创建应用
python manage.py startapp app_test
  • 创建应用后项目结构如下

3.3 修改项目配置

3.3.1 修改环境变量

  • 打开IDE(此处使用的是Pycharm)
  • 选择File/Settings,进入Project Interpreter,选择虚拟环境内的python版本
  • 如果选项内没有,就点击右侧的齿轮,选择Add Local,
    选择所在虚拟环境下的python版本,如/home/python/.virtualenvs/django_blog/bin/python2.7

3.3.2 创建模板文件夹、静态文件文件夹和应用urls

  • 在项目根目录分别创建statictemplates文件夹
  • static文件夹下创建jscssimg三个文件夹,以后用来存放静态文件
  • templates文件夹下创建app_test(应用名)文件夹,以后用来存放模板文件
  • 在应用文件夹下创建urls.py文件,以后用来存放应用的urls映射表
  • 创建完成之后的目录结构如下:

3.3.3 修改settings

  • 打开/project_test下的settings.py
  • 添加应用__在INSTALLED_APPS中将我们刚创建的应用加到最后面
  • 修改模板路径__在TEMPLATES里的DIRS修改为'DIRS': [BASE_DIR, 'templates'],
  • 修改静态文件路径__在最后添加
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

3.4 创建页面

3.4.1 修改项目urls

  • 打开/project_test下的urls.py,在urlpatterns内添加一行,意思是只要不是匹配到admin,都转到app_test应用
url(r'^', include('app_test.urls')),
  • 打开/app_test下的urls.py,添加以下内容:
#coding=utf-8
from django.conf.urls import  url
from . import views  # 从当前文件夹内引入views

urlpatterns = [
    url(r'^$', views.index),  # 匹配到根目录,就执行views文件内的index函数
]
  • 打开/project_test下的views.py,添加刚才urls.py内提到的index函数
#coding=utf-8
from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, 'app_test/index.html')
    # 对请求值不做任何处理,直接跳转到模板目录下app_test文件夹下的index.html页面
  • 将一张图片放在/static/img文件夹内
  • templates/app_test文件夹下,创建模板文件index.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Index</title>
</head>
<body>
    <h1>下面有一张图片</h1>
    <img src="/static/img/test.jpg" alt="">  // 寻找static(settings里面的STATIC_URL)文件夹下img文件夹里的test.jpg图片
</body>
</html>

3.5 打开页面

  • 在项目根目录文件夹下,在终端内(确保已进入虚拟环境)
python manage.py runserver
  • 终端显示如下(红色字体暂不用管,因为我们并没有创建models):

  • 打开红框内显示的IP地址(127.0.0.1:8000)

  • 浏览器成功显示创建的页面

4 将项目上传至服务器(本机 & 服务器)

4.1 导出虚拟环境内的包(本机)

pip freeze > plist.txt
  • 此操作将虚拟环境内所有的包都保存在了list.txt里面

4.2 将项目文件夹整体上传至服务器

  • 传输文件可以用samba,ftp,scp等等,此处使用的是scp
scp -r <YOUR_LOCAL_PROJECT_DIR> <SERVER_USERNAME>@<SERVER_IP>:<YOUR_SERVER_PROJECT_DIR>

4.3 在服务器上安装虚拟环境(服务器)

  • 在服务器上重复2.1~2.3的步骤,安装虚拟环境
  • 进入虚拟环境,进入项目目录,根据本地计算机提供的包列表安装所需要的包
pip install -r plist.txt

5 安装uWSGI(服务器)

  • 安装Python开发版本(因为安装uWSGI过程中需要编译)
sudo apt-get install python-dev
  • 安装gcc(因为安装uWSGI过程中需要C编译器)
sudo apt-get install gcc
  • 安装uWSGI
pip install uwsgi
  • 使用uwsgi --version命令查看版本号,确认已正确安装

6 配置Django(服务器)

  • 配置项目settings,修改以下一项
ALLOWED_HOSTS = ['*', ]
  • 在项目根目录文件夹下,在终端内(确保已进入虚拟环境)
python manage.py runserver 0:8000
  • 在本地计算机浏览器中输入以下地址以测试Django在服务器上是否正常运行
<YOUR SERVER IP>:8000

2017.06.08更新
* 如果不能正常显示页面,一般为以下两个原因:

1.请检查防火墙状态,保证8000端口是能够被访问的,代码如下( 更多防火墙设置可以参考我这篇文章https://t.cn/RSuX8Qj

# 检查防火墙状态
sudo ufw status

# 可以临时关闭防火墙
sudo ufw disable

# 或者保持防火墙开启,允许8000端口连接
sudo ufw allow 8000

2.请检查服务器安全组规则,如阿里云服务器,可以在进入管理控制台后,按以下步骤设置( 如要了解更多服务器安全组规则设置,请参见阿里云官方文档https://t.cn/RST7Sgz

3.重启服务,再次在本地计算机浏览器中查看

  • 确定服务器正常后,打开settings,修改以下一项
DEBUG = False

7 配置uWSGI(服务器)

7.1 打通uWSGI和Python

  • 在项目根目录创建test.py文件,内容如下
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return ["Hello World"]  # python2
    # return [b"Hello World"]  # python3
  • 运行uWSGI(表示使用http协议,并使用8000端口,加载指定文件test.py)
uwsgi --http :8000 --wsgi-file test.py
  • 打开浏览器,输入
127.0.0.1:8000
  • 若显示’Hello World’则表示运行正常,说明以下三个环节是相通的
web client <-> uWSGI <-> Python

7.2 打通uWSGI和Django

  • 在项目根目录创建文件my_uwsgi.ini,并写入以下内容
[uwsgi]
# 使用nginx连接时使用
# socket = 0:8001

# 直接做web服务器使用
http = 0:8080

# 项目目录
chdir = /home/python/Desktop/project_test

# 项目中wsgi.py文件的目录
wsgi-file = /home/python/Desktop/project_test/project_test/wsgi.py

# 主进程
master = true

# 多进程&多线程
processes = 6
threads = 2

# .sock文件目录需与Nginx文件内的配置相同
# socket = /home/python/Desktop/project_test/my_sock.sock
# chmod-socket = 666

# 以守护进程的方式启动
vacuum = true


# 存储pid进程
pidfile=uwsgi.pid

# 存储log日志
daemonize=uwsgi.log
  • 启动uWSGI服务
uwsgi --ini my_uwsgi.ini
  • 终端显示以下内容即代表开启成功
[uWSGI] getting INI configuration from my_uwsgi.ini
  • 打开浏览器,地址栏输入以下地址
<YOUR_SERVER_IP>:8080
  • 可以看到,文字正常显示,图片无法显示,这是正常现象

  • 以上步骤说明以下三个环节是相通的

web client <-> uWSGI <-> Django
  • 停止uWSGI服务
uwsgi --stop uwsgi.pid

8 配置Nginx(服务器)

8.1 安装Nginx

  • 安装Nginx
sudo apt-get install nginx
  • Nginx安装成功后,系统会自动开启 Nginx 服务,默认使用80端口,打开浏览器并输入你服务器的IP地址,就可以看到 nginx 的测试页面
Welcome to nginx!

If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

For online documentation and support please refer to nginx.org.
Commercial support is available at nginx.com.

Thank you for using nginx.
  • 说明以下两个环节是相通的
web client <-> the web server(Nginx)

8.2 配置Nginx

  • /etc/nginx/目录下的uwsgi_params复制到项目文件夹,对此文件不做任何改动
cp /etc/nginx/uwsgi_params .
  • 在项目根目录创建文件my_nginx.conf,并写入以下内容
upstream django {
    server    127.0.0.1:8001;
    # server      unix://home/python/Desktop/project_test/my_sock.sock;
}

server {
    listen      8000;  # 端口号
    server_name 127.0.0.1;  # 服务器 ip 或是域名
    charset     utf-8;  # 字符集

    # 最大上传限制
    # client_max_body_size 75M;

    location /media  {
        alias /home/python/Desktop/project_test/media_common;  # 媒体文件所在文件夹
    }

    location /static {
        alias /home/python/Desktop/project_test/static_common;  # 静态文件所在文件夹
    }


    # 将所有非媒体请求转到Django服务器上
    location / {
        uwsgi_pass      django;  # 最上方已定义
        # 将所有参数都转到uwsgi下
        include         /home/python/Desktop/project_test/uwsgi_params; # uwsgi_params的路径
    }
}

  • 这个配置文件表示将静态文件和媒体文件由Nginx处理,而其它的请求转入uWSGI处理

  • 与Nginx配置目录建立软链接

sudo ln -s /home/python/Desktop/project_test/my_nginx.conf /etc/nginx/sites-enabled/
  • 创建静态文件与媒体文件存放目录
sudo mkdir static_common
sudo mkdir media_common
  • 如果出现权限问题,将权限改下就好
chmod 777 static_common
chmod 777 media_common
  • 修改项目settings,在最后面添加
STATIC_ROOT = os.path.join(BASE_DIR, 'static_common')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media_common')
  • 进入项目根目录,执行静态文件迁移命令
python manage.py collectstatic
  • 提示输入选项时选yes即可

  • 重启Nginx服务

sudo /etc/init.d/nginx restart

9 Nginx & uWSGI & Django(服务器)

9.1 测试Nginx

  • 在浏览器内输入<YOUR_SERVER_IP>:8000/static/admin/css/base.css,检查是否能正常显示这个css文件

  • 将一张测试图片test.jpg放入media_common文件夹中,在浏览器中输入<YOUR_SERVER_IP>:8000/media/test.jpg,如果出现403则将图片的权限改为666,成功显示图片

9.2 测试uWSGI

  • 回到项目根目录,输入以下命令
uwsgi --socket :8001 --wsgi-file test.py
  • 打开浏览器,地址栏输入<YOUR_SERVBER_IP>,看是否能正常显示’Hello World’

9.3 用UNIX socket取代TCP port

  • 修改my_nginx.conf最终版如下:
upstream django {
    # server    127.0.0.1:8001;
    server      unix:///home/python/Desktop/project_test/my_sock.sock;
}

server {
    listen      8000;  # 端口号
    server_name songmingyao.com;  # 服务器 ip 或是域名
    charset     utf-8;  # 字符集

    # 最大上传限制
    # client_max_body_size 75M;

    location /media  {
        alias /home/python/Desktop/project_test/media_common;  # 媒体文件所在文件夹
    }

    location /static {
        alias /home/python/Desktop/project_test/static_common;  # 静态文件所在文件夹
    }


    # 将所有非媒体请求转到Django服务器上
    location / {
        uwsgi_pass      django;  # 最上方已定义
        # 将所有参数都转到uwsgi下
        include         /home/python/Desktop/project_test/uwsgi_params; # uwsgi_params的路径
    }
}
  • 修改my_uwsgi.ini最终版如下
[uwsgi]
# 使用nginx连接时使用
# socket = 0:8001

# 直接做web服务器使用
# http = 0:8080

# 项目目录
chdir = /home/python/Desktop/project_test

# 项目中wsgi.py文件的目录
wsgi-file = /home/python/Desktop/project_test/project_test/wsgi.py

# 主进程
master = true

# 多进程&多线程
processes = 6
threads = 2

# .sock文件目录需与Nginx文件内的配置相同
socket = /home/python/Desktop/project_test/my_sock.sock
chmod-socket = 666

# 以守护进程的方式启动
vacuum = true

# 存储pid进程
pidfile=uwsgi.pid

# 存储log日志
daemonize=uwsgi.log
  • 重启Nginx和uWSGI
sudo /etc/init.d/nginx restart

uwsgi --stop uwsgi.pid
uwsgi --ini my_uwsgi.ini
  • 打开浏览器,地址栏输入网址<YOUR_SERVER_IP>:8000,查看图片和文字是否显示正常

  • 此时以下环节已全部打通

web client <-> web server(nginx) <-> the socket <-> uwsgi <-> Django

9.4 服务器开机自启

  • 修改/etc/rc.local,添加以下内容到exit 0之前
/usr/local/bin/uwsgi --ini /home/python/Desktop/project_test/my_uwsgi.ini

环境

Python 3.5.2

原理

  • 找出列表中最大和最小的元素
  • 构建新列表,元素全为零,长度为最大值与最小值之间的差值加一
  • 统计待排序列表中每个值i出现的次数,并将新列表中下标为i-min的值加一
  • 将新列表中非零值的下标反转回原有元素i(即加上最小值),构建有序列表

源码

def count_sort(l):
    max = 0
    min = 1024

    for item in l:
        if item > max:
            max = item
        elif item < min:
            min = item

    count = [0]*(max-min+1)
    for index in l:
        count[index-min] += 1

    index = 0
    for i in range(max-min+1):
        for j in range(count[i]):
            l[index] = i+min
            index += 1

if __name__ == '__main__':
    l = [6, 5, 2, 8, 9, 4, 1, 0, 3, 7]
    print(l)
    count_sort(l)
    print(l)

时间复杂度

  • 最优时间复杂度:O(n+k)
  • 最坏时间复杂度:O(n+k)
  • 稳定性(多个元素等值的情况下是否会破坏原有顺序):稳定

环境

Python 3.5.2

原理

  • 将列表构造成最大堆或最小堆
  • 将堆的根节点,即最大值或最小值与堆的末尾对调
  • 将末尾元素(即之前得到的堆的根节点)移除后,重构堆,如此循环

源码

def heap_sort(l):
    def max_heapify(root_index, end_index):
        """函数用于构造最大堆"""
        # 减一是因为堆的下标从1开始
        max_child_index = root_index*2-1

        # 在该二叉树有两个子节点的情况下,将两个子节点比较大小
        if max_child_index + 1 < end_index:
            if l[max_child_index+1] > l[max_child_index]:
                max_child_index += 1

        # 将最大的子节点与根节点做比较
        if l[max_child_index] > l[root_index-1]:
            l[max_child_index], l[root_index-1] = l[root_index-1], l[max_child_index]


    # 循环构造最大堆,而后将最大堆的根节点与末节点调换
    for end_index in range(len(l), 1, -1):
        # 每次要构造的最大堆大小与末节点有关
        max_root_index = end_index//2

        # 构造一个最大堆
        for root_index in range(max_root_index, 0, -1):
            max_heapify(root_index, end_index)

        # 将最大堆的根节点与末节点调换
        l[0], l[end_index-1] = l[end_index-1], l[0]


if __name__ == '__main__':
    l = [6, 5, 2, 8, 9, 4, 1, 0, 3, 7]
    print(l)
    heap_sort(l)
    print(l)

时间复杂度

  • 最优时间复杂度:O(nlogn)
  • 最坏时间复杂度:O(nlogn)
  • 稳定性(多个元素等值的情况下是否会破坏原有顺序):不稳定

环境

Python 3.5.2

原理

  • 将递归分解列表,直至最小(即每个列表仅有一个元素)
  • 将列表分解最小之后,递归合并两个列表,即挨个比较两个列表中最前面的元素,谁较小就将谁加入新的列表,而后该列表的下标后移一位,继续比较,直至其中一个列表为空,而后将另一个列表中剩余的元素加入新列表
  • 不断合并,直至完全排序完成

源码

def merge_sort(l):
    # 对列表进行二分分解
    n = len(l)
    if n <= 1:
        return l
    mid_posi = n//2
    # print(1)
    l_list = merge_sort(l[:mid_posi])
    # print(2)
    r_list = merge_sort(l[mid_posi:])
    # print(3)

    # 对列表进行合并
    sorted_list = []
    l_posi = 0
    r_posi = 0
    # print('l_list: %s' % l_list)
    # print('r_list: %s' % r_list)

    while l_posi < len(l_list) and r_posi < len(l_list):
        if l_list[l_posi] <= r_list[r_posi]:
            sorted_list.append(l_list[l_posi])
            l_posi += 1
        else:
            sorted_list.append(r_list[r_posi])
            r_posi += 1

    # 将列表剩余部分合并
    sorted_list += l_list[l_posi:]
    sorted_list += r_list[r_posi:]
    # print('sorted_list: %s' % sorted_list)

    return sorted_list


if __name__ == '__main__':
    l = [6, 5, 2, 8, 9, 4, 1, 0, 3, 7]
    print(l)
    sorted_list = merge_sort(l)
    print(sorted_list)

时间复杂度

  • 最优时间复杂度:O(nlogn)
  • 最坏时间复杂度:O(nlogn)
  • 稳定性(多个元素等值的情况下是否会破坏原有顺序):稳定

环境

Python 3.5.2

原理

  • 为插入排序的优化
  • 对要排序的列表根据一定间隔(初始间隔一般设为列表长度的一半)进行分组
  • 对各列表之间相同位置(下标)的元素进行插入排序
  • 间隔减半,再次分组并对各列表之间相同位置(下标)的元素进行插入排序
  • 如此循环,最终间隔为1,即为正常的插入排序

源码

def shell_sort(l):
    n = len(l)
    # 初始间隔
    gap = n//2

    while gap > 0:
        for i in range(gap, n):
            for j in range(i, gap-1, -gap):
                if l[j] < l[j-gap]:
                    l[j], l[j-gap] = l[j-gap], l[j]
                else:
                    break
        gap //= 2


if __name__ == '__main__':
    l = [6, 5, 2, 8, 9, 4, 1, 0, 3, 7]
    print(l)
    shell_sort(l)
    print(l)

时间复杂度

  • 最优时间复杂度(初始间隔为列表长度一半时):O(nlogn)
  • 最坏时间复杂度:O(n2)
  • 稳定性(多个元素等值的情况下是否会破坏原有顺序):不稳定

环境

Python 3.5.2

原理

  • 在列表中挑选出一个基准值
  • 将列表中的其它元素与基准值对比,比基准值大的放在基准值右侧,比基准值小的放在基准值左侧
  • 以此类推

源码

def quick_sort(l, start, end):
    if start >= end:
        return

    low = start
    high = end
    mid_value = l[low]

    while low < high:
        # low<high这个条件必须在这里再次体现,避免出现循环过程中low>high的情况
        while low < high and l[high] >= mid_value:
            high -= 1
        # 当中间值右侧的值小于中间值时,将该值赋值于l[low](原来的值已保存至mid_value变量)
        l[low] = l[high]

        # low<high这个条件必须在这里再次体现,避免出现循环过程中low>high的情况
        while low < high and l[low] <= mid_value:
            low += 1
        # 当中间值左侧的值大于中间值时,将该值赋值于l[high](原来的值已保存至l[low])
        l[high] = l[low]

    # 退出循环时,说明low==high,则可以将mid_value的值赋予它
    l[low] = mid_value

    quick_sort(l, start, low-1)
    quick_sort(l, low+1, end)


if __name__ == '__main__':
    l = [6, 5, 2, 8, 9, 4, 1, 0, 3, 7]
    print(l)
    quick_sort(l, 0, len(l)-1)
    print(l)

时间复杂度

  • 最优时间复杂度:O(nlogn)
  • 最坏时间复杂度:O(n2)
  • 稳定性(多个元素等值的情况下是否会破坏原有顺序):不稳定