Django的MTV模式

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:

  1. M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。

  2. T 代表模板 (Template):负责如何把页面展示给用户(html)。

  3. V 代表视图(View): 负责业务逻辑,并在适当时候调用Model和Template。

除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:

image.png

注:如下的设计说明都是针对第二版的,也就是pyhton+django+vue实现的。

思想:

        先在urls.py中定义好API,然后对应到对应的视图,按照需求定义方法(get,post,delete,put等),然后再去数据库中取数(经过序列化成json字符串)并返回,定义好之后可以使用postman测试下,看看接口有什么问题,然后再前端中安排这些API就好了。

首先看开发完成后的主页面:

image.png

上面的*宝和*神是我们的应用名称,总共有这两个应用,然后每个应用有多个服务,如如下的第二行。因此我在设计数据库的时候将这两个表建立表关系,以后再有新的应用只在第一张表中添加:

models.py 文件部分内容:
class Appname(models.Model):
    """应用分类表"""
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32,verbose_name="应用的名称")
    title = models.CharField(max_length=32,unique=True,verbose_name="应用的分类")
    def __str__(self):
        return self.title



class Service(models.Model):
    """服务表"""
    appname = models.ForeignKey(to="Appname",verbose_name="应用的分类",on_delete=None)
    title = models.CharField(max_length=32,verbose_name="服务的名称")
    title_number = models.IntegerField(verbose_name="服务数量", default=1)
    def __str__(self):
        return self.title

主页面的表的相关东西设计完成之后,下来写获取应用名和获取服务名的api:
总的url控制器,获取相应的应用名和服务名在Automatic中:

image.png

Autimatic中相应的内容:

image.png

上面的url控制器部分定义完之后,下来时view视图部分:

from rest_framework.views import APIView
from rest_framework.response import Response

from . import models
from .serializers import APPNameSerializer,ServiceSerializer

class AppView(APIView):
    def get(self,request):
        queryset = models.Appname.objects.all()
        ser_obj = APPNameSerializer(queryset, many=True)
        return Response(ser_obj.data)

class ServiceclassifyView(APIView):
    def get(self,request,pk):
        queryset = models.Service.objects.filter(appname_id=pk).all()
        print(queryset)

        ser_obj = ServiceSerializer(queryset,many=True)

        return Response(ser_obj.data)

上面的视图中我们确实从数据库中返回了。但是我们要返回给前端,为了规范,我们使用json字符串进行返回,所以要先对我们从数据库取的数据进行序列化成json格式,你可以使用JsonResponse。在DRF框架中为我们提供了更好的序列化方式:ModelSerializer。

注:此处的截图为部分截图。

image.png

model:表示你要序列化的表。

fields是要序列化的字段,当然也可以建立表关系的字段,”__all__”表示所以字段都要进行序列化。对于复杂的字段需要重新构建,使用get_字段名这个方法进行重构。

经过这个序列化处理之后将数据返回。

对于登录接口:

整体流程:前端输入用户名和密码之后,点击登录,然后先使用md5对密码加密,然后给后端发送post请求并携带用户名和密码,在后端取出相应的用户名和密码,然后进行对比,如果成功,会使用uuid生效一个随机数用户token,以token为健,用户名为值保存到redis。

class LoginView(APIView):
    def post(self,request):
        res = BaseResponse()
        username = request.data.get('username','')
        pwd = request.data.get('pwd','')
        user_obj = Account.objects.filter(username=username,pwd=pwd).first()
        if not user_obj:
            res.code=1030
            res.error='用户名或密码错误'
            return Response(res.dict)

        #用户登录成功生成token,并写入redis中
        conn = redis.Redis(connection_pool=POOL)
        try:
            token = uuid.uuid4()
            conn.set(str(token),user_obj.username)
            res.data = token
            res.username = user_obj.username
        except Exception as e:
            res.code = 1031
            res.error = '创建令牌失败'

        return Response(res.dict)

BaseResponse类是定义一个格式。

class BaseResponse(object):
    def __init__(self):
        self.code = 1000
        self.data = None
        self.error = None
    @property
    def dict(self):
        return self.__dict__    #将所有初始化属性组成字典返回

注:对于用户的注册功能并没有做,对于自动化发布平台来讲,根据目前的需求,所以提前在数据库中写入一个用户,注册其实也可以做得,流程是前端的用户和密码信息返回到后端,然后根据ModelSerializer中的序列化之后并使用create方法写入到数据库中。

对于登录认证功能,很多页面都有登录认证,就是访问当前页面或接口之前先进行验证是否登录,此处使用DRF中的登录验证模块:BasrAuthentication。

整体流程:

前端访问后端某些api时,需要进行登录验证时,先在前端判断是否登录,登录之后,将用户名以及从后端返回的token保存到SessionStrorage中,对于需要登录验证的后端API,我们在访问时将token加入到请求头部,当请求到后端时,先从头部信息中拿到相关token,然后再redis中进行判断token是否正常,然后成功返回user,token,对于这一系列后端验证,使用了DRF中的认证模块。

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed

from .redis_pool import POOL
import redis

from Automatic.models import Account

CONN = redis.Redis(connection_pool=POOL)

"""
从请求头部取出token,然后和redis中的token进行对比,成功返回   user,token
"""


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # 从请求头中获取前端带过来的token
        token = request.META.get("HTTP_AUTHENTICATION", "")
        if not token:
            raise AuthenticationFailed("没有携带token")
        # 去redis比对
        user_id = CONN.get(str(token))
        if user_id == None:
            raise AuthenticationFailed("token过期")
        user_obj = Account.objects.filter(id=user_id).first()
        return user_obj, token

定义好之后,然后再需要进行登录验证的接口的视图前加上这个类就好了。

image.png

详细代码联系我,qq:1159291043。