1.什么是前后端分離開(kāi)發(fā):
就是前后端工程師約定好數(shù)據(jù)交互接口测萎,并行的進(jìn)行開(kāi)發(fā)和測(cè)試,后端只提供數(shù)據(jù)届巩,不負(fù)責(zé)將數(shù)據(jù)渲染到頁(yè)面上硅瞧,前端通過(guò)HTTP請(qǐng)求獲取數(shù)據(jù)并負(fù)責(zé)將數(shù)據(jù)渲染到頁(yè)面上,這個(gè)工作是交給瀏覽器中的JavaScript代碼來(lái)完成恕汇。
2.前后端開(kāi)發(fā)的好處:
- 1.提升開(kāi)發(fā)效率
- 2.增強(qiáng)代碼的可維護(hù)性腕唧。
- 支持多終端和服務(wù)化架構(gòu)。
3.數(shù)據(jù)接口(FBV - 基于函數(shù)的視圖):
FBV - 基于函數(shù)的視圖
CBV - 基于類的視圖
前后端分離的開(kāi)發(fā)模式下瘾英,后端需要為前端提供數(shù)據(jù)接口枣接,這些接口通常返回JSON格式的數(shù)據(jù)。在Django項(xiàng)目中缺谴,我們可以先將對(duì)象處理成字典但惶,然后就可以利用Django封裝的JsonResponse向?yàn)g覽器返回JSON格式的數(shù)據(jù),例如以下例子
def show_subjects(request):
queryset = Subject.objects.all() #獲取所有學(xué)科對(duì)象
subjects = [] #定義一個(gè)空字典
for subject in queryset: #遍歷查詢學(xué)科的到的對(duì)象湿蛔,并把數(shù)據(jù)處理一個(gè)字典保存在subjects列表容器中
subjects.append({
'no': subject.no,
'name': subject.name,
'intro': subject.intro,
'isHot': subject.is_hot
})
return JsonResponse(subjects, safe=False) #利用JsonResponse完成對(duì)列表的序列化膀曾,返回json格式的數(shù)據(jù)。由于序列化的是一個(gè)列表而不是字典阳啥,所以需要指定safe參數(shù)的值為False
值得注意的是添谊,這樣的處理雖然沒(méi)問(wèn)題,但是如果對(duì)象的屬性很多時(shí)候察迟,情況就比較糟糕了碉钠,為此我們可以使用一個(gè)比較小眾的第三庫(kù)bpmappers來(lái)簡(jiǎn)化將對(duì)象轉(zhuǎn)成字典的操作
4.使用bpmappers
4.1 安裝第三方庫(kù)bpmappers
pip install bpmappers
4.2 創(chuàng)建一個(gè)mappers.py文件編寫映射器,實(shí)現(xiàn)對(duì)象到字典的轉(zhuǎn)換卷拘。
from bpmappers.djangomodel import ModelMapper
from poll2.models import Subject
class SubjectMapper(ModelMapper):
class Meta:
model = Subject#相關(guān)聯(lián)的類
fields = ('carno', 'owner') #包含的字段 exclude()排除的字段
4.3 打開(kāi)view.py修改視圖函數(shù)
ef show_subjects(request):
queryset = Subject.objects.all()
subjects = []
for subject in queryset:
subjects.append(SubjectMapper(subject).as_dict()) #序列化成字典
#生成式:subjects = [SubjectMapper(subject).as_dict()
for subject in queryset]
return JsonResponse(subjects, safe=False)
4.4 配置URL映射喊废,然后訪問(wèn)該接口,可以得到如下所示的JSON格式數(shù)據(jù)栗弟。
前端部分代碼如下所示:
5.網(wǎng)絡(luò)API接口設(shè)計(jì)和配置
網(wǎng)絡(luò)API - 通過(guò)HTTP請(qǐng)求一個(gè)URL獲得(JSON)數(shù)據(jù)
4.5安裝三方庫(kù)djangorestframework
pip install djangorestframework
pip install drf-extensions
4.6配置setting.py文件
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
]
#配置一個(gè)數(shù)據(jù)庫(kù)
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'apidemo',
'HOST': '47.107.87.249',
'PORT': 3306,
'USER': 'renwoxing', #需要授權(quán)操作
'PASSWORD': 'xxx',
'CHARSET': 'utf8',
'TIME_ZONE': 'Asia/Chongqing',
}
}
#配置緩存
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': [
'redis://47.107.87.249/0',
],
'KEY_PREFIX': 'apidemo',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'CONNECTION_POOL_KWARGS': {
'max_connections': 512,
},
'PASSWORD': ' ',
}
},
4.7用二維表生成模型
4.7.1創(chuàng)建應(yīng)用污筷,執(zhí)行以下代碼:
python manage.py startapp common
4.7.2打開(kāi)應(yīng)用目錄下的model.py 在終端執(zhí)行以下代碼(前提是數(shù)據(jù)庫(kù)已經(jīng)配好且有操作權(quán)限):
python manage.py inspectdb > common/models.py
查看model.py可以看到已經(jīng)遷移成功,接下來(lái)需要根據(jù)需要對(duì)生成的模型進(jìn)行微調(diào)
5. REST架構(gòu)
REST架構(gòu)簡(jiǎn)單介紹
REpresentational State Transfer ---> 表述性狀態(tài)轉(zhuǎn)移
REST架構(gòu)兩大特點(diǎn): 無(wú)狀態(tài)和冪等性
HTTP協(xié)議請(qǐng)求行 GET / POST / DELETE / PUT / PATCH
新建 ---> POST-不需要冪等性
查看 ---> GET
更新 ---> PUT/PATCH
刪除 ---> DELETE
5.1 使用drf給的裝飾器修改視圖函數(shù)乍赫,例如
from rest_framework.decorators import api_view
@api_view(('GET', )) #限制視圖函數(shù)支持哪些請(qǐng)求方法
def get_provinces(request):
"""獲取省級(jí)行政區(qū)域"""
queryset = District.objects\
#不能直接寫pid==null
.filter(pid__isnull=True).only('distid', 'name')
#序列化對(duì)象瓣蛀,是一個(gè)集合需要寫上many=True
serializer = DistrictSimpleSerializer(queryset, many=True)
return Response(serializer.data)#框架給的response
5.3 利用drf框架里的ModelSerializer自定義序列化器,新建一個(gè)serializer.py文件雷厂,編寫如下代碼
class DistrictSimpleSerializer(serializers.ModelSerializer):
"""行政區(qū)域簡(jiǎn)單序列化器"""
class Meta:
model = District
fields = ('distid', 'name')
5.4 創(chuàng)建映射
5.4.1 在自己的應(yīng)用底下建立一個(gè)urls.py,便于管理自己的各種接口
from common.models import District
urlpatterns = [
path('districts/', get_provinces),
5.4.2 修改全局映射URL
urlpatterns = [
path('', index),
#相當(dāng)于前綴
path('api/', include('common.urls')),
path('admin/', admin.site.urls),
]
此時(shí)就可以訪問(wèn)自己創(chuàng)建的接口看到數(shù)據(jù)了惋增。
6. CBV 基于類的視圖定制接口
6.1在view.py文件中定義類
繼承drf框架的類
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
"""樓盤視圖"""
#定義查詢集
queryset = Estate.objects.all().defer('agents','district')
#序列化查詢集
serializer_class = EstateSerializer
6.2自定義序列化器
class EstateSerializer(serializers.ModelSerializer):
"""樓盤序列化器(GET、DELETE請(qǐng)求)"""
class Meta:
model = Estate
exclude = ('district', 'agents')
6.3映射urls
urlpatterns = [
path('estates/', EstateView.as_view()),
path('estates/<int:pk>/', EstateView.as_view()),可以拿單個(gè)樓盤
]
在瀏覽器中輸入127.0.0.1:8000/api/estates/便可以看到數(shù)據(jù)改鲫。
7.全套接口視圖集,兩行代碼寫接口(不推薦使用)
??打開(kāi) view.py 定義視圖函數(shù):
from rest_framework.viewsets import ModelViewSet
class HouseTypeViewSet(ModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
??打開(kāi) urls.py創(chuàng)建映射 :
urlpatterns = [
...
]
# 路由器
router = SimpleRouter() # 簡(jiǎn)單路由
router.register('housetypes', HouseTypeViewSet) # 注冊(cè)
urlpatterns += router.urls # 添加
補(bǔ)充1
若需要拿到單個(gè)數(shù)據(jù)則可以用重寫get方法诈皿。
打開(kāi)view.py文件林束,找到定義的類,然后修改為如下:
繼承drf框架的類
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
"""樓盤視圖"""
#定義查詢集
queryset = Estate.objects.all().defer('agents','district')
#重寫get方法
def get_serializer_class(self):
if self.request.method in ('POST', 'PUT', 'PATCH'):
return EstatePostSerializer
else:
return EstateSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
cls = RetrieveAPIView
else:
cls = ListAPIView
return cls.get(self, request, *args, **kwargs)
補(bǔ)充2
利用DRF進(jìn)行 分頁(yè)
1. PageNumberPagination ---> 按頁(yè)碼分頁(yè)
2. LimitoffsetPagination ---> 跳過(guò)N條,查第N+1條
3. CursorPagination --->游標(biāo)進(jìn)行分頁(yè)
- 自定義分頁(yè)(商業(yè)項(xiàng)目中不推薦使用稽亏,可能會(huì)暴露服務(wù)器規(guī)模)
中間鍵后面添加以下代碼(全部實(shí)現(xiàn)分頁(yè))
# DRF配置文件
REST_FRAMEWORK = {
# 按頁(yè)碼分頁(yè)
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5,
}
# 若單個(gè)視圖類不想分頁(yè)可以加以下代碼:
pagination_class = None
2)游標(biāo)分頁(yè)(不能使用緩存壶冒,應(yīng)無(wú)法準(zhǔn)確獲取上下文信息)
# 定義游標(biāo)分頁(yè)類
class EstatePagination(CursorPagination):
page_size_query_param = 'size' # 自定義單頁(yè)顯示記錄數(shù)
max_page_size = 20 # 單頁(yè)最多記錄數(shù)
ordering = 'estateid' # 按樓盤id分頁(yè)
# 在視圖類里面調(diào)用
pagination_class = EstatePagination
補(bǔ)充3
利用DRF配置緩存
添加緩存也有兩種方式(繼承父類CacheResponseMixin,加類裝飾器)
準(zhǔn)備:
- 依賴項(xiàng)drf-extensions==0.5.0 為DRF提供緩存擴(kuò)展
1)修改配置文件setting.py
# 緩存混入類
REST_FRAMEWORK_EXTENSIONS = {
'DEFAULT_CACHE_RESPONSE_TIMEOUT': 120,
'DEFAULT_USE_CACHE': 'default',
'DEFAULT_OBJECT_CACHE_KEY_FUNC': 'rest_framework_extensions.utils.default_object_cache_key_func',
'DEFAULT_LIST_CACHE_KEY_FUNC': 'rest_framework_extensions.utils.default_list_cache_key_func',
}
- django里的裝飾器@method_decorator可以將裝飾函數(shù)的裝飾器變成可以裝飾類方法的裝飾器
# 讓視圖類繼承混入類:
class EstatesView(CacheResponseMixin):
# 帶Mixin的類是混入類,混入類必須寫在前面
class EstateView(CacheResponseMixin, ListCreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = Estate.objects.all().defer('agents')
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_class = EstateFilterSet
@method_decorator(decorator=cache_page(500), name='list')
@method_decorator(decorator=cache_page(120), name='retrieve')
class HouseTypeViewSet(ModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
# 拒絕分頁(yè)
pagination_class = None
補(bǔ)充4
接口限流: 限制接口的訪問(wèn)頻率
原理: 在緩存(Redis)內(nèi)記錄IP地址,并記錄訪問(wèn)次數(shù),當(dāng)請(qǐng)求超過(guò)閾值則限制訪問(wèn)
配置文件截歉,打開(kāi)settings.py
REST_FRAMEWORK = {
# 限流配置
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '5/min', #定義限流頻率
},
}
若不想限流,可以在視圖類里面加入以下代碼:
throttle_classes = ()
補(bǔ)充5
1.數(shù)據(jù)接口篩選(自定義序列化器和自定義模糊查詢)
例如:
- view.py
class HouseInfoView(ListCreateAPIView, RetrieveUpdateDestroyAPIView):
'''房源展示圖集'''
queryset = HouseInfo.objects.all().select_related('type', 'estate')
#導(dǎo)入自定義的序列化器
serializer_class = HouseInfoSerializer
==篩選部分==
# 繼承父類支持篩選(DjangoFilterBackend)胖腾,排序(OrderingFilter)
filter_backends = (DjangoFilterBackend, OrderingFilter)
# 自定義方法查詢
filter_class = HouseInfoFilterSet
# 按'area', 'price'排序
ordering_fields = ('area', 'price')
=========================自定義序列化器====================================
- serializers.py
class HouseInfoSerializer(serializers.ModelSerializer):
'''房源序列化器'''
# 都是關(guān)聯(lián)鍵,需要二次處理的字段瘪松,自定義序列化的方法(SerializerMethodField)
type = serializers.SerializerMethodField()
estate = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
district = serializers.SerializerMethodField()
@staticmethod
def get_type(obj): #obj是上述序列化對(duì)象
# return obj.type.name #顯示type中的name名稱咸作,是一個(gè)字段
return obj.type.name
@staticmethod
def get_estate(obj):
# return obj.estate.name #顯示estate中的name名稱,得到是一個(gè)字段宵睦,例如“樓盤名稱”
'''重寫一個(gè)序列器记罚,將字段("樓盤")可拓展查詢?yōu)樽值洌值渲械淖侄斡尚露x的序列器(EstateSimpleSerializer)決定'''
return EstateSimpleSerializer(obj.estate).data
@staticmethod
def get_tags(obj):
'''tag是多對(duì)多的關(guān)系状飞,因此返回是一個(gè)集合毫胜,可以用生成器的方式將對(duì)象一個(gè)個(gè)取出來(lái)'''
return [tag.content for tag in obj.tags.all()]
@staticmethod
def get_district(obj):
'''distid3無(wú)上下關(guān)聯(lián)所以重定義一個(gè)district字段根據(jù)需要的篩選條件(distid=obj.distid3)查詢出來(lái)的數(shù)據(jù)扔進(jìn)自定義的序列器中(DistrictSimpleSerializer)進(jìn)行序列化'''
district = District.objects.filter(distid=obj.distid3).only('name').first()
return DistrictSimpleSerializer(district).data
class Meta:
model = HouseInfo
exclude = ('detail','distid3','pubdate','hassubway','isshared','userid','distid2','agent')
=============================自定義查詢方法============================
# 引入搜索引擎 Whoosh +jieba(小型) -----> 倒排索引
# 中科院研發(fā)的搜索引擎對(duì)中文適配度很高ElasticSearch / Solr + 中文分詞插件ik-analysis/smartcn/pinyin支持?jǐn)?shù)據(jù)量大书斜,并發(fā)高的查詢支持
class HouseInfoFilterSet(django_filters.FilterSet):
"""自定義房源信息模糊查詢"""
# title字段模糊查詢,contacts/startwith/endswith性能很差,建議搞個(gè)搜索引擎
title = django_filters.CharFilter(lookup_expr='contains')
minprice = django_filters.NumberFilter(field_name='price')
maxprice = django_filters.NumberFilter(field_name='price')
#自定義關(guān)鍵字查詢方法
keyword = django_filters.CharFilter(method='filter_by_keyword')
# 自定義查詢方法
@staticmethod #若不是靜態(tài)方法诬辈,第一個(gè)參數(shù)是self
def filter_by_keyword(queryset, key, value):
queryset = queryset.filter(Q(title__contains=value) |
Q(detail__startswith=value))
return queryset
[站外圖片上傳中...(image-431ce9-1579510832645)]
補(bǔ)充5
導(dǎo)出excel工作簿(原生SQL查詢)
pip install xlwt
前端頁(yè)面中給一個(gè)連接并創(chuàng)建映射。例如:
<a href="/excel/">導(dǎo)出經(jīng)理人報(bào)表</a>
urls.py
urlpatterns = [
path('excel/', export_excel),
]
創(chuàng)建工作表視圖函數(shù)
def export_excel(request):
#使用游標(biāo)對(duì)象
with connection.cursor() as cursor:
cursor.execute('select t1.agentid, name, tel, total '
' from tb_agent t1 inner join '
' (select agentid, count(agentid) as total '
' from tb_agent_estate group by agentid) t2 '
' on t1.agentid=t2.agentid')
agents = cursor.fetchall()
workbook = xlwt.Workbook()
#表名稱
sheet = workbook.add_sheet('經(jīng)理人統(tǒng)計(jì)表')
#表格字段
titles = ('編號(hào)', '姓名', '聯(lián)系電話', '樓盤數(shù)量')
for col, title in enumerate(titles):
sheet.write(0, col, title)
for row, agent in enumerate(agents):
for col in range(4):
sheet.write(row + 1, col, agent[col])
# str(只讀) --->StringIO(可寫)
# bytes ---->BytesIO
buffer = BytesIO()
# buffer緩沖區(qū),存儲(chǔ)成二進(jìn)制文件
workbook.save(buffer)
resp = HttpResponse(buffer.getvalue(),
content_type='application/vnd.ms-excel')
# resp['content_type'] = 'application/vnd.ms-excel'
resp['content-disposition'] = 'attachment荐吉;filename="data.xls"' # inline直接打開(kāi)文件焙糟,attachment下載文件
return resp
補(bǔ)充6
軟件開(kāi)發(fā)過(guò)程模型
~ 傳統(tǒng)過(guò)程模型:大型、超大型項(xiàng)目
- 瀑布模型(經(jīng)典模型):
- 可行性分析(研究做還是不做)---> 可行性分析報(bào)告
- 需求分析(研究做什么)--->
頭腦風(fēng)暴 ---> 思維導(dǎo)圖 ---> 需求規(guī)格說(shuō)明書
產(chǎn)品原型圖 ---> Axure RP ---> 線框圖/高保真原型 - 概要設(shè)計(jì)和詳細(xì)設(shè)計(jì)
數(shù)據(jù)庫(kù)設(shè)計(jì) ---> E-R圖 ---> 物理模型圖 <--- PowerDesigner
面向?qū)ο笤O(shè)計(jì)(OOAD)---> UML ---> 類圖 <--- StarUML / Enterprise Architect
~ is-a:繼承
~ has-a:關(guān)聯(lián)样屠、聚合穿撮、合成
~ use-a:依賴 - 編碼
- 測(cè)試(單元測(cè)試 ---> 系統(tǒng)測(cè)試 ---> 集成測(cè)試 ---> 驗(yàn)收測(cè)試)
- 交付(上線)+ 運(yùn)維
~ 敏捷模型:迅速推出產(chǎn)品占領(lǐng)市場(chǎng)
核心理念:增量迭代式開(kāi)發(fā)
- SCRUM(將開(kāi)發(fā)過(guò)程分為若干個(gè)沖刺周期)
- 建立或更新需求池
- 計(jì)劃會(huì)議(評(píng)估工作量、制定計(jì)劃)
- 日常開(kāi)發(fā)(站立會(huì)議痪欲、番茄工作法)
- 版本發(fā)布
- 評(píng)審會(huì)議(Show case)
- 回顧會(huì)議(總結(jié)得失)
~敏捷閉環(huán)工具 - JIRA/禪道
~團(tuán)隊(duì)開(kāi)發(fā)工具 - 釘釘/ TeamBition
- 建立或更新需求池
~ 版本控制 - 團(tuán)隊(duì)開(kāi)發(fā)模式下如何使用Git
~ Git私服 ---> GitLab
~ Git標(biāo)準(zhǔn)工作流程:
- git-flow
- github-flow(PR流程)
- 克隆或者更新項(xiàng)目
git clone URL
git pull - 基于master分支創(chuàng)建并切換到自己的分支
git branch NAME
git checkout NAME / git switch NAME
git checkout -b NAME / git switch -C NAME - 在自己的分支上做開(kāi)發(fā)并實(shí)施版本控制
- git add FILE
- git commit -m message
- 把自己的分支推到服務(wù)器上
git push -u origin NAME - 在線發(fā)起合并請(qǐng)求(線上操作)悦穿,請(qǐng)求將工作成果合并到master分支
- Pull Request / Merge Request
- 克隆或者更新項(xiàng)目
- gitlab-flow
- github-flow(PR流程)