本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/Rozol/article/details/79526637
以下代码以Python3.6.1为例
Less is more!
Python 3.6.1
Django 2.0.2
django-tinymce==2.7.0
django-redis-cache==1.7.1
django-haystack==2.8.0
whoosh==2.7.4
jieba==0.39
celery==4.1.0
celery-with-redis==3.0
django-celery==3.2.2
uwsgi==2.0.17
项目名: Django_templates 应用名: booktest
配置settings:py
:
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
路径选择: (STATIC_URL = '/static/'
)
os.path.join(BASE_DIR, 'static'),
os.path.join(BASE_DIR, 'booktest.static'),
static
文件夹STATIC_URL = '/static/'
时, 路径为 
STATIC_URL = '/img/'
时, 路径为 

{% load static from staticfiles %}

MEDIA_ROOT = os.path.join(BASE_DIR,"static/media")
media
文件夹上传图片
方式一: 通过表单上传
图片上传表单:
<form method="post" action="upload" enctype="multipart/form-data">
{% csrf_token %}
<input type="text" name="title"><br>
<input type="file" name="pic"/><br>
<input type="submit" value="上传">
form>
图片接收并保存:
from django.conf import settings
# 接收图片
def upload(request):
if request.method == "POST":
# 接收图片
file = request.FILES['pic']
# 保存图片
file_name = os.path.join(settings.MEDIA_ROOT, file.name) # 图片路径
with open(file_name, 'wb') as pic:
for b in file.chunks():
pic.write(b)
return HttpResponse('
'.format(file.name))
else:
return HttpResponse('图片接收错误')
方式二: 移动端上传
以Android系统, okhttp3为例
// --- Android的代码 ---
// 混合类型(Multipart)
// python django2 可用
// java spring 可用
// RequestBody构建方式一
RequestBody mbody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", file.getName(), RequestBody.create(MediaType.parse("media/type"), file))
.addFormDataPart("key1", "value1")
.addFormDataPart("key2", "value2")
.build();
// RequestBody构建方式二
RequestBody mbody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"key1\""),
RequestBody.create(null, "value1"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"key2\""),
RequestBody.create(null, "value2"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"file\"; filename =\"" + file.getName() + "\""),
RequestBody.create(MediaType.parse("media/type"), file))
.build();
Request request = new Request.Builder().url(url).post(mbody).build();
new OkHttpClient().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) { }
@Override
public void onResponse(Call call, Response response) throws IOException { }
});
# --- django2的代码 ---
# 从移动端接收图片, 移动端使用okhttp库post请求
def uoload_mobile(request):
http = HttpResponse()
if request.method == "POST":
# 接收图片
file = request.FILES['file']
# 保存图片
file_name = os.path.join(settings.MEDIA_ROOT, file.name) # 图片路径
with open(file_name, 'wb') as pic:
for b in file.chunks():
pic.write(b)
print('key1: {}'.format(request.POST['key1']))
print('key2: {}'.format(request.POST['key2']))
http.status_code = 200
http.write('ok')
else:
http.status_code = 400
http.write('err')
return http
# 分页
def pagination(request, index_page):
books = BookInfo.objects.all()
'''
Paginator
属性:
count: item总条数
num_pages: 总页数
page_range: 页码, range对象
方法:
page(num): 指定页, 页码不存在抛InvalidPage异常, 页码非整数抛PageNotAnInteger异常, 页码无页面抛EmptyPage异常
'''
# 分页(一次取几页)
list = Paginator(books, 3)
'''
Page: 由Paginator.page()返回的Page对象, 可迭代对象
属性:
object_list: 当前页所有对象的列表
number: 当前第几页 (非item序号)
paginator: 当前page对象的Paginator对象
方法:
has_next(): 是否有下一页
has_previous(): 是否有上一页
has_other_pages(): 是否还有其他页
next_page_number(): 下一页的页码, 不存在抛InvalidPage异常
previous_page_number(): 上一页的页码, 不存在抛InvalidPage异常
len(): 当前页面item个数
'''
# 单页(一页有多个条目)
pages = list.page(int(index_page))
context = {
'books': pages, 'page_sum': list.num_pages, 'page': pages.number,
}
return render(request, 'pagination.html', context)
省市区三级联动案例:
# view层处理
# 省市区选择测试
def areatest(request):
return render(request, 'areatest.html')
# 省
def pro(request):
prolist = AreaInfo.objects.filter(area_parent__isnull=True)
list = []
for item in prolist:
list.append([item.id, item.area_title]) # 采用列表 [[111, 'xxx'],[112, 'xxx']]
return JsonResponse({'data': list})
# 市
def city(request, id):
citylist = AreaInfo.objects.filter(area_parent_id=id)
list = []
for item in citylist:
list.append({'id': item.id, 'title': item.area_title}) # 采用字典 [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]
return JsonResponse({'data': list})
# html页ajax编写
<head>
<title>Titletitle>
<script src="/static/jquery-1.12.4.min.js">script>
<script>
$(function () { // 执行方法
// 使用ajax异步加载省信息
$.get('/booktest/pro', function (list) { // get请求, 并回调
pro = $('#pro')
// 列表, {data: [[111, 'xxx'][112, 'xxx']]}
$.each(list.data, function(index, item) {
//
pro.append('] + '">' + item[1] + '')
});
})
// 查询市的信息
$('#pro').change(function () { // 绑定pro改变事件
$.get('/booktest/city' + $(this).val(), function (list) { // this是pro的option
city = $('#city');
// 重置
city.empty().append('')
$('#dis').empty().append('')
// 字典, {data: [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]}
$.each(list.data, function(index, item) {
//
city.append(' + item.title + '');
});
});
});
//查询区县的信息
$('#city').change(function() {
$.get('/booktest/city' + $(this).val(), function(list) {
// 重置
dis = $('#dis').empty().append('');
// {data: [{'id'= 111, 'title'= 'xxx'},{'id'= 112, 'title'= 'xxx'}]}
$.each(list.data, function(index, item) {
//
dis.append(' + item.title + '');
});
});
});
});
script>
head>
<body>
<select id="pro">
<option value="0">请选择省option>
select>
<select id="city">
<option value="0">请选择市option>
select>
<select id="dis">
<option value="0">请选择区县option>
select>
body>
setting.py
配置
'tinymce',
添加 (展示于admin)
TINYMCE_DEFAULT_CONFIG = {
'theme': 'advanced',
'width': 600,
'height': 400,
}
项目下urls.py
添加 path('tinymce/', include('tinymce.urls')),
models.py
模型(可在admin界面使用)
from django.db import models
from tinymce.models import HTMLField
class BookInfo(models.Model):
book_name = models.CharField(max_length=10)
book_content = HTMLField()
def __str__(self):
return self.book_name
html页面配置:
<head>
<meta charset="UTF-8">
<title>Titletitle>
<script type="text/javascript" src='/static/tiny_mce/tiny_mce.js'>script>
<script type="text/javascript">
tinyMCE.init({
'mode':'textareas',
'theme':'advanced',
'width':400,
'height':100
});
script>
head>
<body>
<form method="post" action="content">
{% csrf_token %}
<input type="text" name="book_name">
<br>
<textarea name='book_content'>内容多行文本框textarea>
<br>
<input type="submit" value="提交">
form>
body>
通过post传递数据:
def html_content(request):
name = request.POST['book_name']
content = request.POST['book_content']
book = BookInfo()
book.book_name = name
book.book_content = content
book.save()
return render(request, 'html_content.html', {'book_content': book.book_content})
settings.py
配置:
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'TIMEOUT': 60, # 默认300s, 永久None, 立即失效0
}
}
可以缓存到django-redis-cache (默认库1)
CACHES = {
"default": {
"BACKEND": "redis_cache.cache.RedisCache",
"LOCATION": "localhost:6379",
'TIMEOUT': 60,
},
}
redis-server redis.windows.conf
redis-cli
select 1
keys *
get [键]
del [键/*]
缓存指定数据:
指定views视图:
from django.views.decorators.cache import cache_page
@cache_page(60) # 缓存指定视图
def index(request):
return render(request, 'index.html')
指定模板片段:
{% load cache %}
{% cache 60 cache_name %}
<img src="{% static '1.jpg' %}" />
{% endcache %}
指定值:
from django.core.cache import cache
# 缓存指定值
def cachetest(request):
# 设置缓存
cache.set('key', 'value', 600)
# # 获取缓存
cache_content = cache.get('key')
# 删除缓存
cache.delete('key')
# 清空缓存
cache.clear()
return HttpResponse('缓存: {}'.format(cache_content))
settings.py
配置
INSTALLED_APPS'haystack',
添加
# 搜索引擎
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.whoosh_cn_backend.WhooshEngine',
'PATH': os.path.join(BASE_DIR, 'whoosh_index'),
}
}
添加
# 自动更新 whoosh_index 索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
项目urls.py
中添加 path('search', include('haystack.urls')),
应用下创建search_indexes.py
文件
from haystack import indexes
from .models import BookInfo
class BookInfoIndex(indexes.SearchIndex, indexes.Indexable):
# 将来将对这种字段进行检索
text = indexes.CharField(document=True, use_template=True)
# 获取模型对象
def get_model(self):
return BookInfo
# 对哪些数据进行检索
def index_queryset(self, using=None):
return self.get_model().objects.all() # 检索所有数据
目录templates/search/indexes/booktest[应用名称]/
下创建BookInfo[模型类名]_text.txt
文件
# 列出要对哪些字段进行检索
{{ object.book_name }}
{{ object.book_content }}
在目录templates/search/
下创建search.html
文件
{% if query %}
搜索结果如下:
{% for result in page.object_list %}
{{ result.object.id }} {{ result.object.book_name }} {{ result.object.book_content }}
{% empty %}
没有检索到内容
{% endfor %}
{% if page.has_previous or page.has_next %}
{% if page.has_previous %}{% endif %}« 上一页{% if page.has_previous %}{% endif %}
|
{% if page.has_next %}{% endif %}下一页 »{% if page.has_next %}{% endif %}
{% endif %}
{% endif %}
在haystack安装
目录python\Lib\site-packages\haystack\backends\
下创建ChineseAnalyzer.py
文件
import jieba
from whoosh.analysis import Tokenizer, Token
class ChineseTokenizer(Tokenizer):
def __call__(self, value, positions=False, chars=False,
keeporiginal=False, removestops=True,
start_pos=0, start_char=0, mode='', **kwargs):
t = Token(positions, chars, removestops=removestops, mode=mode,
**kwargs)
seglist = jieba.cut(value, cut_all=True)
for w in seglist:
t.original = t.text = w
t.boost = 1.0
if positions:
t.pos = start_pos + value.find(w)
if chars:
t.startchar = start_char + value.find(w)
t.endchar = start_char + value.find(w) + len(w)
yield t
def ChineseAnalyzer():
return ChineseTokenizer()
在haystack安装
目录复制whoosh_backend.py
文件, 改名为whoosh_cn_backend.py
from .ChineseAnalyzer import ChineseAnalyzer
analyzer=StemmingAnalyzer()
替换为 analyzer=ChineseAnalyzer()
生成索引python manage.py rebuild_index
模板中创建搜索栏
{% comment %}
action="/search/" // 自动匹配到localhost/search/
{% endcomment %}
配置settings.py
INSTALLED_APPS
列表中添加 'djcelery',
添加
import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://127.0.0.1:6379/0'
CELERY_IMPORTS = ('booktest[应用名].task') # 将耗时任务定义到该task文件里
在应用下创建task.py
文件
封装任务: 用@task
语法糖注解
import time
from celery import task
@task
def tasktest(par):
print('hello')
time.sleep(10)
print('world')
迁移数据 python manage.py migrate
python manage.py celery worker --loglevel=info
调用:
def celery(request):
# celery调用耗时任务
tasktest.delay('参数')
return HttpResponse('hello!!!')
pip freeze > plist.txt
pip install -r plist.txt
更改settings.py
配置:
DEBUG = False
ALLOW_HOSTS=['*',]表示可以访问服务器的ip
启动服务器 (注: 使用runserver
运行的服务器, 静态文件在生产环境是无法访问的)
uWSGI
python manage.py runserver
= 生成环境: WSGI
(协议)pip install uwsgi==2.0.17
配置:
usgi.ini
文件编写(使用要去掉注释)
[uwsgi]
#socket = 127.0.0.1:8000 # 外网ip:端口 (使用nginx连接时, 使用socket)
http = 127.0.0.1:8000 # 外网ip:端口 (直接做web服务器, 使用http)
chdir = C:\Code\Python_Vir\Django_other # 项目根目录
wsgi-file = Django_other\wsgi.py # 项目中wsgi.py文件的目录, 相对于项目根目录
processes = 2 # 运行进程数
threads = 2 # 每个进程的线程数
master = True
pidfile = uwsgi.pid
daemonize = uwsgi.log
命令
uwsgi --ini uwsgi.ini
ps ajx|grep uwsgi
uwsgi --stop uwsgi.pid
uwsgi --reload uwsgi.pid
nginx
sudo apt-get install nginx
配置(版本是不同版本的nginx配置不同, 其他配置操作一样):
ps -ef | grep nginx
nginx -t
√版本一(默认安装在/usr/local/niginx
目录下):
编辑 vim conf/nginx.conf
文件, http的server下
listen 80; # 监听端口
server_name luzhuo.me; # 服务名(域名)
location / {
include uwsgi_params; # 将所有的参数转到uwsgi下
uwsgi_pass 127.0.0.1:8000; # uwsgi的ip与端口
}
location /static {
alias /var/www/django[项目名]/static/;
}
版本二(默认安装在/etc/nginx
目录下)
编辑 vim /etc/nginx/sites-enabled/default
文件, server下
# 注释掉, 另外一个listen...
listen 外网ip:80 default_server # 端口 服务名
location / {
# 注释掉, try_files ...
include uwsgi_params; # 将所有的参数转到uwsgi下
uwsgi_pass 127.0.0.1:8000; # uwsgi的ip与端口
}
location /static {
alias /var/www/django[项目名]/static/;
}
cd /var
下创建目录/var/www/django/static
sudo chmod 777 static
uwsgi.ini
文件 修改settings.py
文件
STATIC_URL='/static/'
STATIC_ROOT = '/var/www/django[项目名]/static/' # 用于nginx采集静态文件
收集项目所有静态文件, 并移到STATIC_ROOT指定目录: python manage.py collectstatic
重启nginx, uwsgi (注意顺序)
sudo sbin/nginx -v
sudo sbin/nginx
sudo sbin/nginx -s stop
sudo sbin/nginx -s reload
sudo nginx -v
sudo nginx
sudo nginx -s stop
sudo nginx -s reload
共用页面
目录选择
常用命令
python manage.py makemigrations
python manage.py migrate
如何 服务器/虚拟机里的服务器 里的网站?
python manage.py runserver 外网ip:端口
http://外网ip:端口
安装uWSGI时报错
sudo apt-get install python3.6-dev
(注意版本号, 直接在虚拟环境安装就可以了)