1.什么是前后端分离开发:
就是前后端工程师约定好数据交互接口,并行的进行开发和测试,后端只提供数据,不负责将数据渲染到页面上,前端通过HTTP请求获取数据并负责将数据渲染到页面上,这个工作是交给浏览器中的JavaScript代码来完成。
2.前后端开发的好处:
- 1.提升开发效率
- 2.增强代码的可维护性。
- 支持多终端和服务化架构。
3.数据接口(FBV - 基于函数的视图):
FBV - 基于函数的视图
CBV - 基于类的视图
前后端分离的开发模式下,后端需要为前端提供数据接口,这些接口通常返回JSON格式的数据。在Django项目中,我们可以先将对象处理成字典,然后就可以利用Django封装的JsonResponse向浏览器返回JSON格式的数据,例如以下例子
def show_subjects(request):
queryset = Subject.objects.all() #获取所有学科对象
subjects = [] #定义一个空字典
for subject in queryset: #遍历查询学科的到的对象,并把数据处理一个字典保存在subjects列表容器中
subjects.append({
'no': subject.no,
'name': subject.name,
'intro': subject.intro,
'isHot': subject.is_hot
})
return JsonResponse(subjects, safe=False) #利用JsonResponse完成对列表的序列化,返回json格式的数据。由于序列化的是一个列表而不是字典,所以需要指定safe参数的值为False
值得注意的是,这样的处理虽然没问题,但是如果对象的属性很多时候,情况就比较糟糕了,为此我们可以使用一个比较小众的第三库bpmappers来简化将对象转成字典的操作
4.使用bpmappers
4.1 安装第三方库bpmappers
pip install bpmappers
4.2 创建一个mappers.py文件编写映射器,实现对象到字典的转换。
from bpmappers.djangomodel import ModelMapper
from poll2.models import Subject
class SubjectMapper(ModelMapper):
class Meta:
model = Subject#相关联的类
fields = ('carno', 'owner') #包含的字段 exclude()排除的字段
4.3 打开view.py修改视图函数
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映射,然后访问该接口,可以得到如下所示的JSON格式数据。
前端部分代码如下所示:
5.网络API接口设计和配置
网络API - 通过HTTP请求一个URL获得(JSON)数据
4.5安装三方库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',
]
#配置一个数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'apidemo',
'HOST': '47.107.87.249',
'PORT': 3306,
'USER': 'renwoxing', #需要授权操作
'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创建应用,执行以下代码:
python manage.py startapp common
4.7.2打开应用目录下的model.py 在终端执行以下代码(前提是数据库已经配好且有操作权限):
python manage.py inspectdb > common/models.py
查看model.py可以看到已经迁移成功,接下来需要根据需要对生成的模型进行微调
5. REST架构
REST架构简单介绍
REpresentational State Transfer ---> 表述性状态转移
REST架构两大特点: 无状态和幂等性
HTTP协议请求行 GET / POST / DELETE / PUT / PATCH
新建 ---> POST-不需要幂等性
查看 ---> GET
更新 ---> PUT/PATCH
删除 ---> DELETE
5.1 使用drf给的装饰器修改视图函数,例如
from rest_framework.decorators import api_view
@api_view(('GET', )) #限制视图函数支持哪些请求方法
def get_provinces(request):
"""获取省级行政区域"""
queryset = District.objects\
#不能直接写pid==null
.filter(pid__isnull=True).only('distid', 'name')
#序列化对象,是一个集合需要写上many=True
serializer = DistrictSimpleSerializer(queryset, many=True)
return Response(serializer.data)#框架给的response
5.3 利用drf框架里的ModelSerializer自定义序列化器,新建一个serializer.py文件,编写如下代码
class DistrictSimpleSerializer(serializers.ModelSerializer):
"""行政区域简单序列化器"""
class Meta:
model = District
fields = ('distid', 'name')
5.4 创建映射
5.4.1 在自己的应用底下建立一个urls.py,便于管理自己的各种接口
from common.models import District
urlpatterns = [
path('districts/', get_provinces),
5.4.2 修改全局映射URL
urlpatterns = [
path('', index),
#相当于前缀
path('api/', include('common.urls')),
path('admin/', admin.site.urls),
]
此时就可以访问自己创建的接口看到数据了。
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请求)"""
class Meta:
model = Estate
exclude = ('district', 'agents')
6.3映射urls
urlpatterns = [
path('estates/', EstateView.as_view()),
path('estates/<int:pk>/', EstateView.as_view()),可以拿单个楼盘
]
在浏览器中输入127.0.0.1:8000/api/estates/便可以看到数据。
7.全套接口视图集,两行代码写接口(不推荐使用)
📌打开 view.py 定义视图函数:
from rest_framework.viewsets import ModelViewSet
class HouseTypeViewSet(ModelViewSet):
queryset = HouseType.objects.all()
serializer_class = HouseTypeSerializer
📌打开 urls.py创建映射 :
urlpatterns = [
...
]
# 路由器
router = SimpleRouter() # 简单路由
router.register('housetypes', HouseTypeViewSet) # 注册
urlpatterns += router.urls # 添加
补充1
若需要拿到单个数据则可以用重写get方法。
打开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)
补充2
利用DRF进行 分页
1. PageNumberPagination ---> 按页码分页
2. LimitoffsetPagination ---> 跳过N条,查第N+1条
3. CursorPagination --->游标进行分页
- 自定义分页(商业项目中不推荐使用,可能会暴露服务器规模)
中间键后面添加以下代码(全部实现分页)
# DRF配置文件
REST_FRAMEWORK = {
# 按页码分页
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 5,
}
# 若单个视图类不想分页可以加以下代码:
pagination_class = None
2)游标分页(不能使用缓存,应无法准确获取上下文信息)
# 定义游标分页类
class EstatePagination(CursorPagination):
page_size_query_param = 'size' # 自定义单页显示记录数
max_page_size = 20 # 单页最多记录数
ordering = 'estateid' # 按楼盘id分页
# 在视图类里面调用
pagination_class = EstatePagination
补充3
利用DRF配置缓存
添加缓存也有两种方式(继承父类CacheResponseMixin,加类装饰器)
准备:
- 依赖项drf-extensions==0.5.0 为DRF提供缓存扩展
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可以将装饰函数的装饰器变成可以装饰类方法的装饰器
# 让视图类继承混入类:
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
# 拒绝分页
pagination_class = None
补充4
接口限流: 限制接口的访问频率
原理: 在缓存(Redis)内记录IP地址,并记录访问次数,当请求超过阈值则限制访问
配置文件,打开settings.py
REST_FRAMEWORK = {
# 限流配置
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '5/min', #定义限流频率
},
}
若不想限流,可以在视图类里面加入以下代码:
throttle_classes = ()
补充5
1.数据接口筛选(自定义序列化器和自定义模糊查询)
例如:
- view.py
class HouseInfoView(ListCreateAPIView, RetrieveUpdateDestroyAPIView):
'''房源展示图集'''
queryset = HouseInfo.objects.all().select_related('type', 'estate')
#导入自定义的序列化器
serializer_class = HouseInfoSerializer
==筛选部分==
# 继承父类支持筛选(DjangoFilterBackend),排序(OrderingFilter)
filter_backends = (DjangoFilterBackend, OrderingFilter)
# 自定义方法查询
filter_class = HouseInfoFilterSet
# 按'area', 'price'排序
ordering_fields = ('area', 'price')
=========================自定义序列化器====================================
- serializers.py
class HouseInfoSerializer(serializers.ModelSerializer):
'''房源序列化器'''
# 都是关联键,需要二次处理的字段,自定义序列化的方法(SerializerMethodField)
type = serializers.SerializerMethodField()
estate = serializers.SerializerMethodField()
tags = serializers.SerializerMethodField()
district = serializers.SerializerMethodField()
@staticmethod
def get_type(obj): #obj是上述序列化对象
# return obj.type.name #显示type中的name名称,是一个字段
return obj.type.name
@staticmethod
def get_estate(obj):
# return obj.estate.name #显示estate中的name名称,得到是一个字段,例如“楼盘名称”
'''重写一个序列器,将字段("楼盘")可拓展查询为字典,字典中的字段由新定义的序列器(EstateSimpleSerializer)决定'''
return EstateSimpleSerializer(obj.estate).data
@staticmethod
def get_tags(obj):
'''tag是多对多的关系,因此返回是一个集合,可以用生成器的方式将对象一个个取出来'''
return [tag.content for tag in obj.tags.all()]
@staticmethod
def get_district(obj):
'''distid3无上下关联所以重定义一个district字段根据需要的筛选条件(distid=obj.distid3)查询出来的数据扔进自定义的序列器中(DistrictSimpleSerializer)进行序列化'''
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(小型) -----> 倒排索引
# 中科院研发的搜索引擎对中文适配度很高ElasticSearch / Solr + 中文分词插件ik-analysis/smartcn/pinyin支持数据量大,并发高的查询支持
class HouseInfoFilterSet(django_filters.FilterSet):
"""自定义房源信息模糊查询"""
# title字段模糊查询,contacts/startwith/endswith性能很差,建议搞个搜索引擎
title = django_filters.CharFilter(lookup_expr='contains')
minprice = django_filters.NumberFilter(field_name='price')
maxprice = django_filters.NumberFilter(field_name='price')
#自定义关键字查询方法
keyword = django_filters.CharFilter(method='filter_by_keyword')
# 自定义查询方法
@staticmethod #若不是静态方法,第一个参数是self
def filter_by_keyword(queryset, key, value):
queryset = queryset.filter(Q(title__contains=value) |
Q(detail__startswith=value))
return queryset
[站外图片上传中...(image-431ce9-1579510832645)]
补充5
导出excel工作簿(原生SQL查询)
pip install xlwt
前端页面中给一个连接并创建映射。例如:
<a href="/excel/">导出经理人报表</a>
urls.py
urlpatterns = [
path('excel/', export_excel),
]
创建工作表视图函数
def export_excel(request):
#使用游标对象
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('经理人统计表')
#表格字段
titles = ('编号', '姓名', '联系电话', '楼盘数量')
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缓冲区,存储成二进制文件
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直接打开文件,attachment下载文件
return resp
补充6
软件开发过程模型
~ 传统过程模型:大型、超大型项目
- 瀑布模型(经典模型):
- 可行性分析(研究做还是不做)---> 可行性分析报告
- 需求分析(研究做什么)--->
头脑风暴 ---> 思维导图 ---> 需求规格说明书
产品原型图 ---> Axure RP ---> 线框图/高保真原型 - 概要设计和详细设计
数据库设计 ---> E-R图 ---> 物理模型图 <--- PowerDesigner
面向对象设计(OOAD)---> UML ---> 类图 <--- StarUML / Enterprise Architect
~ is-a:继承
~ has-a:关联、聚合、合成
~ use-a:依赖 - 编码
- 测试(单元测试 ---> 系统测试 ---> 集成测试 ---> 验收测试)
- 交付(上线)+ 运维
~ 敏捷模型:迅速推出产品占领市场
核心理念:增量迭代式开发
- SCRUM(将开发过程分为若干个冲刺周期)
- 建立或更新需求池
- 计划会议(评估工作量、制定计划)
- 日常开发(站立会议、番茄工作法)
- 版本发布
- 评审会议(Show case)
- 回顾会议(总结得失)
~敏捷闭环工具 - JIRA/禅道
~团队开发工具 - 钉钉/ TeamBition
- 建立或更新需求池
~ 版本控制 - 团队开发模式下如何使用Git
~ Git私服 ---> GitLab
~ Git标准工作流程:
- git-flow
- github-flow(PR流程)
- 克隆或者更新项目
git clone URL
git pull - 基于master分支创建并切换到自己的分支
git branch NAME
git checkout NAME / git switch NAME
git checkout -b NAME / git switch -C NAME - 在自己的分支上做开发并实施版本控制
- git add FILE
- git commit -m message
- 把自己的分支推到服务器上
git push -u origin NAME - 在线发起合并请求(线上操作),请求将工作成果合并到master分支
- Pull Request / Merge Request
- 克隆或者更新项目
- gitlab-flow
- github-flow(PR流程)