新建项目
初始化数据库驱动配置
__init_.py:
import pymysql
pymysql.install_as_MySQLdb()
settings
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'rest_framework',
'django.contrib.staticfiles',
'App',
]
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'RESTEND',
'USER': 'root',
'PASSWORD':'3825447403',
'HOST':'localhost',
'PORT':3306
}
}
CACHES = {
'default': {
# 指定通过django-redis接入Redis服务
'BACKEND': 'django_redis.cache.RedisCache',
# Redis服务器的URL
'LOCATION': ['redis://127.0.0.1:6379/1', ],
# Redis中键的前缀(解决命名冲突)
'TIMEOUT': 60*60*24,
# 其他的配置选项
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
# 连接池(预置若干备用的Redis连接)参数
'CONNECTION_POOL_KWARGS': {
# 最大连接数
'max_connections': 512,
},
}
},
}
创建数据库RESTEND
级联需求
- 存在级联数据
- 用户和收获地址
- 请求节流
功能分析
数据开始
- 模型定义
用户和收获地址 一对多
- 用户表
地址表
- ForeignKey
- 级联数据如何实现序列化
- 节流
实现用户注册
新建models
from django.db import models
class UserModel(models.Model):
u_name = models.CharField(max_length=16,unique=True)
u_password = models.CharField(max_length=256)
class Address(models.Model):
a_address = models.CharField(max_length=128)
a_user = models.ForeignKey(UserModel)
新建serializers
serializers.py:
from rest_framework import serializers
from App.models import UserModel, Address
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = UserModel
fields = ['url', 'u_name','u_password']
class AddressSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Address
fields = ['url','id','a_address']
视图函数
views:
from django.shortcuts import render
import uuid
from django.contrib import admin
from django.core.cache import cache
from rest_framework import exceptions
from rest_framework.generics import CreateAPIView, RetrieveAPIView
from rest_framework.response import Response
from App.models import UserModel
from RESTend.serializers import UserSerializer
class UsersAPIView(CreateAPIView):
serializer_class = UserSerializer
query = UserModel.objects.all()
def post(self, request, *args, **kwargs):
action = request.query_params.get('action')
if action == 'login':
u_name = request.data.get('u_name')
u_password = request.data.get('u_password')
try:
user = UserModel.objects.get(u_name=u_name)
if user.u_password != u_password:
raise exceptions.AuthenticationFailed
token = uuid.uuid4().hex
cache.set(token, user.id, timeout=60 * 60)
data = {
'msg': 'login success',
'status': 200,
'token': token,
}
return Response(data)
except UserModel.DoesNotExist:
raise exceptions.NotFound
elif action == 'register':
return self.create(request, *args, **kwargs)
else:
raise exceptions.ParseError
class UserAPIView(RetrieveAPIView):
serializer_class = UserSerializer
query = UserModel.objects.all()
新建URLS
新建urls.py:
from django.urls import re_path
from App import views
urlpatterns = {
re_path(r'users/$', views.UsersAPIView.as_view()),
re_path(r'users/(?P<pk>\d+)/', views.UserAPIView.as_view(),name = 'usermodel-detail'),
}
总urls注册:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('app/', include('App.urls')),
]
测试创建用户
实现地址模块
功能分析1
- 地址添加、地址删除、地址修改
- 使用
viewsets.ModelViewSet
作为继承的视图类 viewsets.ModelViewSet
的路由也可以使用as_view来处理,但是as_view是重写过的,所以需要额外指定请求类型和对应的方法。这里先设置models.py中的address模型中的a_user为空,之后级联的时候再删除这个参数,否侧单独测试不了address模块功能。
class Address(models.Model): a_address = models.CharField(max_length=128) a_user = models.ForeignKey(UserModel, on_delete=models.CASCADE,null=True)
实现1
urls
urlpatterns = [
re_path(r'users/$', views.UsersAPIView.as_view()),
re_path(r'users/(?P<pk>\d+)/', views.UserAPIView.as_view(),name='usermodel-detail'),
re_path(r'address/$', views.AddressAPIView.as_view(
{
'post': 'create',
}
)),
re_path(r'address/(?P<pk>\d+)/$', views.AddressAPIView.as_view(
{
'get':'retireve',
}
), name='address-detail'),
]
views
class AddressAPIView(viewsets.ModelViewSet):
serializer_class = AddressSerializer
queryset = Address.objects.all()
测试
功能分析2
- 在添加地址时,同时绑定提交地址的用户数据。
只有已登录用户可以添加地址,未登录的人不可以添加地址,所以需要认证权限
实现2
新建auth认证器
新建auth.py:
class LoginAuthentication(BaseAuthentication): def authenticate(self, request): # get,post方法需要认证 if request.method == "GET": try: token = request.query_params.get('token') user_id = cache.get(token) user = UserModel.objects.get(pk = user_id) return user,token except Exception: return
将认证器添加入视图函数中
views.py:class AddressAPIView(viewsets.ModelViewSet): serializer_class = AddressSerializer queryset = Address.objects.all() authentication_classes = (LoginAuthentication, )
新建permissions权限器
from rest_framework.permissions import BasePermission from App.models import UserModel class RequireLoginPermission(BasePermission): def has_permission(self, request, view): # 是否有权限访问,就是判断是否登录 return isinstance(request.user, UserModel) #如果是UserModel的实例就有权限
views
class AddressAPIView(viewsets.ModelViewSet):
serializer_class = AddressSerializer
queryset = Address.objects.all()
authentication_classes = (LoginAuthentication, )
permission_classes = (RequireLoginPermission,)
测试添加地址
只有携带token才允许添加路径,但是此时保存的地址还没有存入对应的用户
功能分析3
实现3
在创建的存储过程中,修改存储内容,把对应用户添加进去,然后存储到数据库。
也就是重写CreateModelMinxin中的create方法
class AddressAPIView(viewsets.ModelViewSet):
serializer_class = AddressSerializer
queryset = Address.objects.all()
authentication_classes = (LoginAuthentication, )
permission_classes = (RequireLoginPermission,)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
# 拿到用户,地址并修改
user = request.user
a_id = serializer.data.get('id')
address = Address.objects.get(pk=a_id)
address.a_user = user
address.save()
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
测试
携带token提交post请求创建地址
数据库可以观察到绑定了对应的用户id。
实现4
- 让用户只能查询到自己的地址 ,而不是使用get请求address时查询到所有人地址
首先在urls中允许对address使用get请求。
re_path(r'address/$', views.AddressAPIView.as_view(
{
'post': 'create',
'get':'list',
}
)),
要实现该功能就是重写get请求时进行筛选的步骤,也就是重写ModelViewSet
继承的ListModelMixin
中的list
方法。
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.queryset.filter(a_user=request.user)) # 这里根据用户筛选地址
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
测试
可以看到用户只能查询到自己绑定的地址。
实现5
- 当前如果知道用户id可以直接使用get直接访问获取到所有用户信息,所以get请求也需要进行登录认证
- 当获取到自己用户的token进行get地址时,get其他用户地址时也应该被禁止,所以还需要一个新的认证规则,在get时判断token对应的用户和访问对应的用户是否匹配。
views:
class UserAPIView(RetrieveAPIView):
serializer_class = UserSerializer
query = UserModel.objects.all()
authentication_classes = (LoginAuthentication,)
permission_classes = (RequireLoginPermission,)
# 判断tokenid和用户id是一致的,重写RetrieveModelMixin中的retrieve
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
# 如果tokenid和对应用户id不匹配
if instance.id !=request.user.id:
raise exceptions.AuthenticationFailed
serializer = self.get_serializer(instance)
return Response(serializer.data)
测试
用1号用户的token 来 get 4号用户的信息
不匹配,所以访问失败。
接着访问1号用户:
现在不从instance查询id,而是直接从kwags中获取id
可以修改views.py中的retiteve:
# 判断tokenid和用户id是一致的,重写RetrieveModelMixin中的retrieve
def retrieve(self, request, *args, **kwargs):
if kwargs.get('pk') != str(request.user.id):
raise exceptions.AuthenticationFailed
instance = self.get_object()
# # 如果tokenid和对应用户id不匹配
# if instance.id != request.user.id:
# raise exceptions.AuthenticationFailed
serializer = self.get_serializer(instance)
return Response(serializer.data)
级联查询
功能分析
在get用户信息时,将对应的地址也一并返回。
使用序列化关系中的Nested relationships
(嵌套关系):https://q1mi.github.io/Django-REST-framework-documentation/api-guide/relations/#nested-relationships
在序列化器中添加address_set:
serializers.py:
from rest_framework import serializers
from App.models import UserModel, Address
class AddressSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = Address
fields = ['url', 'id', 'a_address']
class UserSerializer(serializers.HyperlinkedModelSerializer):
# 调用AddressSerializer,实现嵌套序列化
# 使用AddressSerializer的隐形属性address_set
address_set = AddressSerializer(many=True,read_only=True)
class Meta:
model = UserModel
fields = ['url', 'u_name', 'u_password', 'address_set']
14行添加address_set
models.py:
from django.db import models
class UserModel(models.Model):
u_name = models.CharField(max_length=16,unique=True)
u_password = models.CharField(max_length=256)
class Address(models.Model):
a_address = models.CharField(max_length=128)
a_user = models.ForeignKey(UserModel, on_delete=models.CASCADE,null=True,blank=True)
因为models中Address的a_user使用了ForeignKey所以UserModel会生成隐形属性address_set
如果我们将address_set修改成其他变量名,需要将新的变量名在models.py中注册。
class Address(models.Model):
a_address = models.CharField(max_length=128)
a_user = models.ForeignKey(UserModel, related_name='address_list',on_delete=models.CASCADE,null=True,blank=True)
a_user中添加related_name来命令。
serializers.py:
class UserSerializer(serializers.HyperlinkedModelSerializer):
# 调用AddressSerializer,实现嵌套序列化
# 使用AddressSerializer的隐形属性address_set
address_list = AddressSerializer(many=True,read_only=True)
class Meta:
model = UserModel
fields = ['url', 'u_name', 'u_password', 'address_list']
这样就可以使用address_list变量名来实现嵌套查询
测试:
这样就实现了级联查询。
此处评论已关闭