# 创建虚拟环境 环境位置 环境名称
mkvirtualenv -p python3 dj_pro # dj_pro 虚拟环境名字
虚拟环境操作
workon #查看运行环境
deactivate #退出虚拟环境
# 删除虚拟环境
rmvirtualenv hello_django
# 切换环境
workon py3env
软连接
# 系统中python默认版本为py2.7,可以将其改为py3
# 第一步:先删除python
rm -rf /user/bin/python
# 第二部:粗昂见软连接
ln -s /user/bin/python3 /user/bin/python
# 第三部:查看py版本是否改为py3
python -V
New Project - 选择Dango模式、选择本地项目存储目录
连接远程虚拟机的虚拟环境中的python
Tools - Deployment - Configuration
详情看文档
创建SFTP远程连接
ALLOWED_HOSTS = ['*']
#设置项是否开启URL访问地址后面不为/跳转至带有/的路径
APPEND_SLASH=True
创建static文件夹,指定为根目录
pycharm鼠标右键static文件夹(Mark Directory - Sources Root)
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'] # 添加此项
},
},
]
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
TIME_ZONE = 'Asia/Shanghai'
LANGUAGE_CODE = 'zh-hans'
在项目根目录下粗昂见media文件夹,用于存放用户上传文件
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
在根路由配置
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
#...
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
安装mysqlclient,虚拟机
sudo apt-get update # 更新ubuntu包
sudo apt-get install default-libmysql
# 启动
mysql root -u -p
#password: qwe123
方法一:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'ucan', # 使用的数据库
'USER': 'root', # 用户
'PASSWORD': '123456', # 密码
'HOST': '127.0.0.1', # mysql服务器的ip地址
'PORT': '3306' # 端口
}
}
方法二:
主目录下创建utils包(py包),里面创建dbs数据库文件夹
utils/dbs/dbs.cnf
[client]
database = django_blog
user = root
password = 123456
host = 127.0.0.1
port = 3306
default-character-set= utf8
在settings.py里配置mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
'OPTIONS': {
'read_default_file': 'utils/dbs/dbs.cnf'
}
}
}
安装插件(方法一二都要)
pip install pymysql
# djangoStudy/djangoStudy/__init__.py
import pymysql
pymysql.install_as_MySQLdb() # 数据库连接器
在 .gitignore中加入排除项,最后追加
用于存放用户session信息、验证码以及图片验证码信息等,有16个库
pip install django-redis
settings.py指定redis配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
from django.shortcuts import render,HttpResponse
from django_redis import get_redis_connection
def index(request):
# 连接池中获取连接
conn = get_redis_connection("default")
# Redis Setex 命令为指定的 key 设置值及其过期时间
conn.setex("key", 300 ,"value")
# 获取值
print(conn.get("key"))
return HttpResponse('...')
# redis
CACHES = {
# ...
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
}
# 将用户的session保存到redis数据库中
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
# 指定缓存redis的别名
SESSION_CACHE_ALIAS = 'session'
用于记录系统运行过程中的各种日志信息
跟项目创建logs文件夹,用于存放日志文件
# 配置日志器,记录网站的日志信息
LOGGING = {
# 版本
'version': 1,
# 是否禁用已存在的日志器
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
'level': 'DEBUG',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
'filename': os.path.join(BASE_DIR, "logs/dj_youkou.log"), # 日志文件的位置
'maxBytes': 300 * 1024 * 1024,
'backupCount': 10,
'formatter': 'verbose'
},
},
'loggers': {
'django': {
# 定义了一个名为django的日志器
'handlers': ['console', 'file'],
'propagate': True,
'level': 'INFO', # 日志器接收的最低日志级别
},
}
}
创建apps文件夹并指定为根目录,里面创建app,
python ../manage.py startapp user
创建应用之后,把apps目录加入到sys.path中
# settings.py
import sys
sys.path.insert(0,BASE_DIR)
sys.path.insert(1, os.path.join(BASE_DIR, 'apps'))
注册app
# settings.py
INSTALLED_APPS = [
# 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'news', # 注册
'user', # 注册
]
创建utils/modules.py,为公共基类模型
# -*- coding: utf-8 -*-
"""
----------------------------------------------------
@Time : 2019/12/22 20:42
@Author : zzw
@File : models.py
----------------------------------------------------
"""
from django.db import models
class ModelBase(models.Model):
"""
"""
create_time = models.DateTimeField(
auto_now_add=True,
verbose_name="创建时间"
)
update_time = models.DateTimeField(
auto_now=True,
verbose_name="更新时间"
)
is_delete = models.BooleanField(
default=False,
verbose_name="逻辑删除"
)
class Meta:
# 定义前模版为抽象模型类,用于其他模版继承,数据库迁移不会创建ModelBase表
abstract = True
导入基类
from django.db import models
from utils.models import ModelBase
class Articles(ModelBase):
title = models.CharField(max_length=150, verbose_name="标题", help_text="标题")
digest = models.CharField(max_length=200, verbose_name="摘要", help_text="摘要")
content = models.TextField(verbose_name="内容", help_text="内容")
clicks = models.IntegerField(default=0, verbose_name="点击量", help_text="点击量")
image_url = models.URLField(default="", verbose_name="图片url", help_text="图片url")
tag = models.ForeignKey('Tags', on_delete=models.SET_NULL, null=True)
author = models.ForeignKey('users.Users', on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['-update_time','-id'] # 排序
db_table = "tb_article" # 表名
verbose_name = "文章"
verbose_name_plural = verbose_name # 显示的复数名称
在原来django的user模型新增字段
from django.db import models
from django.contrib.auth.models import AbstractUser, UserManager as _UserManager
# 创建超级管理人默认为空修改
class UserManager(_UserManager):
"""
"""
# email=None
def create_superuser(self, username, password, email=None, **extra_fields):
super(UserManager, self).create_superuser(
username=username,
password=password,
email=email,
**extra_fields
)
# 用户表
class Users(AbstractUser):
"""
添加mobile、email_active to user modules
"""
# 添加手机号填项检测
REQUIRED_FIELDS = ['mobile']
object = UserManager() # 获取修改后的UserManager
mobile = models.CharField(
max_length=11,
unique=True,
help_text="手机号",
verbose_name="手机号",
error_messages={
"unique": "此手机号已经注册"
}
)
email_active = models.BooleanField(default=False, verbose_name="邮箱验证码")
class Meta:
db_table = 'tb_user' # 数据表名称
verbose_name = "用户" # 视图
verbose_name_plural = verbose_name # 复数形式
def __str__(self):
return self.username
settings.py配置验证用户模型
# 验证用户模型
AUTH_USER_MODEL = 'user.User'
创建用户
python manage.py createuser # 创建用户
python manage.py createsuperuser # 创建超级用户
pip install Pillow
创建验证码app-verifications,拉人apps文件夹
导入验证码模块
tools-captcha-captcha.py
# captcha.py
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import random
# 随机码 默认长度=1
def random_code(lenght=1):
code = ''
for char in range(lenght):
code += chr(random.randint(65, 90))
return code
# 随机颜色 默认颜色范围【1,255】
def random_color(s=1, e=255):
return (random.randint(s, e), random.randint(s, e), random.randint(s, e))
# 生成验证码图片
# length 验证码长度
# width 图片宽度
# height 图片高度
# 返回验证码和图片
def veri_code(lenght=4, width=160, height=40):
# 创建Image对象
image = Image.new('RGB', (width, height), (255, 255, 255))
# 创建Font对象
font = ImageFont.truetype('Arial.ttf', 32)
# 创建Draw对象
draw = ImageDraw.Draw(image)
# 随机颜色填充每个像素
for x in range(width):
for y in range(height):
draw.point((x, y), fill=random_color(64, 255))
# 验证码
code = random_code(lenght)
# 随机颜色验证码写到图片上
for t in range(lenght):
draw.text((40 * t + 5, 5), code[t], font=font, fill=random_color(32, 127))
# 模糊滤镜
image = image.filter(ImageFilter.BLUR)
return code, image
在settings.py文件中配置verify库
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
"verify": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1", # 1库
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
}
uuid是128位的全局唯一标识符,通常由32字节的字符串表示,保证时间和 空间的唯一性
from django.urls import path
from . import views
urlpatterns = [
path('image_code//' , views.ImageCode),
]
验证码应用下创建constants.py文件,存放全局参数
# 图片验证码redis有效期,单位秒
IMAGE_CODE_EXPIRES = 5 * 60
from django.shortcuts import render
from django.http import HttpResponse
from django.views import View
from utils.captcha.captcha import captcha
from django_redis import get_redis_connection
from . import constants
class ImageCode(View):
"""
/image_code//
"""
def get(self, request, image_code_id):
text, image = captcha.generate_captcha()
# print(text)
# 建立redis,将图片验证码保存到redis
con_redis = get_redis_connection(alias='verify')
img_key = "img_{}".format(image_code_id)
con_redis.setex(img_key, constants.IMAGE_CODE_EXPIRES, text)
# 指定图片格式,并把图片返回前端
return HttpResponse(content=image,content_type='images/jpg')
import logging
# 导入日志器
logger = logging.getLogger('django')
class ImageCode(View):
"""
/image_code//
"""
def get(self, request, image_code_id):
text, image = captcha.generate_captcha()
# print(text)
# 建立redis,将图片验证码保存到redis
con_redis = get_redis_connection(alias='verify')
img_key = "img_{}".format(image_code_id)
con_redis.setex(img_key, constants.IMAGE_CODE_EXPIRES, text)
logger.info('img_code:{}'.format(text)) # 把验证码记录在logger日志中
# 指定图片格式,并把图片返回前端
return HttpResponse(content=image, content_type='images/jpg')
js生成uuid请求接口
注册路由
from django.urls import path, re_path
from . import views
urlpatterns = [
path('image_code//' , views.ImageCode.as_view(), name='image_code'),
re_path('username/(?P\w{5,20})/' , views.CheckUsernameView.as_view(), name='check_username'),
]
添加视图
from users.models import Users
class CheckUsernameView(View):
"""
检测用户是否存在
"""
def get(self, request, username):
count = Users.objects.filter(username=username).count()
data = {
username: username,
count: True if count else False
}
return HttpResponse(data)
# 图片验证码redis有效期,单位秒
IMAGE_CODE_EXPIRES = 5 * 60
# 短信验证码有效期,单位分钟
SMS_CODEREDIS_EXPIRES = 5
# 发送间隔
SEND_SMS_CODE_INTERVAL = 60
# 短信发送模版
SMS_CODE_TEMP_ID = 1
# 短信验证码位数
SMS_CODE_NUMS = 6
from django.core.validators import RegexValidator
from django_redis import get_redis_connection
from django import forms
from users.models import Users
# 手机号验证,即错误返回
mobile_validator = RegexValidator(r'^1[3-9]\d{9}$', '手机格式不正确')
class CheckImgCodeForm(forms.Form):
mobile = forms.CharField(
max_length=11,
min_length=11,
validators=[mobile_validator],
error_messages={
'min_length': '手机号长度有误',
'max_length': '手机号长度有误',
'required': '手机号不能为空'
}
)
image_code_id = forms.UUIDField(
error_messages={
'required': '图片uuid不能为空'
}
)
text = forms.CharField(
max_length=4,
min_length=4,
error_messages={
'min_length': '手机号长度有误',
'max_length': '手机号长度有误',
'required': '手机号不能为空'
}
)
# clean_xxx 固定形式,检测某个字段
# 判断手机号是否注册
def clean_mobile(self):
mobile = self.cleaned_data.get('mobile')
count = Users.objects.filter(mobile=mobile).count()
if count:
# 抛出异常
raise forms.ValidationError('手机号已注册')
return mobile
# 联合判断
# 判断是否重复获取验证码
def clean(self):
cleaned_data = super().clean()
mobile = cleaned_data.get('mobile')
image_uuid = cleaned_data.get('image_code_id')
image_text = cleaned_data.get('text').upper()
# 获取redis存储的图片验证码
con_redis = get_redis_connection(alias='verify_code')
img_key = "img_{}".format(image_uuid)
real_image_code = con_redis.get(img_key)
# 数据不为空转码
if not real_image_code:
real_image_code = ''
else:
# 解码转义
real_image_code = real_image_code.decode()
# 判断验证码正确性
if (not real_image_code) or(image_text.upper() != real_image_code.upper()):
raise forms.ValidationError("图片验证码验证失败")
# 判断60秒内是否有发送短信记录
sms_flag_fmt = "sms_flag_{}".format(mobile).encode('utf8')
sms_flag = con_redis.get(sms_flag_fmt)
# 验证码已经存在不允许再次获取
if sms_flag:
raise forms.ValidationError("获取短信验证码过于频繁")
import requests
# APIID
account = 'C40665997'
# APIKEY
password = 'ebe6a6b70cd80a0bd3151f2170b2c7e9'
url = 'https://106.ihuyi.com/webservice/sms.php?method=Submit'
"""
短信发送
"""
def send_sms(mobile, content):
headers = {
"Content-type": "application/x-www-form-urlencoded",
"Accept": "text/plain"
}
data = {
"account": account,
"password": password,
"mobile": mobile,
"content": content,
"format": "json"
}
resp = requests.post(
url=url,
headers=headers,
data=data
)
return resp.content.decode()
if __name__ == '__main__':
mobile = '13059224121'
content = '您的验证码是:222222。请不要把验证码泄露给其他人。'
result = send_sms(mobile, content)
print(result)
import json
import random
import string
from . import forms
class SmsCodesView(View):
"""
发送手机验证码
POST /sms_codes/
"""
def post(self, request):
json_data = request.body
# 参数为空判断
if not json_data:
return result_json(msg="参数为空")
dict_data = json.loads(json_data)
# 检测验证码是否重复
form = forms.CheckImgCodeForm(data=dict_data)
# 判断验证结果
if form.is_valid():
# 获取手机号
mobile = form.cleaned_data.get('mobile')
# 创建6位验证码
sms_num = ''.join([random.choice(string.digits) for _ in range(6)])
# 保存短信验证码
redis_con = get_redis_connection(alias='verify_code')
sms_flag_fmt = "sms_flag_{}".format(mobile)
sms_text_fmt = "sms_{}".format(mobile)
# 短信验证码发送60秒
# redis_con.setex(sms_flag_fmt, constants.SEND_SMS_CODE_INTERVAL, 1)
# 短信验证码有效5分钟
# redis_con.setex(sms_text_fmt, constants.SMS_CODE_REDIS_EXPIRES, sms_num)
# 通过管道调用
p1 = redis_con.pipeline()
try:
p1.setex(sms_flag_fmt, constants.SEND_SMS_CODE_INTERVAL, 1)
p1.setex(sms_text_fmt, constants.SMS_CODE_REDIS_EXPIRES, sms_num)
p1.execute()
except Exception as e:
err = "redis 执行出现异常:{}".format(e)
logging.error(err)
return result_json(msg=err)
else:
# 模拟发送短信
logger.info('发送短信成功[mobile:%s,sms_code:%s]' % (mobile, sms_num))
return result_json(msg='发送短信码成功', code=0)
# content = "您的验证码是:{}。请不要把验证码泄露给其他人。".format(sms_num)
# try:
# result = send_sms(mobile, content)
# except Exception as e:
# err = "发送短信验证码异常[mobile:%s,message:%s]".format(mobile,e)
# logging.error(err)
# return result_json(msg=err)
# else:
# if result.code == 2:
# logger.info('发送短信成功[mobile:%s,sms_code:%s]' % (mobile, sms_num))
# return result_json(msg='发送短信验证码成功', code=0)
# else:
# logging.error('发送短信失败[mobile:%s,sms_code:%s]' % (mobile, result.msg))
# return result_json(msg='发送短信验证码失败')
else:
# 获取错误信息
error_list = [] # 把具体的form表单验证错误信息 序列化成字符串 复制给err_msg
for item in form.errors.get_json_data().values():
error_list.append(item[0].get('message'))
err_str = '/'.join(error_list)
return result_json(msg=err_str)
看官方文档 :https://docs.docker.com/install/linux/docker-ce/ubuntu/
镜像:https://lug.ustc.edu.cn/wiki/mirrors/help/docker
a.获取镜像
# 拉取镜像到本地仓库
docker image pull delron/elasticsearch-ik:2.4.6-1.0
# 查看本地仓库是否有这个镜像
docker images
或docker image ls
b.将百度云盘中的elasticsearch.zip文件传到虚拟机中的家目录,然后unzip解压。在虚拟机中的elasticsearch/config/elasticsearch.yml第54行,更改ip地址为0.0.0.0,端口改为8002,默认端口为9200
# 在xshell中使用rz命令将elasticsearch.zip文件传到虚拟机的家目录中
#然后在家目录中解压
unzip elasticsearch.zip
cd ~/elasticsearch/config
# network.host: 172.18.168.123
network.host: 0.0.0.0
#
# Set a custom port for HTTP:
#
http.port: 8002
c.创建docker容器并运行
# pwd获取当前的路径替换
# 根据拉取到本地的镜像创建容器,需要将/home/pyvip/elasticsearch/config配置文件所在目录修改为你自己的路径
docker run -dti --network=host --name=elasticsearch -v /home/pyvip/elasticsearch/config:/usr/share/elasticsearch/config delron/elasticsearch-ik:2.4.6-1.0
# 查看是否创建成功
docker container ls -a
# 如果STATUS为Up则创建容器成功
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b254fe1ee0eb delron/elasticsearch-ik:2.4.6-1.0 "/docker-entrypoint.…" 3 days ago Up 3 days elasticsearch
# 启动容器
sudo docker start 容器id
# 运行如下命令,如果有显示则elasticsearch配置成功
curl 127.0.0.1:8002
d.进入项目虚拟环境中,安装相关包
# 进入项目虚拟环境
workon myblog_env
pip install django-haystack
pip install elasticsearch==2.4.1
e.在settings.py文件中加入如下配置:
INSTALLED_APPS = [
'haystack',
]
ELASTICSEARCH_DSL = {
'default': {
'hosts': '127.0.0.1:8002'
},
}
# Haystack
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://127.0.0.1:8002/', # 此处为elasticsearch运行的服务器ip地址,端口号默认为9200
'INDEX_NAME': 'dj_pre_class', # 指定elasticsearch建立的索引库的名称
},
}
# 设置每页显示的数据量
HAYSTACK_SEARCH_RESULTS_PER_PAGE = 5
# 当数据库改变时,会自动更新索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
在文章app下创建search_indexes.py
# -*- coding: utf-8 -*-
"""
----------------------------------------------------
@Time : 2019/12/29 9:47
@Author : zzw
@File : search_indexes.py
----------------------------------------------------
"""
from haystack import indexes
from .models import Articles
# 修改此处,类名为模型类的名称+Index,比如模型类为GoodsInfo,则这里类名为GoodsInfoIndex
class ArticlesIndex(indexes.SearchIndex,indexes.Indexable):
"""
文章搜索引擎
"""
# text是固定格式,其他与文章类以一一对应
text = indexes.CharField(document=True,use_template=True)
# 返回的数据
id = indexes.IntegerField(model_attr='id')
title = indexes.CharField(model_attr='title')
digest = indexes.CharField(model_attr='digest')
content = indexes.CharField(model_attr='content')
image_url = indexes.CharField(model_attr='image_url')
clicks = indexes.IntegerField(model_attr='clicks')
def get_model(self):
"""
返回简历索引的模型类
"""
return Articles
def index_queryset(self, using=None):
"""
返回简历索引的数据查询集
"""
return self.get_model().objects.filter(is_delete=False,tag_id__in=[1,2,3,4,5,6])
在创建文件template/search/indexes/articles/articles_text.txt
# articles 文章类
# articles_text.txt 固定格式 xxx_text.txt
# 指定搜索内容
{
{
object.title }}
{
{
object.digest }}
{
{
object.content }}
模版类修改
from haystack.views import SearchView as _SearchView
class SearchView(_SearchView):
def create_response(self):
keyword = self.request.GET.get('q','')
if not keyword:
show_all = True
try:
tag_id = int(self.request.GET.get('tag_id', 0))
except Exception as e:
logger.error("文章标签错误:\n{}".format(e))
tag_id = 0
try:
page = int(self.request.GET.get('page', 1))
except Exception as e:
logger.error("当前页数错误:\n{}".format(e))
page = 1
try:
pagesize = int(self.request.GET.get('pagesize', constants.PER_PAGE_ARTICLES_COUNT))
except Exception as e:
logger.error("当前页码数错误:\n{}".format(e))
pagesize = constants.PER_PAGE_ARTICLES_COUNT
# 获取文章
# tag__xxx 获取外键表的字段
articles_queryset = Articles.objects.select_related('tag', 'author') \
.only('id', 'title', 'digest', 'image_url', 'create_time', 'update_time', 'tag__name',
'author__username')
# 根据tag_id=0搜索文章不存在时,搜索全部
articles = articles_queryset.filter(is_delete=False, tag_id=tag_id) or \
articles_queryset.filter(is_delete=False)
# 分页
paginator = Paginator(articles, pagesize)
try:
articles_info = paginator.page(page)
except EmptyPage:
# 用户访问页数大于实际页面,取最后一页
logging.info("用户访问页数大于总页数")
articles_info = paginator.page(paginator.num_pages)
# 序列化输出
articles_info_list = []
for item in articles_info:
articles_info_list.append({
'article_id': item.id,
'title': item.title,
'digest': item.digest,
'image_url': item.image_url,
'tag_name': item.tag.name,
'author': item.author.username,
# 时间格式化
'update_time': item.update_time.strftime('%Y-%m-%d %H:%M'),
'create_time': item.create_time.strftime('%Y-%m-%d %H:%M')
})
return result_json(
code=0,
data=articles_info_list,
total_page=paginator.num_pages,
page=page,
count=paginator.count
)
else:
show_all = False
qs = super(SearchView,self).create_response()
return qs
添加到路由
from django.urls import path, re_path
from . import views
urlpatterns = [
path('search_article/', views.SearchView(), name='search_article'),
]
切换到项目虚拟环境
服务器上切换项目文件夹,创建索引表
python manage.py rebuild_index -k 1 # 创建索引表
python manage.py update_index -k 1 # 更新索引表
重构引擎方法
from haystack.views import SearchView as _SearchView
class SearchView(_SearchView):
def create_response(self):
kw = self.request.GET.get('q', '')
try:
page = int(self.request.GET.get('page', 1))
except Exception as e:
logger.error("当前页数错误:\n{}".format(e))
page = 1
try:
pagesize = int(self.request.GET.get('pagesize', constants.PER_PAGE_ARTICLES_COUNT))
except Exception as e:
logger.error("当前页码数错误:\n{}".format(e))
pagesize = constants.PER_PAGE_ARTICLES_COUNT
if not kw:
articles_queryset = Articles.objects.select_related('tag', 'author') \
.only('id', 'title', 'digest', 'image_url', 'create_time', 'update_time', 'tag__name',
'author__username')
articles = articles_queryset.filter(is_delete=False)
# 分页
paginator = Paginator(articles, pagesize)
try:
articles_info = paginator.page(page)
except EmptyPage:
# 用户访问页数大于实际页面,取最后一页
logging.info("用户访问页数大于总页数")
articles_info = paginator.page(paginator.num_pages)
# 序列化输出
articles_info_list = []
for item in articles_info:
articles_info_list.append({
'article_id': item.id,
'title': item.title,
'digest': item.digest,
'image_url': item.image_url,
'tag_name': item.tag.name,
'author': item.author.username,
# 时间格式化
'update_time': item.update_time.strftime('%Y-%m-%d %H:%M'),
'create_time': item.create_time.strftime('%Y-%m-%d %H:%M')
})
return result_json(
code=0,
data=articles_info_list,
total_page=paginator.num_pages,
page=page,
count=paginator.count
)
else:
context = super().get_context()
# 分页
paginator = Paginator(context['page'], pagesize)
try:
articles_info = paginator.page(page)
except EmptyPage:
# 用户访问页数大于实际页面,取最后一页
logging.info("用户访问页数大于总页数")
articles_info = paginator.page(paginator.num_pages)
articles_info_list = []
for item in articles_info:
articles_info_list.append({
'article_id': item.id,
'title': item.title,
'digest': item.digest,
'image_url': item.image_url,
'tag_name': item.object.tag.name,
'author': item.object.author.username,
'clicks': item.clicks,
# 时间格式化
'update_time': item.update_time.strftime('%Y-%m-%d %H:%M'),
'create_time': item.create_time.strftime('%Y-%m-%d %H:%M')
})
return result_json(
code=0,
data=articles_info_list,
total_page=paginator.num_pages,
page=page,
count=paginator.count
)
setting.py
# 站点域名信息
SITE_DOMAIN_PORT = 'http://127.0.0.1:8000'
导入
from django.conf import settings
from django.http import FileResponse
from django.utils.encoding import escape_uri_path
class DocDownload(View):
"""
下载文档
GET doc_download/
"""
def get(self, request, doc_id):
# 找到当前id的doc文档
doc = Doc.objects.only('file_url').filter(is_delete=False, id=doc_id)
if doc:
# 拼接路径名称
doc_url = settings.SITE_DOMAIN_PORT + doc.first().file_url
# 请求下载的文档
try:
# stream=True 分流请求下载
res = FileResponse(
requests.get(doc_url,stream=True)
)
# 下载方式二:open
except Exception as e:
err_txt = "文档下载失败:{}".format(e)
logging.info(err_txt)
return result_json(msg=err_txt)
# 获取文档后缀名,根据后缀名返回相应的请求
ex_name = doc_url.split('.')[-1] # 后缀名从文件路由切割
if not ex_name:
err_txt = "文档类型错误"
logger.info(err_txt)
return result_json(msg=err_txt)
else:
# 后缀名一律转为小写
ex_name = ex_name.lower()
if ex_name == "pdf":
res["Content-type"] = "application/pdf"
elif ex_name == "zip":
res["Content-type"] = "application/zip"
elif ex_name == "doc":
res["Content-type"] = "application/msword"
elif ex_name == "xls":
res["Content-type"] = "application/vnd.ms-excel"
elif ex_name == "docx":
res["Content-type"] = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
elif ex_name == "ppt":
res["Content-type"] = "application/vnd.ms-powerpoint"
elif ex_name == "pptx":
res["Content-type"] = "application/vnd.openxmlformats-officedocument.presentationml.presentation"
elif ex_name == 'txt':
res["Content-type"] = "text/plain"
else:
raise result_json(msg="文档格式不正确!")
# 中文文件名编译
doc_filename = escape_uri_path(doc_url.split('/')[-1])
# 返回给浏览器相应的参数,编译中文会在浏览者转回中文
res["Content-Disposition"] = "attachment; filename*=UTF-8''{}".format(doc_filename)
# 设置为inline,会直接打开
# res["Content-Disposition"] = "设置为inline; filename*=UTF-8''{}".format(doc_filename)
return res
else:
raise result_json(msg="文档不存在")
# 安装tracker
docker run -dti --network=host --name tracker -v /var/fdfs/tracker:/var/fdfs youkou1/fastdfs tracker
# 安装storage
# 修改172.18.168.123 用ip a获取
# 不能写127.0.0.1
docker run -dti --network=host --name storage -e TRACKER_SERVER=172.18.168.123:22122 -v /var/fdfs/storage:/var/fdfs youkou1/fastdfs storage
创建utils/fastdfs/logs日志文件夹,用于存放日志信息
创建utils/fastdfs/client.conf配置文件
修改path to store路径 : base_path=utils/fastdfs/logs
ip a获取的地址,端口不改
修改ip address路径 : tracker_server=10.0.2.15:22122
# 创建utils/fastdfs/client.conf配置文件
# connect timeout in seconds
# default value is 30s
connect_timeout=30
# network timeout in seconds
# default value is 30s
network_timeout=60
# the base path to store log files
# 修改相对路径对这log文件夹右键copy relative Path !!!
base_path=/Users/ninyoukou/PycharmProjects/dj_pre_class/utils/fastdfs/logs
# tracker_server can ocur more than once, and tracker_server format is
# "host:port", host can be hostname or ip address
tracker_server=172.18.168.123:22122
#standard log level as syslog, case insensitive, value list:
### emerg for emergency
### alert
### crit for critical
### error
### warn for warning
### notice
### info
### debug
log_level=info
# if use connection pool
# default value is false
use_connection_pool = false
# connections whose the idle time exceeds this time will be closed
# unit: second
# default value is 3600
connection_pool_max_idle_time = 3600
# if load FastDFS parameters from tracker server
# default value is false
load_fdfs_parameters_from_tracker=false
# if use storage ID instead of IP address
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
# default value is false
use_storage_id = false
# specify storage ids filename, can use relative or absolute path
# same as tracker.conf
# valid only when load_fdfs_parameters_from_tracker is false
storage_ids_filename = storage_ids.conf
#HTTP settings
http.tracker_server_port=80
进入虚拟环境,把包fdfs_client.zip进去
安装相关包
# 安装相关包
# fdfs_client.zip文件从百度云中下载
pip install fdfs_client.zip
pip install mutagen
pip install requests
测试是否成功
服务器项目所在地 python manage.py shell
from fdfs_client.client import Fdfs_client
# 指定fdfs客户端配置文件所在路径
FDFS_Client = Fdfs_client('utils/fastdfs/client.conf')
ret = FDFS_Client.upload_by_filename('media/youkou.jpg')
ret # 获取打印数据
{
'Group name': 'group1', 'Remote file_id': 'group1/M00/00/00/CgACD14R7N-Af7y0AAfh_rrm7jw568.png', 'Status': 'Upload successed.', 'Local file name': 'media/2018.png', 'Uploaded size': '504.00KB', 'Storage IP': '10.0.2.15'}
# 访问图片
http://127.0.0.1:8888/group1/M00/00/00/CgACD14R7N-Af7y0AAfh_rrm7jw568.png
拉下docker
docker pull youkou1/fastdfs