1.redis
2.购物车的构建
api结构:

models.py(创建完后自行添加数据)
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
# Create your models here.
class Course(models.Model):
"""专题课/学位课模块表"""
name = models.CharField(max_length=128, unique=True)
course_img = models.CharField(max_length=255)
course_type_choices = ((0, '付费'), (1, 'VIP专享'), (2, '学位课程'))
course_type = models.SmallIntegerField(choices=course_type_choices)
brief = models.TextField(verbose_name="课程概述", max_length=2048)
# 用于GenericForeignKey反向查询,不会生成表字段,切勿删除
price_policy = GenericRelation("PricePolicy")
class PricePolicy(models.Model):
"""价格与有课程效期表"""
content_type = models.ForeignKey(ContentType) # 关联course
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
# course = models.ForeignKey("Course")
valid_period_choices = ((1, '1天'), (3, '3天'),
(7, '1周'), (14, '2周'),
(30, '1个月'),
(60, '2个月'),
(90, '3个月'),
(180, '6个月'), (210, '12个月'),
(540, '18个月'), (720, '24个月'),
)
valid_period = models.SmallIntegerField(choices=valid_period_choices)
price = models.FloatField()
class Meta:
unique_together = ("content_type", 'object_id', "valid_period")
verbose_name_plural = "15. 价格策略"
def __str__(self):
return "%s(%s)%s" % (self.content_object, self.get_valid_period_display(), self.price)
class Account(models.Model):
username = models.CharField("用户名", max_length=64, unique=True)
email = models.EmailField(
verbose_name='邮箱',
max_length=255,
unique=True,
blank=True,
null=True
)
password = models.CharField('密码', max_length=128)
class Meta:
verbose_name_plural = "22. 账户信息"
class UserToken(models.Model):
user = models.OneToOneField(to='Account')
token = models.CharField(max_length=36)
class Meta:
verbose_name_plural = "34. token表"
admin.py(用于在在admin中添加数据)
from django.contrib import admin from api import models # Register your models here. admin.site.register(models.UserToken) admin.site.register(models.Course) admin.site.register(models.Account) admin.site.register(models.PricePolicy)
urls.py
from django.conf.urls import url
from api.views import shoppingcar,auth
urlpatterns = [
url(r'auth/$', auth.AuthView.as_view({'post': 'login'})),
url(r'shoppingcar/$', shoppingcar.ShoppingCarView.as_view({'post': 'create', 'get': 'list',
'delete': 'destory', 'put': 'update'})),
]
app01下的urls.py
from django.conf.urls import url,include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/(?P<version>w+)/', include('api.urls')),
]
utils/response.py
class BaseResponse(object):
def __init__(self):
self.code = 1000
self.data = None
self.error = None
@property
def dict(self):
return self.__dict__
utils/auth.py(重写用户认证组件)
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from api import models
class AccountAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
源码:
Authenticate the request and return a two-tuple of (user, token).
"""
# 从前端获取用户携带的token
token = request.query_params.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise AuthenticationFailed({'code':1000,'error':'认证失败'})
# 认证通过
return (token_obj.user, token_obj)
views/auth.py(登录认证)
import uuid
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from api import models
from api.utils.response import BaseResponse
class AuthView(ViewSetMixin,APIView):
def login(self,request,*args,**kwargs):
"""api_usertoken
用户登录认证
"""
response = BaseResponse()
try:
user = request.data.get('username')
pwd = request.data.get('password')
obj = models.Account.objects.filter(username=user,password=pwd).first()
if not obj:
response.code = 1000
response.error = '用户名或密码错误'
else:
uid = str(uuid.uuid4())
# UserToken和Account是一对一的关系,登陆成功生成uid并加入表格
models.UserToken.objects.update_or_create(user=obj,defaults={'token':uid})
response.code = 99999
response.data = uid
except Exception as e:
response.code = 10005
response.error = '操作异常'
return Response(response.dict)
views/shoppingcar.py
import json
import redis
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.viewsets import ViewSetMixin
from rest_framework.response import Response
from api.utils.auth import AccountAuthentication
from api.utils import response
from api import models
# 注意这里的ip地址
CONN = redis.Redis(host="118.24.140.138",port=6379)
class ShoppingCarView(ViewSetMixin, APIView):
# 加入用户认证
authentication_classes = [AccountAuthentication,]
def list(self,request,*args,**kwargs):
"""
从redis中查看购物车信息
"""
ret = response.BaseResponse()
try:
shopping_car_list = []
# 这里的request.user来自认证组件的的返回值
print(request.user)
pattern = settings.LUFFY_SHOPPING_CAR % (request.user.id, '*',)
user_key_list = CONN.keys(pattern)
for key in user_key_list:
temp = {
"id":CONN.hget(key,"id").decode("utf-8"),
"name":CONN.hget(key,"name").decode("utf-8"),
"img":CONN.hget(key,"img").decode("utf-8"),
}
shopping_car_list.append(temp)
ret.data = shopping_car_list
except Exception as e:
ret.code = 1000
ret.error = "获取数据失败"
return Response(ret.dict)
def create(self, request, *args, **kwargs):
"""
加入购物车
"""
# 需要先接收用户选中的课程id以及价格策略id
course_id = request.data.get('courseid')
policy_id = request.data.get('policyid')
course = models.Course.objects.first(id=course_id)
ret = response.BaseResponse()
# 判断数据是否合法,防止非正常点击,比如利用其他软件0元购买某些商品
try:
# 判断课程是否存在
if not course:
ret.code = 1000
ret.error = "课程不存在"
return Response(ret.dict)
# 判断价格策略的合法性,也是避免蓄意修改
# 获取价格策略的所有信息
price_policy_queryset = course.price_policy.all()
price_policy_dict = {}
for item in price_policy_queryset:
temp = {
'id':item.id,
'price':item.price,
'valid_period': item.valid_period,
'valid_period_display': item.get_valid_period_display()
}
price_policy_dict[item.id] = temp
# 校验id是是否在字典中
if policy_id not in price_policy_dict:
ret.code = 1000
ret.error = "不要乱动"
return Response(ret.dict)
# 设置购物车物品数
pattern = settings.LUFFY_SHOPPING_CAR % (request.user.id, '*',)
keys = CONN.keys(pattern)
if keys and len(keys) >=100:
ret.code = 1000
ret.error = "物品栏已满"
return Response(ret.dict)
except Exception as e:
# 进行添加操作
key = settings.LUFFY_SHOPPING_CAR % (request.user.id, course_id,)
CONN.hset(key,'id',course_id)
CONN.hset(key, 'name', course.name)
CONN.hset(key, 'img', course.course_img)
# 设置保存时长,24h
CONN.expire(key,60*60*24)
ret.code= 500
ret.data = "保存成功"
return Response(ret.dict)
def update(self,request,*args,**kwargs):
"""
修改操作,这里我们只能修改我们的价格策略
"""
ret = response.BaseResponse()
try:
# 获取用户通过操作相关字段传来的id
course_id = request.data.get('courseid')
policy_id = str(request.data.get('policyid'))
key = settings.LUFFY_SHOPPING_CAR % (request.user.id, course_id,)
if not CONN.exists(key):
ret.code = 10000
ret.error = '课程不存在'
return Response(ret.dict)
# 取我们存的price_policy_dict数据
price_policy_dict = json.loads(CONN.hget(key, 'price_policy_dict').decode('utf-8'))
# 判断价格策略是否存在
if policy_id not in price_policy_dict:
ret.code = 1000
ret.error = '价格策略不存在'
return Response(ret.dict)
# 修改价格策略
CONN.hset(key, 'default_price_id', policy_id)
set.data = '修改成功'
except Exception as e:
ret.code = 1000
ret.error = "修改失败"
return Response(ret.dict)
def destory(self,request,*args,**kwargs):
"""
删除选中的某个课程
"""
ret = response.BaseResponse()
try:
course_id = request.data.get('courseid')
# 获取它的key值
key = settings.LUFFY_SHOPPING_CAR % (request.user.id, course_id,)
CONN.delete(key)
ret.data = "删除成功"
except Exception as e:
ret.code = 1000
ret.error = "删除失败"
return Response(ret.dict)
setting相关配制:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'api.apps.ApiConfig',
'rest_framework'
]
REST_FRAMEWORK = {
# 版本相关信息
'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
'VERSION_PARAM':'version',
'DEFAULT_VERSION':'v1',
'ALLOWED_VERSIONS':['v1','v2'],
# 分页相关信息,数字代表一页几条数据
# 'PAGE_SIZE':2
}
LUFFY_SHOPPING_CAR = "shopping_car_%s-%s"
测试(成功登录,获取它的token)

发送一个错误请求,这里直接被认证组件拦截了,并没有到达视图

在通过携带token发送一个get请求:

3.全局配置
如果100个类,有98个视图要认证。可以加到全局rest_framework里面
'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.LuffyAuthentication',]
对于某些不用认证的类,可以直接定义认证类为空列表即可
authentication_classes = []