qq登录
dev
QQ_CLIENT_ID = '101792382' # 应用ID
QQ_REDIRECT_URI = 'http://demo.myuxi.wang/oauth_callback.html' # 回调网址
QQ_STATE = '/wap/member/member.html' # 会员中心
QQ_CLIENT_SECRET = '09a0d1903eb475f6c9e7181048a8e7b0' # 应用秘钥
qq认证类
import json
import logging
import urllib
from urllib.request import urlopen
from django.conf import settings
from rest_framework.serializers import Serializer
from oauth.constants import BING_USER_ACCESS_TOKEN_EXPIRES
from oauth.excepterror import OauthQQAPIError
logger = logging.getLogger(‘django’)
from itsdangerous import TimedJSONWebSignatureSerializer as TJWSSerializer, BadData
class OAuthQQ(object):
"""
QQ认证辅助工具类
"""
def __init__(self, client_id=None, client_secret=None, redirect_uri=None, state=None):
self.client_id = client_id if client_id else settings.QQ_CLIENT_ID
self.redirect_uri = redirect_uri if redirect_uri else settings.QQ_REDIRECT_URI
# self.state = state if state else settings.QQ_STATE
self.state = state or settings.QQ_STATE
self.client_secret = client_secret if client_secret else settings.QQ_CLIENT_SECRET
def get_login_url(self):
url = 'https://graph.qq.com/oauth2.0/authorize?'
params = {
'response_type': 'code',
'client_id': self.client_id,
'redirect_uri': self.redirect_uri,
'state': self.state # 登陆后默认跳转地址
}
url += urllib.parse.urlencode(params)
# print(url)
return url
def get_login_token(self, code):
url = 'https://graph.qq.com/oauth2.0/token?'
params = {
'grant_type': 'authorization_code',
'client_id': self.client_id,
'client_secret': self.client_secret,
'code': code # 登陆后默认跳转地址
, 'redirect_uri': self.redirect_uri
}
url += urllib.parse.urlencode(params)
try:
resp = urlopen(url) # 发送请求
print(type(resp))
resp_data = resp.read().decode() # str
print('resp_data', resp_data)
print(type(resp_data))
# 吧 str转换成字典类型,一层获取他的token
resp_dict = urllib.parse.parse_qs(resp_data)
print(resp_dict)
except Exception as e:
logger.error("获取access_token异常: %s" % e)
raise OauthQQAPIError
else:
access_token = resp_dict.get("access_token")
print('action_token', access_token)
return access_token[0]
def get_login_opeid(self, access_token):
url = 'https://graph.qq.com/oauth2.0/me?access_token=' + access_token
# url返回的数据callback( {"client_id":"YOUR_APPID","openid":"YOUR_OPENID"} );
try:
resp = urlopen(url)
resp_data = resp.read().decode()
resp_data = resp_data[10:-4]
print('resp_date', resp_data)
resp = json.loads(resp_data)
except Exception:
logger.error("获取opend_id异常")
raise OauthQQAPIError
else:
openid = resp.get('openid')
print(openid, "openid")
return openid
def get_noteist_user(self, openid):
s = TJWSSerializer(settings.SECRET_KEY, BING_USER_ACCESS_TOKEN_EXPIRES) # 配置 (秘钥,有效期
token = s.dumps({"openid": openid})
return token.decode()
@staticmethod
def check_noteist_user(access_token):
s = TJWSSerializer(settings.SECRET_KEY, BING_USER_ACCESS_TOKEN_EXPIRES) # 配置 (秘钥,有效期
try:
data = s.loads(access_token)
print(data['openid'])
except BadData:
return None
else:
print('openidlllllll',data['openid'])
return data['openid']
设置序列化器
#! /usr/bin/env python
# -*- coding: utf-8 -*-
# author:weizi
from django_redis import get_redis_connection
from rest_framework import serializers
from oauth.models import OAuthQQUser
from oauth.utils import OAuthQQ
from users.models import User
class OAuthQQUserSerialzier(serializers.ModelSerializer):
"""创建或绑定用户的序列化器"""
mobile = serializers.RegexField(label="手机号", regex=r'1[3-9]\d{9}$')
sms_code = serializers.CharField(label="短信验证码", write_only=True)
tuser = serializers.CharField(label="推荐码", write_only=True, allow_null=True, allow_blank=True)
access_token = serializers.CharField(label="操作凭证", write_only=True)
token = serializers.CharField(label="JWT token", read_only=True) # JWT
class Meta:
model = User
fields = ("id","username", "password", "tuser", "sms_code", "mobile", "promoter", "promocode", "token",'access_token')
extra_kwargs = {
'username': {
'read_only':True
},
'password': {
'write_only': True,
'min_length': 6,
'max_length': 20,
'error_messages': {
'min_length': '仅允许6-20个字符的密码',
'max_length': '仅允许6-20个字符的密码',
}
},
'tuser': {
'max_length': 11,
'error_messages': {
'max_length': '仅允许最大11个字符的推荐码',
}
}
}
def validate(self, data):
access_token = data['access_token']
openid = OAuthQQ.check_noteist_user(access_token)
if not openid:
raise serializers.ValidationError("无效的access_token")
data['openid'] = openid
"""判断短信验证码"""
redis_conn = get_redis_connection('verify_codes')
mobile = data["mobile"]
real_sms_code = redis_conn.get("sms_%s" % mobile)
if real_sms_code is None:
raise serializers.ValidationError("无效的验证码")
if data["sms_code"] != real_sms_code.decode(): # 从redis取出来的数据都是二进制 必须进行解码
raise serializers.ValidationError("短信验证码错误")
# 如果用户存在,检查用户手机对应的密码是否正确
try:
user = User.objects.get(mobile=mobile)
except User.DoesNotExist:
pass
else:
password = data['password']
if not user.check_password(password):
raise serializers.ValidationError("密码错误")
data['user'] = user
return data
def create(self, validated_data):
"""重写保存方法 密码也要加密"""
print(validated_data)
openid = validated_data['openid']
user = validated_data.get('user') # 如果用户不存在 None
mobile = validated_data['mobile']
password = validated_data['password']
promocode = validated_data['tuser']
try:
upuser = User.objects.get(promocode=promocode)
except User.DoesNotExist:
promoter = 0
else:
promoter = upuser.id
# 判断用户是否存在
if not user:
user = User.objects.create_user(username=mobile,mobile=mobile,password=password,promoter=promoter) # create_user 底层自动会把密码加密保存
OAuthQQUser.objects.create(user=user, openid=openid)
# 颁发令牌
from rest_framework_jwt.settings import api_settings # 切记 不要导错包
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
user.token = token # 赋值 颁发token
return user
view
from django.shortcuts import render
# Create your views here.
from rest_framework import status
from rest_framework.generics import CreateAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from django.http import HttpResponse
from oauth.excepterror import OauthQQAPIError
from oauth.models import OAuthQQUser
from oauth.serializers import QqUserSerializer
from oauth.seriallizers1 import OAuthQQUserSerialzier
from oauth.utils import OAuthQQ
# 跳转到qq登陆页面
class OauthView(APIView):
def get(self, request):
# city = request.args.get('c')
next = request.query_params.get('next')
# print(city)
oauth_QQ = OAuthQQ(state=next) # 这里的state是为了登陆成功后返回的url
login_url = oauth_QQ.get_login_url()
print(login_url)
return Response({"login_url": login_url})
class QqUserView(CreateAPIView):
def get(self, request):
code = request.query_params.get('code')
if not code:
return Response({"status": "缺少code"}, status=status.HTTP_400_BAD_REQUEST)
try:
oauth_QQ = OAuthQQ(state=next)
oauth_token = oauth_QQ.get_login_token(code)
openid = oauth_QQ.get_login_opeid(oauth_token)
#return Response({'openid':openid})
print('view里面的openid',openid)
except OauthQQAPIError:
return Response({"emssage": "qq接口出现问题"},
status = status.HTTP_503_SERVICE_UNAVAILABLE)
try:
oauth_qq_user = OAuthQQUser.objects.get(openid=openid)
except OAuthQQUser.DoesNotExist:
# 如果openid数据不存在,处理这个openid并返回
access_token =oauth_QQ.get_noteist_user(openid)
# 用户自己生成的access_token
return Response({'access_token': access_token})
else:
# 如果数据存在,表示用户已经绑定过身份 签发token
# 颁发令牌
from rest_framework_jwt.settings import api_settings # 切记 不要导错包
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
user = oauth_qq_user.user
payload = jwt_payload_handler(user)
token = jwt_encode_handler(payload)
print(token)
return Response({'token': token, 'username': user.username, 'user_id': user.id})
serializer_class = OAuthQQUserSerialzier
def post(self,request,*args,**kwargs):
return super().post(request,*args, **kwargs)
# return Response(request.data)
记得配置对应的urls
urlpatterns = [
re_path(r’^qq/authorization/
′
,
v
i
e
w
s
.
O
a
u
t
h
V
i
e
w
.
a
s
v
i
e
w
(
)
)
,
r
e
p
a
t
h
(
r
′
q
q
/
u
s
e
r
/
', views.OauthView.as_view()), re_path(r'^qq/user/
′,views.OauthView.asview()),repath(r′qq/user/’, views.QqUserView.as_view()),
]
七牛云存储
dev
‘’’
qiniustorage app注册
#七牛存储
QINIU_ACCESS_KEY = 'nyXcuSxtLRxBX2SRFphG2DjNDuEFFkyCqHRrr7I2'
QINIU_SECRET_KEY = 'FWpyEKyAivREU_H9amdOo2LEGvNvVmOO4Hc-beKh'
QINIU_BUCKET_NAME = 'yxwall'
QINIU_BUCKET_DOMAIN = 'qglcu1e3u.hb-bkt.clouddn.com/' # 解析的域名
QINIU_SECURE_URL = False # 使用http
PREFIX_URL = 'http://' # 前缀协议是否用https
MEDIA_URL = PREFIX_URL + QINIU_BUCKET_DOMAIN + '/media/'
# MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_ROOT = 'media'
STATIC_URL = QINIU_BUCKET_DOMAIN + '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
# STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATIC_ROOT = "static" # 静态文件收集
DEFAULT_FILE_STORAGE = 'qiniustorage.backends.QiniuMediaStorage'
STATICFILES_STORAGE = 'qiniustorage.backends.QiniuStaticStorage'
'''
python manage.py collectstatic 命令收集
白名单配置
添加白名单
pip install django-cors-headers
INSTALLED_APPS = (
…
‘corsheaders’,
…
)
MIDDLEWARE = [
‘corsheaders.middleware.CorsMiddleware’,
…
]
CORS_ORIGIN_WHITELIST = (
'http://127.0.0.1',
'http://localhost',
'http://m.myuxi.com',
'http://api.myuxi.com',
'http://demo.myuxi.wang',
)
CORS_ALLOW_CREDENTIALS = True # 允许携带cookie
定时任务
# 这里的目录是在www中生成index.html静态文件 定时生成
GENERATED_STATIC_HTML_FILES_DIR = os.path.join(os.path.dirname(os.path.dirname(BASE_DIR)), 'www')
# 解决crontab中文问题
CRONTAB_COMMAND_PREFIX = 'LANG_ALL=zh_cn.UTF-8'
# 定时任务
CRONJOBS = [
# 每2分钟执行一次生成主页静态文件
('*/5 * * * *', 'contents.crons.generate_static_index_html',
'>> /home/sixstar/Desktop/MyxAll/myuxiwall/logs/crontab.log')
]
import sys
sys.path.insert(0, '../')
sys.path.insert(0, '../myuxiwall/apps')
# sys.path.insert(0, '../myuxiwall/utils')
if not os.getenv('DJANGO_SETTINGS_MODULE'):
os.environ['DJANGO_SETTINGS_MODULE'] = 'myuxiwall.settings.dev'
import django
django.setup() # 项目初始化
# 防止找不到路径,进在系统路径中添加
from django.conf import settings
from django.template import loader
from goods.models import SKU
def generate_static_index_html(sku_id):
"""
生产静态首页 http://demo.myuxi.wang/index.html
:return:
"""
# 查询各个数据表的数据 组成复杂的数据
sku = SKU.objects.get(id=sku_id)
sku.image = sku.skuimage_set.all()
Goods ={
"sku":sku
}
# 制定一个首页的模板 ,templates是一个链接对象明白
templates = loader.get_template('detail_temp.html')
# 吧goods数据写入这个模板中
html_text = templates.render(Goods)
temp_path = os.path.join(settings.GENERATED_STATIC_HTML_FILES_DIR, 'wap/shop/goods/'+str(sku_id)+'.html')
print("ok")
with open(temp_path,'w') as f:
f.write(html_text)
if __name__ == '__main__':
skus =SKU.objects.all()
for sku in skus:
print(sku.default_image_url)
generate_static_index_html(sku.id)
python manage.py crontab add 添加定时任务
python manage.py crontab show 查看
日志
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在debug模式下才输出日志
'()': '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(os.path.dirname(BASE_DIR), "logs/yxmall.log"), # 日志文件的位置
'maxBytes': 300 * 1024 * 1024,
'backupCount': 10,
'formatter': 'verbose'
},
},
'loggers': { # 日志器
'django': { # 定义了一个名为django的日志器
'handlers': ['console', 'file'], # 可以同时向终端与文件中输出日志
'propagate': True, # 是否继续传递日志信息
'level': 'INFO', # 日志器接收的最低日志级别
},
}
}
搜索引擎
Haystack
dev下
HAYSTACK_CONNECTIONS = {
'default': {
'ENGINE': 'haystack.backends.elasticsearch_backend.ElasticsearchSearchEngine',
'URL': 'http://192.168.1.100:9200/', # 此处为elasticsearch运行的服务器ip地址,端口号固定为9200
'INDEX_NAME': 'myuxi', # 指定elasticsearch建立的索引库的名称
},
}
# 当添加、修改、删除数据时,自动生成索引
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# HAYSTACK_SEARCH_RESULTS_PER_PAGE = 2
设置搜索索引
之前会需要docker运行一个容器
之后
pip install drf-haystack
pip install elasticsearch==2.4.1
INSTALLED_APPS = [
…
‘haystack’,
…
]
新建一个search_index.py
class SKUIndex(indexes.SearchIndex, indexes.Indexable):
"""
SKU索引数据模型类
"""
text = indexes.CharField(document=True, use_template=True)
id = indexes.CharField(model_attr='id')
name = indexes.CharField(model_attr='name')
price = indexes.DecimalField(model_attr='price')
default_image_url = indexes.CharField(model_attr='default_image_url')
comments = indexes.IntegerField(model_attr='comments')
def get_model(self):
return SKU
def index_queryset(self, using=None):
return self.get_model().objects.filter(is_launched=True)
序列化器指定
from goods.search_indexes import SKUIndex
class SKUIndexSerializer(HaystackSerializer):
class Meta:
index_classes = [SKUIndex]
filter = ('text', 'id', 'name', 'price', 'comments', 'default_image_url')
手动生成索引
python manage.py rebuild_index
view
from drf_haystack.viewsets import HaystackViewSet
class SKUSearchViewSet(HaystackViewSet):
"""
SKU搜索
"""
index_models = [SKU]
serializer_class = SKUIndexSerializer
定义路由
router = DefaultRouter()
router.register('skus/search', views.SKUSearchViewSet, base_name='skus_search')
urlpatterns += router.urls
测试
http://127.0.0.1:8000/skus/search/?text=wifi
http://127.0.0.1:8000/skus/search/?id=1
http://127.0.0.1:8000/skus/search/?name=iphone
图片验证码
需要导包
class ImageAPiVew(APIView):
def get(self,request,iamge_code_Id):
text,image = captcha.generate_captcha()
return HTTPResponse(image,context_type= 'image/jpg')
短信验证码
需要导包调用
import sys
import logging
import uuid
# 需要安装 pip install aliyun-python-sdk-core-v3 aliyun-python-sdk-dysmsapi
from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest
# from aliyunsdkdysmsapi.request.v20170525 import QuerySendDetailsRequest
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.profile import region_provider
# from aliyunsdkcore.http import method_type as MT
# from aliyunsdkcore.http import format_type as FT
from django_redis.serializers import json
logger = logging.getLogger('django')
"""
短信业务调用接口示例,版本号:v20170525
"""
ACCESS_KEY_ID = "LTAIcbWjQFpuyqud"
ACCESS_KEY_SECRET = "aI6tNQTVNIekVosPoVLk67wt9Wjgi0"
"""
try:
reload(sys)
sys.setdefaultencoding('utf8')
except NameError:
pass
except Exception as err:
raise err
"""
# 注意:不要更改
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(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).decode()
# TODO 业务处理
return smsResponse
def send_sms_code(mobile,code):
try:
__business_id = uuid.uuid1()
ret = send_sms(__business_id, mobile, "新奇特商城", "SMS_120410887", {"code": code})
#ret = {"Message": "OK", "RequestId": "03AD9770-0E46-4047-B954-51889CD27A9D", "BizId": "462021798320537017^0",
# "Code": "OK"}
result = json.loads(ret).get('Message')
except Exception as e:
logger.error("验证码错误")
else:
if result =='OK':
logger.info("验证码正常")
else:
logger.error('验证码错误了')
# if __name__ == '__main__':
# __business_id = uuid.uuid1()
# # print(__business_id)
# params = '{"code":"123456"}'
# print(send_sms(__business_id, "17610805506", "新奇特商城", "SMS_120410887", params))
redis在django的使用数据库连接
建立数据库链接
redis_conn = django_redis.get_redis_connection('verify_codes')
pl = redis_conn.pipeline() # redis管道
pl.setex("sms_%s" %mobile,有效期,sms_code)
pl.setex("sms_flag_%s" % mobile,间隔时间,1)
pl.execute() 执行操作