整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          極大簡(jiǎn)化Django開(kāi)發(fā),讓程序員沒(méi)代碼敲的DRF框

          極大簡(jiǎn)化Django開(kāi)發(fā),讓程序員沒(méi)代碼敲的DRF框架

          置基礎(chǔ)知識(shí)

          web開(kāi)發(fā)的兩種模式

        1. 前后端不分離
        2. 前后端分離
        3. 1. 前后端不分離

          完整的html頁(yè)面是在后端生成的,后端給前端返回完整的頁(yè)面,前端只是進(jìn)行展示,前端與后端的耦合度很高。

          缺點(diǎn):只適用于純網(wǎng)頁(yè)的應(yīng)用。

          優(yōu)點(diǎn):有利于網(wǎng)站的SEO優(yōu)化

          2. 前后端分離

          完整的html頁(yè)面是在前端生成的,后端只給前端返回所需的數(shù)據(jù),前端將數(shù)據(jù)填充在頁(yè)面上,前端與后端的耦合度很低。

          優(yōu)點(diǎn):可以對(duì)接不同類型的客戶端。

          缺點(diǎn):不利于SEO優(yōu)化

          3.API: 在前后端分離開(kāi)發(fā)模式中,我們通常將后端開(kāi)發(fā)的每個(gè)視圖都稱為一個(gè)接口或者API。

          4.RESTful設(shè)計(jì)風(fēng)格

          統(tǒng)一的接口設(shè)計(jì)方式就是普遍采用的RESTful API設(shè)計(jì)風(fēng)格

          Restful風(fēng)格設(shè)計(jì)-關(guān)鍵點(diǎn)

          • URL地址盡量使用名詞復(fù)數(shù),不要使用動(dòng)詞。
          • 訪問(wèn)同一個(gè)URL地址,采用不同的請(qǐng)求方式,代表要執(zhí)行不同的操作。(GET)獲取 POST(新增) PUT(修改)DELETE(刪除) 不常用:PATCH(修改) HEAD(只返回請(qǐng)求頭沒(méi)有請(qǐng)求體) OPTIONS(獲取信息)
          • 過(guò)濾參數(shù)可以放在查詢字符串中。常見(jiàn)的參數(shù):
          ?limit=10:指定返回記錄的數(shù)量
          ?offset=10:指定返回記錄的開(kāi)始位置。
          ?page=2&pagesize=100:指定第幾頁(yè),以及每頁(yè)的記錄數(shù)。
          ?sortby=name&order=asc:指定返回結(jié)果按照哪個(gè)屬性排序,以及排序順序。
          • 針對(duì)不同操作,服務(wù)器向用戶返回不同的響應(yīng)數(shù)據(jù)。一般遵循以下規(guī)范:
          1. 獲取一組數(shù)據(jù),返回一組數(shù)據(jù)
          2. 獲取指定數(shù)據(jù),返回指定數(shù)據(jù)
          3. 新增數(shù)據(jù),返回新增的數(shù)據(jù)
          4. 修改數(shù)據(jù),返回修改的數(shù)據(jù)
          5. 刪除數(shù)據(jù),返回空
          • 服務(wù)器返回的響應(yīng)數(shù)據(jù)格式,應(yīng)該盡量使用JSON。
          • 響應(yīng)狀態(tài)碼,服務(wù)器向客戶端返回的狀態(tài)碼和提示信息,常見(jiàn)的狀態(tài)碼如下:
          200 OK - [GET/PUT]:服務(wù)器成功返回用戶請(qǐng)求的數(shù)據(jù)
          201 CREATED - [POST]:用戶新建數(shù)據(jù)成功。
          204 NO CONTENT - [DELETE]:用戶刪除數(shù)據(jù)成功。
          400 INVALID REQUEST - [POST/PUT]:用戶發(fā)出的請(qǐng)求有錯(cuò)誤,服務(wù)器沒(méi)有進(jìn)行新建或修改數(shù)據(jù)的操作
          404 NOT FOUND - [*]:用戶發(fā)出的請(qǐng)求針對(duì)的是不存在的記錄,服務(wù)器沒(méi)有進(jìn)行操作,該操作是冪等的。。
          500 INTERNAL SERVER ERROR - [*]:服務(wù)器發(fā)生錯(cuò)誤,用戶將無(wú)法判斷發(fā)出的請(qǐng)求是否成功。

          了解:

          1. 域名:使用專有域名
          2. 版本:將版本信息放在url地址
          3. 錯(cuò)誤:將錯(cuò)誤信息返回
          4. 在訪問(wèn)api接口時(shí),將和接口相關(guān)的其他API接口的地址也在響應(yīng)數(shù)據(jù)中返回

          使用Django開(kāi)發(fā)REST API

          需求:
              設(shè)計(jì)一套符合RestAPI風(fēng)格的接口,提供以下5個(gè)接口:
                  1. 獲取所有直播間數(shù)據(jù):GET /lives/
                  2. 新增一本直播間數(shù)據(jù):POST /lives/
                  3. 獲取指定的直播間數(shù)據(jù)(根據(jù)id):GET /lives/(?P<pk>\d+)/
                  4. 修改指定的直播間數(shù)據(jù)(根據(jù)id):PUT /lives/(?P<pk>\d+)/
                  5. 刪除指定的直播間數(shù)據(jù)(根據(jù)id):DELETE /lives/(?P<pk>\d+)/

          模型類定義

          # models.py
          class LiveInfo(models.Model):
              live_id=models.IntegerField(verbose_name='直播間號(hào)')
              live_streamer=models.CharField(max_length=20, verbose_name='主播名字')
              live_title=models.CharField(max_length=30, verbose_name='直播間標(biāo)題')
              live_pop=models.IntegerField(default=0, verbose_name='人氣')
              live_content=models.CharField(default='未設(shè)定', max_length=20, verbose_name='直播類型')
              is_delete=models.BooleanField(default=False, verbose_name='刪除標(biāo)記')
          
              class Meta:
                  db_table='tb_lives'
                  verbose_name='直播間'
                  verbose_name_plural=verbose_name
          
              def __str__(self):
                  return self.live_streamer

          views.py文件中定義視圖實(shí)現(xiàn)API接口:

          class LiveListView(View):
              def get(self, request):
                  """獲取所有直播間"""
                  lives=LiveInfo.objects.all()
                  lives_list=[]
                  for live in lives:
                      lives_list.append({
                          'live_id': live.live_id,
                          'live_streamer': live.live_streamer,
                          'live_title': live.live_title,
                          'live_pop': live.live_pop,
                          'live_content': live.live_content
                      })
                  return JsonResponse(lives_list, safe=False)
          
              def post(self, request):
                  """新增直播間"""
                  pass
          
          
          class LiveDetailView(View):
              def get(self, request, pk):
                  """查詢一間直播間信息"""
                  pass
          
              def put(self, request, pk):
                  """修改直播間信息"""
                  pass
          
              def delete(self, request, pk):
                  """刪除直播間"""
                  pass

          RestAPI開(kāi)發(fā)核心工作

          分析上節(jié)直播間管理的5個(gè)API接口,可以發(fā)現(xiàn),在開(kāi)發(fā)REST API接口時(shí),視圖中做的最主要有三件事:

          • 將請(qǐng)求的數(shù)據(jù)(如JSON格式)轉(zhuǎn)換為模型類對(duì)象
          • 操作數(shù)據(jù)庫(kù)
          • 將模型類對(duì)象轉(zhuǎn)換為響應(yīng)的數(shù)據(jù)(如JSON格式)

          在以上操作中,涉及到兩個(gè)概念:序列化和反序列化。

          序列化Serialization

          將程序中的一個(gè)數(shù)據(jù)結(jié)構(gòu)類型轉(zhuǎn)換為其他格式(字典、JSON、XML等),例如將Django中的模型類對(duì)象轉(zhuǎn)換為字典或JSON字符串,這個(gè)轉(zhuǎn)換過(guò)程我們稱為序列化。

          反序列化

          將其他格式(字典、JSON、XML等)轉(zhuǎn)換為程序中的數(shù)據(jù),例如將JSON字符串或字典轉(zhuǎn)換保存為Django中的模型類對(duì)象,這個(gè)過(guò)程我們稱為反序列化。

          在開(kāi)發(fā)REST API接口時(shí),我們?cè)?strong>視圖中在做的最核心的事是:

          • 將數(shù)據(jù)庫(kù)數(shù)據(jù)序列化為前端所需要的格式,并返回。
          • 將前端發(fā)送的數(shù)據(jù)反序列化保存到模型類對(duì)象,并保存到數(shù)據(jù)庫(kù)中。

          Django REST framework 簡(jiǎn)介

          Django REST framework 框架是一個(gè)用于構(gòu)建Web API 的強(qiáng)大而又靈活的工具。通常簡(jiǎn)稱為DRF框架 或 REST framework。

          作用:幫助我們大大提高REST API的開(kāi)發(fā)速度

          核心功能:

          • 序列化器:序列化和反序列化
          • 類視圖,MiXin擴(kuò)展類:簡(jiǎn)化視圖代碼的編寫(xiě)

          環(huán)境安裝與使用:

          pip install djangorestframework
          # 注:DRF框架依賴于Django,需要先安裝Django環(huán)境,再安裝djangorestframework。

          在Django項(xiàng)目中使用DRF框架進(jìn)行開(kāi)發(fā)時(shí),需要將rest_framework在INSTALLED_APPS中進(jìn)行注冊(cè):

          INSTALLED_APPS=[
              ...
              'rest_framework',
          ]

          序列化器

          序列化器類定義

          from rest_framework import serializers
          
          
          class LiveInfoSerializer(serializers.Serializer):
              id=serializers.IntegerField(label='ID', read_only=True)
              live_id=serializers.IntegerField(label='直播間號(hào)')
              live_streamer=serializers.CharField(max_length=20, label='主播名字', required=False)
              live_title=serializers.CharField(max_length=30, label='直播間標(biāo)題')
              live_pop=serializers.IntegerField(default=0, label='人氣', required=False)
              live_content=serializers.CharField(default='未設(shè)定', max_length=20, label='直播類型', required=False)
              is_delete=serializers.BooleanField(default=False, label='刪除標(biāo)記', required=False)

          選項(xiàng)參數(shù):

          • write_only:為True,字段只在反序列化時(shí)使用
          • read_only:為True,字段只在序列化時(shí)使用
          • required:為True,如果字段在反序列化時(shí)使用,該字段必傳傳入
          • default:設(shè)置序列化和反序列化操作時(shí)的默認(rèn)值
          • max_length和min_length:設(shè)置字符串的最大長(zhǎng)度和最小長(zhǎng)度
          • max_value和min_value:設(shè)置數(shù)字的最大值和最小值

          序列化操作

          • 序列化單個(gè)對(duì)象:序列化單個(gè)對(duì)象obj時(shí),在創(chuàng)建序列化器對(duì)象時(shí),將obj傳遞給instance即可
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          class LiveDetailView(View):
              def get(self, request, pk):
                  """查詢一間直播間信息"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      return HttpResponse(status=404)
                  # 創(chuàng)建序列化器對(duì)象
                  serializer=LiveInfoSerializer(live)
                  # 返回序列化之后的數(shù)據(jù)
                  return JsonResponse(serializer.data)
          
          • 序列化多個(gè)對(duì)象:如果要被序列化的是包含多個(gè)對(duì)象的查詢集QuerySet或list,在創(chuàng)建序列化器對(duì)象時(shí),需要添加many=True參數(shù)
          class LiveListView(View):
              def get(self, request):
                  """獲取所有直播間"""
                  lives=LiveInfo.objects.all()
                  # 創(chuàng)建序列化器對(duì)象
                  serialize# 獲取序列化之后的數(shù)據(jù)r=LiveInfoSerializer(lives, many=True)
                  
                  return JsonResponse(serializer.data, safe=False)
          • 關(guān)聯(lián)對(duì)象嵌套序列化

          如果在序列化對(duì)象數(shù)據(jù)時(shí),需要將其關(guān)聯(lián)的對(duì)象一并序列化,則定義序列化器類的字段時(shí),需要在定義對(duì)應(yīng)的關(guān)聯(lián)對(duì)象嵌套序列化字段,比如和直播分類關(guān)聯(lián)的直播間。對(duì)于嵌套關(guān)聯(lián)字段,可以采用以下3種方式進(jìn)行定義:

          1.PrimaryKeyRelatedField :將關(guān)聯(lián)對(duì)象序列化為關(guān)聯(lián)對(duì)象的主鍵。

          # 在LiveInfoSerializer中添加此字段
          Ltype=serializers.PrimaryKeyRelatedField(label='直播類型', read_only=True)
          或
          Ltype=serializers.PrimaryKeyRelatedField(label='直播類型', queryset=LiveInfo.objects.all())

          2.使用關(guān)聯(lián)對(duì)象的序列化器

          Ltype=LiveInfoSerializer()

          3.StringRelatedField:將關(guān)聯(lián)對(duì)象序列化為關(guān)聯(lián)對(duì)象模型類__str__方法的返回值。

          Ltype=serializers.StringRelatedField(label='直播類型')

          如果和一個(gè)對(duì)象關(guān)聯(lián)的對(duì)象有多個(gè),在序列化器類中定義嵌套序列化字段時(shí),需要多添加一個(gè)many=True參數(shù)。

          反序列化操作

          # 1. 創(chuàng)建序列化器對(duì)象
          serializer=序列化器類(data=<待校驗(yàn)字典數(shù)據(jù)>)
          
          # 2. 數(shù)據(jù)校驗(yàn):成功返回True,失敗返回False
          serializer.is_valid()
          
          # 3. 獲取校驗(yàn)成功之后的數(shù)據(jù)
          serializer.validated_data
          
          # 4. 如果校驗(yàn)失敗,獲取失敗的錯(cuò)誤提示信息
          serializer.errors

          調(diào)用is_valid時(shí),會(huì)根據(jù)對(duì)應(yīng)序列化類字段是否需要傳遞、字段類型以及一些選項(xiàng)參數(shù)對(duì)data中的數(shù)據(jù)進(jìn)行校驗(yàn)。

          在調(diào)用is_valid進(jìn)行數(shù)據(jù)校驗(yàn)時(shí),除了一些基本的默認(rèn)驗(yàn)證行為,可能還需要補(bǔ)充一些驗(yàn)證行為,可以使用以下三種方法:

        4. 針對(duì)指定字段添加validators選項(xiàng)參數(shù)添加補(bǔ)充驗(yàn)證函數(shù)
        5. 在序列化器類中定義特定方法validate_<field_name>針對(duì)特定字段進(jìn)行補(bǔ)充驗(yàn)證
        6. 在序列化器類中定義方法validate進(jìn)行補(bǔ)充驗(yàn)證
        7. 數(shù)據(jù)校驗(yàn)通過(guò)之后,可以調(diào)用序列化對(duì)象的save方法進(jìn)行數(shù)據(jù)保存,save方法內(nèi)部會(huì)調(diào)用對(duì)應(yīng)序列化器類中的create或update方法,可以在create中實(shí)現(xiàn)數(shù)據(jù)新增,在update中實(shí)現(xiàn)數(shù)據(jù)更新。

          ModelSerializer使用

          如果序列化器類對(duì)應(yīng)的是Django的某個(gè)模型類,則定義序列化器類時(shí),可以直接繼承于ModelSerializer。

          ModelSerializer是Serializer類的子類,相對(duì)于Serializer,提供了以下功能:

          • 基于模型類字段自動(dòng)生成序列化器類的字段
          • 包含默認(rèn)的create()和update()方法的實(shí)現(xiàn)
          class LiveInfoSerializer(serializers.ModelSerializer):
              class Meta:
                  model=LiveInfo
          				# 使用fields來(lái)指明依據(jù)模型類的哪些字段生成序列化器類的字段,__all__表明包含所有字段,也可以指明具體哪些字段
          				# fields='__all__'
                  # fields=('id', 'title', ...)
                  # 使用exclude可以指明排除哪些字段
                  exclude=['is_delete']
                  extra_kwargs={
                      # 'live_title': {'validators': [live_name]},
                      'live_streamer': {'required': False},
                      'live_pop': {'min_value': 0, 'required': False},
                  }

          案例-序列化器改寫(xiě)后的視圖代碼:

          import json
          
          from django.http import JsonResponse, HttpResponse
          from django.views import View
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveListView(View):
              def get(self, request):
                  """獲取所有直播間"""
                  lives=LiveInfo.objects.all()
                  serializer=LiveInfoSerializer(lives, many=True)
                  return JsonResponse(serializer.data, safe=False)
          
              def post(self, request):
                  """新增直播間"""
                  json_dict=json.dumps(request.body.decode())
                  serializer=LiveInfoSerializer(data=json_dict)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  # 返回新增直播間
                  return JsonResponse(serializer.data, status=201)
          
          
          class LiveDetailView(View):
              def get(self, request, pk):
                  """查詢一間直播間信息"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      return HttpResponse(status=404)
                  serializer=LiveInfoSerializer(live)
                  return JsonResponse(serializer.data)
          
              def put(self, request, pk):
                  """修改直播間信息"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      return HttpResponse(status=404)
                  json_dict=json.dumps(request.body.decode())
                  serializer=LiveInfoSerializer(live, data=json_dict)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  return JsonResponse(serializer.data)
          
              def delete(self, request, pk):
                  """刪除直播間"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      return HttpResponse(status=404)
                  # 參數(shù)校驗(yàn)
                  live.delete()
          
                  return HttpResponse(status=204)
          

          視圖部分

          APIView

          APIView是REST framework提供的所有視圖的基類,繼承自Django的View類。

          APIView和View的區(qū)別:

          • 請(qǐng)求對(duì)象:Request類的對(duì)象request.data:解析之后的請(qǐng)求體數(shù)據(jù) ,request.query_params:解析之后的查詢字符串?dāng)?shù)據(jù)
          • 響應(yīng)對(duì)象:Response類對(duì)象統(tǒng)一返回Response對(duì)象,原始響應(yīng)數(shù)據(jù)根據(jù)客戶端請(qǐng)求頭Accept轉(zhuǎn)換為對(duì)應(yīng)的格式進(jìn)行返回
          • 異常處理機(jī)制默認(rèn)異常處理機(jī)制,會(huì)將視圖中出現(xiàn)的異常處理成合適的響應(yīng)返回給客戶端
          • 其他:認(rèn)證&權(quán)限&限流

          案例-使用APIView改寫(xiě)RestAPI:

          GenericAPIView

          繼承自APIView,在APIView功能基礎(chǔ)上,主要增加了操作序列化器和數(shù)據(jù)庫(kù)查詢的屬性和方法

          GenericAPIView和APIView的區(qū)別:

          • 繼承自APIView
          • 封裝了操作序列化器的屬性和方法屬性:serializer_class方法:get_serializer_class和get_serializer
          • 封裝了數(shù)據(jù)庫(kù)操作的屬性和方法屬性:queryset方法:get_queryset和get_object
          • 其他:過(guò)濾和分頁(yè)

          案例-使用GenericAPIView改寫(xiě)RestAPI:

          from rest_framework import status
          from rest_framework.generics import GenericAPIView
          from rest_framework.response import Response
          
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveListView(GenericAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
          
              def get(self, request):
                  """獲取所有直播間"""
                  queryset=self.get_queryset()
                  serializer=self.get_serializer(queryset, many=True)
                  return Response(serializer.data)
          
              def post(self, request):
                  """新增直播間"""
                  serializer=self.get_serializer(data=request.data)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  # 返回新增直播間
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
          
          
          class LiveDetailView(GenericAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
          
              def get(self, request, pk):
                  """查詢一間直播間信息"""
                  instance=self.get_object()
                  serializer=self.get_serializer(instance)
                  return Response(serializer.data)
          
              def put(self, request, pk):
                  """修改直播間信息"""
                  instance=self.get_object()
                  serializer=self.get_serializer(instance, data=request.data)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  return Response(serializer.data)
          
              def delete(self, request, pk):
                  """刪除直播間"""
                  instance=self.get_object()
                  # 參數(shù)校驗(yàn)
                  instance.delete()
                  return Response(status=status.HTTP_204_NO_CONTENT)
          

          Mixin擴(kuò)展類

          使用GenericAPIView改寫(xiě)之后的RestAPI接口中,圖書(shū)管理的各個(gè)代碼已經(jīng)變成了通用的代碼,這些代碼和視圖所使用的序列化器類和查詢集已經(jīng)沒(méi)有直接的關(guān)系,DRF框架已經(jīng)將這些代碼做了封裝,就是5個(gè)Mixin擴(kuò)展類。

          案例-Mixin改寫(xiě)RestAPI接口:

          from rest_framework import mixins
          from rest_framework.generics import GenericAPIView
          
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveListView(mixins.ListModelMixin, mixins.CreateModelMixin,GenericAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
          
              def get(self, request):
                  """獲取所有直播間"""
                  return self.list(request)
          
              def post(self, request):
                  """新增直播間"""
                  return self.create(request)
          
          
          class LiveDetailView(mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin, GenericAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
          
              def get(self, request, pk):
                  """查詢一間直播間信息"""
                  return self.retrieve(request, pk)
          
              def put(self, request, pk):
                  """修改直播間信息"""
                  return self.update(request, pk)
          
              def delete(self, request, pk):
                  """刪除直播間"""
                  return self.destroy(request, pk)
          

          子類視圖:Django框架為了方便視圖的編寫(xiě),還提供了9個(gè)子類視圖類。

          子類視圖一定同時(shí)繼承了GenericAPIView和對(duì)應(yīng)的Mixin擴(kuò)展類,同時(shí)類中提供了對(duì)應(yīng)的請(qǐng)求處理方法,并且請(qǐng)求處理方法中調(diào)用的就是Mixin擴(kuò)展類中封裝的通用方法。

          案例-子類視圖改寫(xiě)RestAPI接口:

          from rest_framework.generics import ListCreateAPIView, RetrieveUpdateDestroyAPIView
          
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveListView(ListCreateAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
          
          
          class LiveDetailView(RetrieveUpdateDestroyAPIView):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()

          視圖集ViewSet

          將操作同一組資源相關(guān)的處理方法放在同一個(gè)類中,這個(gè)類叫做視圖集。

          基本使用:

        8. 繼承視圖集父類ViewSet
        9. 視圖集中的處理方法不再以請(qǐng)求方法(get等)命名,而是以對(duì)應(yīng)的action操作命名
        10. url地址配置時(shí)需要明確指明請(qǐng)求方式和處理函數(shù)之間的對(duì)應(yīng)關(guān)系
        11. 常用視圖集父類:

          • ViewSet:繼承自APIView與ViewSetMixin,作用也與APIView基本類似,提供了身份認(rèn)證、權(quán)限校驗(yàn)、流量管理等。
          • GenericViewSet:繼承自GenericAPIView與ViewSetMixin,可以直接搭配Mixin擴(kuò)展類使用。
          • ModelViewSet:繼承自GenericViewSet和5個(gè)Mixin擴(kuò)展類。
          • ReadOnlyModelViewSet:繼承自GenericViewSet,同時(shí)包括了ListModelMixin、RetrieveModelMixin。

          案例-ViewSet改寫(xiě):

          from django.http import Http404
          from rest_framework import status
          from rest_framework.response import Response
          from rest_framework.viewsets import ViewSet
          
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveInfoViewSet(ViewSet):
              def list(self, request):
                  """獲取所有直播間"""
                  lives=LiveInfo.objects.all()
                  serializer=LiveInfoSerializer(lives, many=True)
                  return Response(serializer.data)
          
              def create(self, request):
                  """新增直播間"""
                  serializer=LiveInfoSerializer(data=request.data)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  # 返回新增直播間
                  return Response(serializer.data, status=status.HTTP_201_CREATED)
          
              def retrieve(self, request, pk):
                  """查詢一間直播間信息"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      raise Http404
                  serializer=LiveInfoSerializer(live)
                  return Response(serializer.data)
          
              def update(self, request, pk):
                  """修改直播間信息"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      raise Http404
                  serializer=LiveInfoSerializer(live, data=request.data)
                  serializer.is_valid(raise_exception=True)
                  serializer.save()
                  return Response(serializer.data)
          
              def destroy(self, request, pk):
                  """刪除直播間"""
                  try:
                      live=LiveInfo.objects.get(pk=pk)
                  except LiveInfo.DoesNotExist:
                      raise Http404
                  # 參數(shù)校驗(yàn)
                  live.delete()
          
                  return Response(status=status.HTTP_204_NO_CONTENT)
          
          # urls.py
          from django.conf.urls import url
          
          from booktest import views
          
          urlpatterns=[
              url(r'^lives/$', views.LiveInfoViewSet.as_view({
                  'get': 'list',
                  'post': 'create'
              })),
              url(r'^lives/(?P<pk>\d+)/$', views.LiveInfoViewSet.as_view({
                  'get': 'retrieve',
                  'put': 'update',
                  'delete': 'destroy'
              }))
          ]
          

          案例-GenericViewSet改寫(xiě):

          案例-ModelViewSet并添加了一些額外的方法改寫(xiě):

          from rest_framework.decorators import action
          from rest_framework.response import Response
          from rest_framework.viewsets import ModelViewSet
          
          from livetest.models import LiveInfo
          from livetest.serializers import LiveInfoSerializer
          
          
          class LiveInfoView(ModelViewSet):
              serializer_class=LiveInfoSerializer
              queryset=LiveInfo.objects.all()
              lookup_value_regex='\d+'
          
              @action(methods='get', detail=False)
              def latest(self, request):
                  """查詢最新的直播間"""
                  live=LiveInfo.objects.latest('pk')
                  serializer=self.get_serializer(live)
                  return Response(serializer.data)
          
              @action(methods='put', detail=True)
              def change_pop(self, request, pk):
                  """修改指定直播間的人氣"""
                  live=self.get_object()
                  live.bread=request.data.get('live_pop')
                  live.save()
                  serializer=self.get_serializer(live)
                  return Response(serializer.data)
          
          # urls.py
          
          from django.conf.urls import url
          
          from . import views
          
          urlpatterns=[
              url(r'^lives/$', views.LiveInfoViewSet.as_view({
                  'get': 'list',
                  'post': 'create'
              })),
              url(r'^lives/(?P<pk>\d+)/$', views.LiveInfoViewSet.as_view({
                  'get': 'retrieve',
                  'put': 'update',
                  'delete': 'destroy'
              })),
              url(r'^lives/latest/$', views.LiveInfoViewSet.as_view({
                  'get': 'latest'
              })),
              url(r'^lives/(?P<pk>\d+)/change_pop/$', views.LiveInfoViewSet.as_view({
                  'put': 'change_pop'
              }))
          ]
          

          路由Router:動(dòng)態(tài)生成視圖集中處理方法的url配置項(xiàng)。

          對(duì)于視圖集ViewSet,除了可以自己手動(dòng)進(jìn)行URL配置指明請(qǐng)求方式與action處理函數(shù)之間的對(duì)應(yīng)關(guān)系外,還可以使用路由Router來(lái)自動(dòng)生成路由信息。

          REST framework提供了兩個(gè)Router類:

          • SimpleRouter
          • DefaultRouter

          案例-router改寫(xiě):

          # urls.py
          from rest_framework.routers import DefaultRouter
          
          from livetest import views
          
          urlpatterns=[]
          
          # 創(chuàng)建Router對(duì)象
          router=DefaultRouter()
          # 注冊(cè)router
          router.register('lives', views.LiveInfoView, base_name='live')
          # 添加配置項(xiàng)
          urlpatterns +=router.urls
          
        12. lookup_value_regex: 設(shè)置router生成路由時(shí),從url中提取pk參數(shù)對(duì)應(yīng)的正則表達(dá)式
        13. 視圖集中額外處理方法的配置項(xiàng),需要添加action裝飾器
        14. DefaultRouter和SimpleRouter的區(qū)別多生成一個(gè)根路徑(/)配置項(xiàng),并且每個(gè)配置項(xiàng)地址后都可以跟上.json,直接返回json數(shù)據(jù)。
        15. DRF框架到此常用的功能都已經(jīng)講解完畢了,當(dāng)然DRF框架還有其他功能:認(rèn)證、權(quán)限、限流、過(guò)濾、排序、分頁(yè)和異常處理機(jī)制。將在后面講述,敬請(qǐng)關(guān)注。

          作者簡(jiǎn)介:Python菜鳥(niǎo)工程師,將在接下來(lái)的一段時(shí)間內(nèi)與大家分享一些與Python相關(guān)的知識(shí)點(diǎn)。如若文中出現(xiàn)問(wèn)題,各位大佬多多指點(diǎn),互相學(xué)習(xí)。喜歡的關(guān)注一個(gè)吧!謝謝!

          、web發(fā)展歷史

          Web是World Wide Web的簡(jiǎn)稱,中文譯為萬(wàn)維網(wǎng)

          我們可以將它規(guī)劃成如下的幾個(gè)時(shí)代來(lái)進(jìn)行理解

          • 石器時(shí)代
          • 文明時(shí)代
          • 工業(yè)革命時(shí)代
          • 百花齊放時(shí)代

          石器時(shí)代

          石器時(shí)代指的就是我們的靜態(tài)網(wǎng)頁(yè),可以欣賞一下1997的Apple官網(wǎng)

          最早的網(wǎng)頁(yè)是沒(méi)有數(shù)據(jù)庫(kù)的,可以理解成就是一張可以在網(wǎng)絡(luò)上瀏覽的報(bào)紙,直到CGI技術(shù)的出現(xiàn)

          通過(guò) CGI Perl 運(yùn)行一小段代碼與數(shù)據(jù)庫(kù)或文件系統(tǒng)進(jìn)行交互,如當(dāng)時(shí)的Google(1998年)

          文明時(shí)代

          ASP,JSP大家應(yīng)該都不會(huì)太陌生,最早出現(xiàn)于 2005 年左右,先后出現(xiàn)了微軟的 ASP 和 Java Server Pages [JSP] 等技術(shù),取代了 CGI ,增強(qiáng)了 WEB 與服務(wù)端的交互的安全性,類似于下面這樣,其實(shí)就是Java + HTML

          <%@ page language="java" contentType="text/html; charset=utf-8"
              pageEncoding="utf-8"%>
          <!DOCTYPE html>
          <html>
          <head>
            <meta charset="utf-8">
            <title>JSP demo</title>
          </head>
          <body>
            <img src="http://localhost:8080/web05_session/1.jpg" width=200 height=100 />
          </body>
          </html>

          JSP有一個(gè)很大的缺點(diǎn),就是不太靈活,因?yàn)镴SP是在服務(wù)器端執(zhí)行的,通常返回該客戶端的就是一個(gè)HTML文本。我們每次的請(qǐng)求:獲取的數(shù)據(jù)、內(nèi)容的加載,都是服務(wù)器為我們返回染完成之后的 DOM,這也就使得我們開(kāi)發(fā)網(wǎng)站的靈活度大打折扣

          在這種情況下,同年:Ajax火了(小細(xì)節(jié),這里為什么說(shuō)火了,因?yàn)?Ajax 技術(shù)并不是 2005 年出現(xiàn)的,他的雛形是 1999 年),現(xiàn)在看來(lái)很常見(jiàn)的技術(shù)手段,在當(dāng)時(shí)可是珍貴無(wú)比

          工業(yè)革命時(shí)代

          到這里大家就更熟悉了,

          移動(dòng)設(shè)備的普及,Jquery的出現(xiàn),以及SPA(Single Page Application 單頁(yè)面應(yīng)用)的雛形,Backbone EmberJS AngularJS 這樣一批前端框架隨之出現(xiàn),但當(dāng)時(shí)SPA的路不好走,例如SEO問(wèn)題,SPA 過(guò)多的頁(yè)面、復(fù)雜場(chǎng)景下 View 的綁定等,都沒(méi)有很好的處理

          這幾年的飛速發(fā)展,節(jié)約了開(kāi)發(fā)人員大量的精力、降低了開(kāi)發(fā)者和開(kāi)發(fā)過(guò)程的門檻,極大提升了開(kāi)發(fā)效率和迭代速度,我們可以稱之其為工業(yè)時(shí)代

          百花齊放時(shí)代

          這里沒(méi)有文字,放一張圖感受一下

          PS:這里為什么要說(shuō)這么多Web的歷史,我們可以看到Web技術(shù)的變化之大與快,每一種新的技術(shù)出現(xiàn)都是一些特定場(chǎng)景的解決方案,那我們今天的主角Vue又是為了解決什么呢?

          我們接著往下看

          二、vue是什么

          Vue.js(/vju?/,或簡(jiǎn)稱為Vue)

          是一個(gè)用于創(chuàng)建用戶界面的開(kāi)源JavaScript框架,也是一個(gè)創(chuàng)建單頁(yè)應(yīng)用的Web應(yīng)用框架。 2016年一項(xiàng)針對(duì)JavaScript的調(diào)查表明,Vue有著89%的開(kāi)發(fā)者滿意度。在GitHub上,該項(xiàng)目平均每天能收獲95顆星,為Github有史以來(lái)星標(biāo)數(shù)第3多的項(xiàng)目

          同時(shí)也是一款流行的JavaScript前端框架,旨在更好地組織與簡(jiǎn)化Web開(kāi)發(fā)。Vue所關(guān)注的核心是MVC模式中的視圖層,同時(shí),它也能方便地獲取數(shù)據(jù)更新,并通過(guò)組件內(nèi)部特定的方法實(shí)現(xiàn)視圖與模型的交互

          PS: Vue作者尤雨溪是在為AngularJS工作之后開(kāi)發(fā)出了這一框架。他聲稱自己的思路是提取Angular中為自己所喜歡的部分,構(gòu)建出一款相當(dāng)輕量的框架

          最早發(fā)布于2014年2月

          三、Vue核心特性

          數(shù)據(jù)驅(qū)動(dòng)(MVVM)

          MVVM表示的是 Model-View-ViewModel

          • Model:模型層,負(fù)責(zé)處理業(yè)務(wù)邏輯以及和服務(wù)器端進(jìn)行交互
          • View:視圖層:負(fù)責(zé)將數(shù)據(jù)模型轉(zhuǎn)化為UI展示出來(lái),可以簡(jiǎn)單的理解為HTML頁(yè)面
          • ViewModel:視圖模型層,用來(lái)連接Model和View,是Model和View之間的通信橋梁

          這時(shí)候需要一張直觀的關(guān)系圖,如下

          組件化

          1.什么是組件化

          一句話來(lái)說(shuō)就是把圖形、非圖形的各種邏輯均抽象為一個(gè)統(tǒng)一的概念(組件)來(lái)實(shí)現(xiàn)開(kāi)發(fā)的模式,在Vue中每一個(gè).vue文件都可以視為一個(gè)組件

          2.組件化的優(yōu)勢(shì)

          • 降低整個(gè)系統(tǒng)的耦合度,在保持接口不變的情況下,我們可以替換不同的組件快速完成需求,例如輸入框,可以替換為日歷、時(shí)間、范圍等組件作具體的實(shí)現(xiàn)

          • 調(diào)試方便,由于整個(gè)系統(tǒng)是通過(guò)組件組合起來(lái)的,在出現(xiàn)問(wèn)題的時(shí)候,可以用排除法直接移除組件,或者根據(jù)報(bào)錯(cuò)的組件快速定位問(wèn)題,之所以能夠快速定位,是因?yàn)槊總€(gè)組件之間低耦合,職責(zé)單一,所以邏輯會(huì)比分析整個(gè)系統(tǒng)要簡(jiǎn)單

          • 提高可維護(hù)性,由于每個(gè)組件的職責(zé)單一,并且組件在系統(tǒng)中是被復(fù)用的,所以對(duì)代碼進(jìn)行優(yōu)化可獲得系統(tǒng)的整體升級(jí)

          指令系統(tǒng)

          解釋:指令 (Directives) 是帶有 v- 前綴的特殊屬性

          作用:當(dāng)表達(dá)式的值改變時(shí),將其產(chǎn)生的連帶影響,響應(yīng)式地作用于 DOM

          • 常用的指令
            • 條件渲染指令 v-if
            • 列表渲染指令v-for
            • 屬性綁定指令v-bind
            • 事件綁定指令v-on
            • 雙向數(shù)據(jù)綁定指令v-model

          沒(méi)有指令之前我們是怎么做的?是不是先要獲取到DOM然后在....干點(diǎn)啥

          四、Vue跟傳統(tǒng)開(kāi)發(fā)的區(qū)別

          沒(méi)有落地使用場(chǎng)景的革命不是好革命,就以一個(gè)高頻的應(yīng)用場(chǎng)景來(lái)示意吧

          注冊(cè)賬號(hào)這個(gè)需求大家應(yīng)該很熟悉了,如下

          jquery來(lái)實(shí)現(xiàn)大概的思路就是選擇流程dom對(duì)象,點(diǎn)擊按鈕隱藏當(dāng)前活動(dòng)流程dom對(duì)象,顯示下一流程dom對(duì)象

          如下圖(代碼就不上了,上了就篇文章就沒(méi)了..)

          vue來(lái)實(shí)現(xiàn),我們知道vue基本不操作dom節(jié)點(diǎn), 雙向綁定使dom節(jié)點(diǎn)跟視圖綁定后,通過(guò)修改變量的值控制dom節(jié)點(diǎn)的各類屬性。

          所以其實(shí)現(xiàn)思路為: 視圖層使用一變量控制dom節(jié)點(diǎn)顯示與否,點(diǎn)擊按鈕則改變?cè)撟兞?,如下圖

          總結(jié)就是:

          • Vue所有的界面事件,都是只去操作數(shù)據(jù)的,Jquery操作DOM
          • Vue所有界面的變動(dòng),都是根據(jù)數(shù)據(jù)自動(dòng)綁定出來(lái)的,,Jquery操作DOM

          五、Vue和React對(duì)比

          這里就做幾個(gè)簡(jiǎn)單的類比吧,當(dāng)然沒(méi)有好壞之分,只是使用場(chǎng)景不同

          相同點(diǎn)

          • 都有組件化思想
          • 都支持服務(wù)器端渲染
          • 都有Virtual DOM(虛擬dom)
          • 數(shù)據(jù)驅(qū)動(dòng)視圖
          • 都有支持native的方案:VueweexReactReact native。
          • 都有自己的構(gòu)建工具:Vuevue-cli、ReactCreate React App。

          區(qū)別

          • 數(shù)據(jù)變化的實(shí)現(xiàn)原理不同。react使用的是不可變數(shù)據(jù),而Vue使用的是可變的數(shù)據(jù)

          • 組件化通信的不同。react中我們通過(guò)使用回調(diào)函數(shù)來(lái)進(jìn)行通信的,而Vue中子組件向父組件傳遞消息有兩種方式:事件和回調(diào)函數(shù)

          • diff算法不同。react主要使用diff隊(duì)列保存需要更新哪些DOM,得到patch樹(shù),再統(tǒng)一操作批量更新DOM。Vue 使用雙向指針,邊對(duì)比,邊更新DOM。

          參考文獻(xiàn)

          • https://segmentfault.com/a/1190000016269636
          • https://zh.wikipedia.org/zh-cn/Vue.js
          • https://zhuanlan.zhihu.com/p/20197803
          • https://zhuanlan.zhihu.com/p/38296857

            @JS語(yǔ)音答題社群

          1.初始設(shè)置

          今天編寫(xiě)一個(gè)elm應(yīng)用實(shí)例。首先,做好初始設(shè)置。

          Elm init

          運(yùn)行命令后,會(huì)在目錄中創(chuàng)建一個(gè)elm.json配置文件,以及一個(gè)名為src的目錄,我們?cè)谠撃夸浿杏镁庉嬈?。可以是vscode、atom等專門甚至最簡(jiǎn)單的記事本。在src目錄中創(chuàng)建一個(gè)名為main.elm文件。該文件就是要編寫(xiě)應(yīng)用代碼的文件。

          用編輯器打開(kāi)文件,建議用vscode,最世界上最流行的程序代碼編輯工具。

          初次設(shè)置完成后,正式進(jìn)入elm編程之旅。

          2.薛定諤的貓

          估計(jì)許多人都聽(tīng)說(shuō)過(guò)薛定諤的貓的實(shí)驗(yàn)。在一個(gè)封閉的房間中放入一只活貓,房間中放置有毒藥陷阱。因?yàn)榉块g是密封的。在房門關(guān)閉時(shí),我們無(wú)法從外界觀測(cè)到貓是死是活,因此貓?zhí)幵谝环N非死非活的不確定性狀態(tài)中;當(dāng)我們打開(kāi)房門時(shí),可以觀察到貓的狀態(tài),貓的死活也確定下來(lái)。薛定諤的貓闡述的是一種量子不確定現(xiàn)象。

          我們的任務(wù)就是編寫(xiě)模擬薛定諤的貓實(shí)驗(yàn)的elm應(yīng)用代碼。

          3.定義規(guī)則

          我們需要定義一個(gè)包含門、鎖、毒藥陷阱(拔掉保險(xiǎn)絲時(shí)開(kāi)始運(yùn)轉(zhuǎn))三種事物的房間。

          規(guī)則1:如果門開(kāi),那么拔掉保險(xiǎn)絲。

          規(guī)則2:如果保險(xiǎn)絲被拔掉,它能夠重新放上。

          規(guī)則3:如果門開(kāi),重新放保險(xiǎn)絲。

          規(guī)則4:如果門開(kāi),能關(guān)門。

          規(guī)則5:如果門關(guān),能開(kāi)門或上鎖。

          規(guī)則6:如果上鎖,能解鎖。

          每一種事物都可以用多種可能的狀態(tài)組合表示,并且明確可能狀態(tài)之間的轉(zhuǎn)換規(guī)則是什么。

          上述規(guī)矩轉(zhuǎn)成對(duì)象狀態(tài)如下:

          -- 可能的狀態(tài):

          Door(門):

          Locked(上鎖)

          Closed(關(guān)門)

          Opened(開(kāi)門)

          Alarm(毒藥陷阱):

          Armed (觸發(fā))

          Disarmed (沒(méi)觸發(fā))

          Triggered(保險(xiǎn)絲)

          --可能的狀態(tài)組合有:

          Locked + Armed(上鎖+毒藥陷阱觸發(fā))

          Locked + Triggered(上鎖+拔掉保險(xiǎn)絲)

          Locked + Disarmed (上鎖+毒藥陷阱沒(méi)有觸發(fā))

          Unlocked + Armed (開(kāi)鎖+毒藥陷阱觸發(fā))

          Unlocked + Triggered (開(kāi)鎖+拔掉保險(xiǎn)絲)

          Unlocked + Disarmed (開(kāi)鎖+毒藥陷阱沒(méi)有觸發(fā))

          Opened + Triggered (開(kāi)門+拔掉保險(xiǎn)絲)

          Opened + Disarmed(開(kāi)門+毒藥陷阱沒(méi)有觸發(fā))

          --互相轉(zhuǎn)換的狀態(tài)有:

          Door(門):

          Closed <-> Locked (關(guān)門 <-> 上鎖)

          Closed <-> Opened (關(guān)門 <-> 開(kāi)門)

          Alarm(毒藥陷阱):

          Armed -> Triggered (毒藥陷阱觸發(fā) -> 拔掉保險(xiǎn)絲)

          Triggered -> Disarmed (拔掉保險(xiǎn)絲 -> 毒藥陷阱沒(méi)有觸發(fā))

          Armed <-> Disarmed (毒藥陷阱觸發(fā) <-> 毒藥陷阱沒(méi)有觸發(fā))

          4.建立模型

          OK,規(guī)則確定好后,我們會(huì)發(fā)現(xiàn),在監(jiān)測(cè)開(kāi)始的時(shí)間點(diǎn),房間只能真正存在一種可能的狀態(tài)組合。因此要定義一個(gè)顯示房間狀態(tài)的模型model,它包括了監(jiān)測(cè)時(shí)間點(diǎn)時(shí)的狀態(tài)等。這個(gè)模型要將失敗的情景加上去,防止觀測(cè)不到的錯(cuò)誤產(chǎn)生,用自定義類型添加到mail.elm代碼中去。

          ? ? ? type Model
          ? ? ? ? ? ?=DisplayingRoom DoorState AlarmState
          ? ? ? ? ? ? | Failure String
          ?
          ? ? ? type DoorState
          ? ? ? ? ? ?=Opened
          ? ? ? ? ? ? | Closed
          ? ? ? ? ? ? | Locked
          ?
          ? ? ? type AlarmState
          ? ? ? ? ? ?=Armed
          ? ? ? ? ? ? | Disarmed
          ? ? ? ? ? ? | Triggered

          好了,我們薛定諤的貓模型建立起來(lái),完成第一步代碼。

          5.建立更新模型的邏輯

          要對(duì)模型進(jìn)行更新,必須要有信息通知到達(dá)才能開(kāi)始更新。因此更新邏輯前需先定義消息:

          ? ? ? type Msg
          ? ? ? ? ? ?=Open
          ? ? ? ? ? ? | Close
          ? ? ? ? ? ? | Lock
          ? ? ? ? ? ? | Unlock
          ? ? ? ? ? ? | Arm
          ? ? ? ? ? ? | Disarm

          消息定義后,開(kāi)始定義update更新函數(shù),實(shí)現(xiàn)消息->模型->返回一個(gè)新模型,我們先從檢查房間狀態(tài)開(kāi)始定義:

          ? ? ? update msg model=
          ? ? ? ? ? ? case model of
          ? ? ? ? ? ? ? ? ? DisplayingRoom doorState alarmState ->
          ? ? ? ? ? ? ? ? ? ? ? ? ...?
          ? ? ? ? ? ? Failure errorMessage ->
          ? ? ? ? ? ? ? ? ? ? ? ? model

          先建立函數(shù)框架,如果因?yàn)楣收显驘o(wú)法觀測(cè)到房間狀態(tài),那么更新函數(shù)只能返回原來(lái)的模型model。

          當(dāng)可以正常地觀測(cè)房間時(shí),如果房門處于打開(kāi)狀態(tài)下,受到的限制最大:門開(kāi)、鎖解、毒藥陷阱不能起作用。

          第一步,讓我們添加房門打開(kāi)時(shí)的更新代碼,擴(kuò)展原來(lái)基礎(chǔ)框架:

          ? ? ? update : Msg -> Model -> Model
          ? ? ? update msg model?=
          ? ? ? ? ? ? case model of
          ? ? ? ? ? ? ? ? ? DisplayingRoom doorState alarmState ->
          ? ? ? ? ? ? ? ? ? ? ? ? case doorState of
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Opened ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case msg of
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Close ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Closed alarmState
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _ -> Failure "故障,觀測(cè)不到!”?
          ? ? ? ? ? ? ? ? ? Failure _ ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? model

          直到門關(guān),毒藥陷阱才可能被觸發(fā)。

          上述代碼中,我們處理了,房間由開(kāi)門轉(zhuǎn)為關(guān)門時(shí)模型的更新代碼:

          ? ? ? ? ? ? ? ? ? DisplayingRoom Closed alarmState

          其中alarmState是包含了毒藥陷阱觸發(fā)或沒(méi)有被觸發(fā)兩種狀態(tài)的變量,確保門由開(kāi)轉(zhuǎn)關(guān)時(shí)。毒藥陷阱的狀態(tài)可以原封不動(dòng)地轉(zhuǎn)移到新的模型上。

          ? ? ? ? ? ? ? ? ? _ -> Failure "故障,觀測(cè)不到!"

          這是一個(gè)兜底代碼,確保無(wú)法獲取監(jiān)測(cè)狀態(tài)時(shí)返回一個(gè)消息通知。這種考慮周全的機(jī)制,也是elm編譯運(yùn)行不出錯(cuò)的優(yōu)勢(shì)。

          第二步,讓我們添加門關(guān)時(shí)的更新邏輯,較為復(fù)雜,打開(kāi)門時(shí),里面涉及到鎖的狀態(tài)、毒藥陷阱的狀態(tài)。

          在門關(guān)的前提下,如果收到開(kāi)門的消息,那么要檢測(cè)毒藥陷阱的狀態(tài),觸發(fā)或沒(méi)有觸發(fā)。因?yàn)槎舅幭葳宓臓顟B(tài),關(guān)系到貓的死活。如果毒藥陷阱一直不被觸發(fā),貓仍然存活。但這些在未打開(kāi)房門的情況下,我們是無(wú)法監(jiān)測(cè)而至的,現(xiàn)在添加門關(guān)情況下的代碼。

          ? ? ? Closed ->
          ? ? ? ? ? ? case?msg of
          ? ? ? ? ? ? ? ? ? Open ->
          ? ? ? ? ? ? ? ? ? ? ? ? case alarmState of
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Armed ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Opened Triggered
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? _ ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Opened alarmState
          ???????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Lock ->
          ?????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Locked alarmState
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Arm ?->
          ???????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Closed Armed
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Disarm ->
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DisplayingRoom Closed Disarmed
          ? ? ? ? ? ? _ -> Failure "故障,觀測(cè)不到!"

          第三步,讓我們添加門鎖住情況下的更新代碼:

          ? ? ? Locked ->
          ? ? ? ? ? ? case msg of
          ? ? ? ? ? ? ? ? ? Unlock ->
          ? ? ? ? ? ? ? ? ? ? ? ? DisplayingRoom Closed alarmState
          ? ? ? ? ? ? ? ? ? Arm -> DisplayingRoom Locked Armed
          ? ? ? ? ? ? ? ? ? ? ? ? Disarm -> DisplayingRoom Locked Disarmed
          ? ? ? ? ? ? ? ? ? _ -> Failure "故障,觀測(cè)不到!"

          好了,我們代碼中最重要的引擎,update更新函數(shù)完成了。下一步是把更新后的模型通過(guò)UI視圖顯示出來(lái),這對(duì)于elm來(lái)說(shuō)輕而易舉,因?yàn)樗旧砭褪菫榱藰?gòu)建前端而生。

          6.構(gòu)建前端界面

          elm有一個(gè)HTML庫(kù),它使我們可以在elm中編寫(xiě)HTML代碼:

          ? ? ? import Html exposing (..)

          現(xiàn)在通過(guò)來(lái)引入這個(gè)庫(kù),為簡(jiǎn)單起見(jiàn),這里只是把房間的狀態(tài)用文字在html界面中展示出來(lái):

          首先建立一個(gè)failure情況下的顯示函數(shù):

          ? ? ? failure message=
          ? ? ? ? ? ? div []
          ? ? ? ? ? ? ? ? ? [ p [] [ "故障,觀測(cè)不到!" ] ]

          然后把各種狀態(tài)組合用文字展示出來(lái),這里要注意的是,按照房間規(guī)則,門關(guān)時(shí)的狀態(tài)要有兩個(gè)可能的消息,但是門開(kāi)時(shí)只需要一條消息即可。最后UI界面代碼如下:

          ? ? ? View: Model -> Html Msg
          ? ? ? view model=
          ? ? ? ? ? ? Case model of
          ???????? ? ? ? ? ? Failure message=
          ? ? ? ? ? ? ? ? ? ? ? ? div []
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  p [] [ "故障,觀測(cè)不到!" ] ]
          ?????????
          ???????? ? ? ? ? ? DispalyingRoom doorState alarmState?=
          ? ? ? ? ? ? ? ? ? ? ? ? div []
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [case doorState of
          ???????????????????????? ? ? ? ? ? Opened ->
          ??????????????????????????? ? ? ? ? ? Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "門開(kāi)?-> 關(guān)門!" ] ]
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Closed=
          ??????????????????????????? ? ? ? ? ? Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "門關(guān)?-> 開(kāi)門!" ] ]
          ???????????????????????? ? ? ? ? ? Locked=
          ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "門鎖?-> 開(kāi)鎖!" ] ]
          ?????????????????????? ? ? ? ? ? ]
          ?????????????? ? ? ?,div []
          ????????????????? ? ? ? ? ? [ case alarmState of
          ???????????????????????? ? ? ? ? ? Armed ->
          ??????????????????????????? ? ? ? ? ? Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "毒藥陷阱被觸發(fā)?-> 開(kāi)門!" ] ]
          ???????????????????????? ? ? ? ? ? disArmed ->
          ??????????????????????????? ? ? ? ? ? Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "毒藥陷阱沒(méi)有觸發(fā)?-> 毒藥陷阱被觸發(fā)!" ] ]
          ???????????????????????? ? ? ? ? ? Triggered ->
          ??????????????????????????? ? ? ? ? ? Div []
          ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [ p [] [ "拔掉保險(xiǎn)絲?-> 毒藥陷阱被觸發(fā)/毒藥陷阱沒(méi)有觸發(fā)!" ] ]
          ????????????????? ? ? ? ? ? ? ? ? ]

          為簡(jiǎn)單起見(jiàn),這里只用文字顯示作為HTML的內(nèi)容,但elm的HTML庫(kù)還有許多強(qiáng)大的功能,可以與react的JSX比美。下回我們?cè)賴L試構(gòu)建更絢麗的圖象或動(dòng)畫(huà)顯示房間狀態(tài)。

          7.連接代碼

          模型、更新、界面代碼已經(jīng)寫(xiě)好,剩下的是把這幾部分連接起來(lái)。我們需要ELM的主核心Browser模塊里面的沙箱sandbox:

          ? ? ? Import Browser exposing (..)

          沙箱允許您創(chuàng)建使用elm架構(gòu)的應(yīng)用程序,但不會(huì)與”外部世界”對(duì)話(即任何外部的API或JavaScript,如果需要與外部世界對(duì)話可以用Browser.element或其它)。建立沙箱前我們還要聲明一下房間的初始狀態(tài),假說(shuō)為門已經(jīng)關(guān)閉、貓已經(jīng)放入、毒藥陷阱的保險(xiǎn)絲已經(jīng)拔開(kāi)。

          ? ? ? initialModel : Model
          ? ? ? initialModel=DisplayingRoom Closed Armed
          ? ? ? main : Program () Model Msg
          ? ? ? ? ? ? main=Browser.sandbox
          ? ? ? ? ? ? ? ? ? { init=initialModel
          ? ? ? ? ? ? ? ? ? , view=view
          ? ? ? ? ? ? ? ? ? , update=update
          ? ? ? ? ? ? ? ? ? }

          8.運(yùn)行程序

          在終端中運(yùn)行命令:

          ? ? ? Elm make src/main.elm

          Elm make 是elm的編譯器命令,它把main.elm編譯成一個(gè)html文件。可以用瀏覽器打個(gè)這個(gè)html文件,讓我們作為觀察者,通過(guò)開(kāi)門、關(guān)門的點(diǎn)擊操作來(lái)模擬這個(gè)薛定諤的貓的實(shí)驗(yàn)。

          (備注:本周寫(xiě)的文章學(xué)習(xí)了尼莫的《我希望有的榆木示例》思路,三體狀態(tài)不容易描述,用javascript寫(xiě)估計(jì)一大堆代碼,elm容易讀些,薛定諤的貓實(shí)驗(yàn)因?yàn)樯婕暗讲淮_定性,應(yīng)該還要引入隨機(jī)發(fā)生器,有時(shí)間再慢慢改進(jìn)。)


          主站蜘蛛池模板: 国产一区二区三精品久久久无广告| bt7086福利一区国产| 国产AV午夜精品一区二区三区| 99精品国产高清一区二区| 一区二区三区久久精品| 成人精品一区二区激情| 久久久久人妻一区精品色| 亚洲熟女少妇一区二区| 天码av无码一区二区三区四区| 相泽南亚洲一区二区在线播放 | 免费人人潮人人爽一区二区| 国产精品视频分类一区| 97av麻豆蜜桃一区二区| 久久91精品国产一区二区| 日本精品一区二区三区在线观看| 国产精品一区二区电影| 国产一区二区三区精品久久呦| 久久精品无码一区二区三区免费| 中文字幕一区在线观看视频| 中文字幕精品一区影音先锋| 精品国产福利一区二区| 亚洲AV一区二区三区四区| 在线成人一区二区| 免费看无码自慰一区二区| 国产成人精品亚洲一区| 精品一区二区三区3d动漫| 一区二区三区免费高清视频| 中文字幕一区在线观看| 免费日本一区二区| 精品国产aⅴ无码一区二区| 国模私拍福利一区二区| 免费看无码自慰一区二区| 亚洲一区精品伊人久久伊人| 91麻豆精品国产自产在线观看一区| 中文字幕一精品亚洲无线一区| 久久精品亚洲一区二区| 亚洲色一区二区三区四区| 波多野结衣一区二区三区高清av | 精品国产乱码一区二区三区| 国产亚洲综合一区二区三区 | 免费无码一区二区三区蜜桃大|