网站建设导入视频,修改wordpress rss,小版本wordpress,网站建设需要提供那些资料文章目录 前后端分离开发展示项目项补充知识开发问题浏览器解决跨域问题 drf 小tips设置资源root目录使用自定义的user表设置资源路径media数据库补充删除表中数据单页面与多页面模式过滤多层自关联后端提交的数据到底是什么jwt token登录设置普通的 token 原理使用流程解析 jw… 文章目录 前后端分离开发展示项目项补充知识开发问题浏览器解决跨域问题 drf 小tips设置资源root目录使用自定义的user表设置资源路径media数据库补充删除表中数据单页面与多页面模式过滤多层自关联后端提交的数据到底是什么jwt token登录设置普通的 token 原理使用流程解析 jwt token原理使用流程 与 原理剖析 用户手机注册 前后端分离开发展示
实际上我们在真正的生产环境之下采用的都是后端开发后端前端开发前端的后端只负责向对应的url传递数据前端设置在访问对应的网页的时候如何去提取对应的数据这边仅以后端程序员的角度进行分析。
开发阶段的基本流程
先在服务器上设置远程连接 然后之后你在编译器上进行每天写代码后就可以维护版本或者说使用服务器的编译器进行构造多人效率大大提升。 然后就是关于后端项目的启动 启动之后到你的服务器的对应端口进行浏览器查看是否后端已经部署完成 出现了对应提供数据的端口证明后端启动成功。
然后下面就是前端的开发这边由于本系列前面没有介绍过Vue这边就不进行详细介绍这边就告诉个大概前端此时就有选择了可以选择在服务器的另一个文件夹下开发也可以选择本地开发这边按照本地进行介绍当然这并不意味着前端之后不部署到服务器上这边由于大家不懂就不详细介绍大概介绍个流程让大家理解前后端分离。
对于前端来说我们来到axios的路径配置的那个文件夹我们先了解一下前端所做的事情就是合理组织我们传递到每个url的数据然后组合成一个页面所以不论部署到本地还是部署到服务器上我们需要的事情都是在每个页面都到对应的url提取数据由于我们的数据是部署到后端也就是前面的那些东西所以我们需要向 服务器的对应端口处进行请求数据。 然后就是启动前端项目和后端一样编辑配置 然后启动前端后端如果没有配置nginx记得后端也需要启动的哈OvO 至此我们大概对于前后端分离的架构思路有了一定的理解。
项目项补充知识
开发问题
浏览器解决跨域问题
谷歌设置为非安全模式
C:\Program Files (x86)\Google\Chrome\Application\chrome.exe --user-data-dirC:/MyChromeDevUserData --test-type --disable-web-security再创建一个新的快捷方式将他的后缀改成这个样子并且记得到C:/MyChromeDevUserData创建这个文件夹或者你也可以修改成别的文件夹名称。
drf 小tips
设置资源root目录
先想一下为什么需要设置资源文件位置原因就是我们往往项目设计的时候并不仅仅只有一个app以及一些额外的app但把这些app都放在项目的外面这明显不是一个好的方法我们更希望集合到一起。 这个时候我们就把项目的apps都放到了一个文件app包当中了第三方插件放到了extra_apps当中了但是此时我们仍然希望我们在使用import的时候不需要进行改变不需要改成from apps.goods import 啥啥啥这样子我们仍然希望可以直接from goods import 啥啥啥 这样子我们此时就需要设置资源路径
第一种方法就是在setting.py当中加入这种
import sys
sys.path.insert(0, os.path.join(BASE_DIR, apps))
sys.path.insert(0, os.path.join(BASE_DIR, extra_apps))第二种方式实际上和第一种是一样的只不过使用可视化替代了上述操作 然后编译器就会自动的去添加了。
使用自定义的user表
第一步就是新建一个user的app
然后在模型类当中去继承原本的user而不是原本继承的models.Model
class UserProfile(AbstractUser):用户name models.CharField(max_length30, nullTrue, blankTrue, verbose_name姓名)birthday models.DateField(nullTrue, blankTrue, verbose_name出生年月)gender models.CharField(max_length6, choices((male, u男), (female, 女)), defaultfemale, verbose_name性别)mobile models.CharField(nullTrue, blankTrue, max_length11, verbose_name电话)email models.EmailField(max_length100, nullTrue, blankTrue, verbose_name邮箱)class Meta:verbose_name 用户verbose_name_plural verbose_namedef __str__(self):return self.username接着去setting当中
AUTH_USER_MODEL users.UserProfile如此只是改变了寻找user表的路径然后讲一下在使用的时候该怎么进行使用。
from django.contrib.auth import get_user_modelUser get_user_model()User此时就是我们定义的那个对应的类了。设置资源路径media
到setting中设置
MEDIA_URL /media/
MEDIA_ROOT os.path.join(BASE_DIR, media)数据库补充删除表中数据
TRUNCATE 清空数据 还原主键自增的 ID 会重新从 1 开始 DELETE 删除数据 删除数据自增的 ID 会继续递增
如果整张的表的数据不想要了我们可以使用上面的那个删除的操作。
从逻辑上说TRUNCATE 语句与 DELETE 语句作用相同但是在某些情况下两者在使用上有所区别
DELETE 是 DML 类型的语句TRUNCATE 是 DDL 类型的语句。它们都用来清空表中的数据。DELETE 是逐行一条一条删除记录的TRUNCATE 则是直接删除原来的表再重新创建一个一模一样的新表而不是逐行删除表中的数据执行数据比 DELETE 快。
使用方式
TRUNCATE [TABLE] 表名单页面与多页面模式
虽然这个实际上和前端相关但是后端也了解一下吧。
链接
对于个人的理解的话先讲区别吧区别就是实际上你的生成的时候生成的是一个页面还是多个页面静态资源就是你在前端代码运行生成出来的文件时一个还是多个hh个人水平有限看上面的链接单页面也是大厂很喜欢的一种模式当然对于SEO很不友好但是谁管SEO呢hh。
过滤
之前的drf当中确实讲解过过滤但是没有讲解到自定义过滤器给出官网的快速入门进行理解。
链接
配置
# settings.py
INSTALLED_APPS [...rest_framework,django_filters,
]REST_FRAMEWORK {DEFAULT_FILTER_BACKENDS: (django_filters.rest_framework.DjangoFilterBackend,...),
}这边给出一部分简单的解析深层次的到我给的官方文档里面进行查看
首先先创建一个新的py文件对应应用中filters.py:
from django_filters import rest_framework as filtersclass GoodsFilter(filters.FilterSet):pricemin filters.NumberFilter(field_nameshop_price, lookup_exprgte)pricemax filters.NumberFilter(field_nameshop_price, lookup_exprlte)name filters.CharFilter(field_name name,lookup_expr icontains) #contains代表包含i代表不区分大小写
然后到view视图进行具体使用的时候:
class GoodsListViewSet(viewsets.ReadOnlyModelViewSet) :这么少的代码实现了 商品列表分页搜索过滤排序queryset Goods.objects.all()serializer_class GoodsSerializerpagination_class GoodsPaginationfilter_backends [ DjangoFilterBackend,filters.SearchFilter,filters.OrderingFilter]# filterset_fields [ name, shop_price ] #根据model中的名字进行设置filter_classGoodsFiltersearch_fields [name, goods_brief, goods_desc]ordering_fields [ sold_num, shop_price ]
根据上面我们可以发现我们自定义的filter_class实际上用到的就是DjangoFilterBackend内部的选项我们讲我们自定义的覆盖了其。
多层自关联
在子查询的时候我们普通查询出来的数据仅仅只是第一层的数据比如说parent_id null的但是我们可能想要展示的数据包含着第二层的数据乃至第三层的数据而且我们想要全部都进行展示按照一定的json格式进行展示当然可以使用嵌套类进行完成但鉴于这种子查询的层数一般不会太多这边采用一种比较方便的做法例子
先展示模型类只展示重点代码以及具体处理的方式
class GoodsCategory(models.Model):商品类别parent_category models.ForeignKey(self, nullTrue, blankTrue, verbose_name父类目级别, help_text父目录,related_namesub_cat,on_deletemodels.CASCADE)其他略重点在意那两列即可 然后就是具体的展示子查询的方式修改serializer的序列化功能即可因为我们发现我们的级别最多只有三层那么这边进行调用三次即可值得关注的是sub_cat这个名字不是乱取的这个名字是自关联的model当中定义的。
class CategorySerializer3(serializers.ModelSerializer):class Meta:model GoodsCategoryfields __all__class CategorySerializer2(serializers.ModelSerializer):sub_cat CategorySerializer3(manyTrue)class Meta:model GoodsCategoryfields __all__class CategorySerializer(serializers.ModelSerializer):sub_cat CategorySerializer2(manyTrue)class Meta:model GoodsCategoryfields __all__这样子执行就有了相关的三层数据这种做法好好理解一下这边的all很有学问。
后端提交的数据到底是什么
学了很久前后端但是始终没有明说前后端分离下后端每一个url所提交的数据到底是什么这边说一下因为博主在开发的时候发现前端的想要的数据和后端提供的数据发生了不一样的地方这边就提一嘴并且给出博主错误的原因 但这些都只是纸面上谈谈具体的格式协商需要和前端程序员进行协商一般都不会采用全局设置因为全局设置之后后面想要再个性化设置不论是数据发生了改变代码层面也不适合阅读调试。
jwt token登录设置
首先引入一下为什么需要使用jwt token进行登录我们之前写的那个session登录不是不是很完美吗上一章也介绍过了是很完美但是在面对分布式的情况下session只存储在其中一台服务器上当请求发送到另一台服务器那么这就造成了登录不上的问题了所以这个时候就需要使用token进行登录使用token解码来进行实现即使发送的请求到不同的服务器上也能通过这个token来进行分辨出你到底登录没有以及登录的信息这个详细理论可见之前的网络通信那一边有进行介绍-----不过当时实现的是虚假的token。
这边稍微提一嘴session遵循base64解码后面不说了OxO。
相同的jwt又是什么呢和普通的jwt登录又有什么区别这个不急先理解普通的token
普通的 token 原理
普通的token实际上就是一个随机字符串他和Session最大的区别就是Session做的事情太多了每次提交一次网页都需要进行对于session-id然后提取出数据然后base64解码这边就很有问题token简化了这一个过程在你成功登录之后就会以cookie的方式给你派发一个随机字符串token然后把这个token存储到数据库当中而对应的这张表当中的数据的第一列token码第二列:你的user_id这就极大的方便了流程其实好像还好但是确实可以减少步骤。
而使用token进行登录之前也稍微提了一嘴这边会稍微再提一点。
具体使用 首先到setting当中加上’rest_framework.authentication.TokenAuthentication’
REST_FRAMEWORK {DEFAULT_AUTHENTICATION_CLASSES: [rest_framework.authentication.BasicAuthentication,rest_framework.authentication.SessionAuthentication,rest_framework.authentication.TokenAuthentication,
]}实际上是最后一个起效果。
然后app当中加入对应的app
INSTALLED_APPS [
...
rest_framework.authtoken]这时我们执行 migrations 和 migrate 就会产生一张表token的上面讲述原理的那张表
接着就是到对应的urls当中加入
from rest_framework.authtoken import viewsurlpatterns [re_path(r^api-token-auth/, views.obtain_auth_token)
]#向这个 url post 我们的用户名和密码就会返回我们的 token然后实际上后面进行使用的话就是控制登录之后访问这个url返回对应的token给前端。
使用流程解析
经过上面的步骤之后配置好了url此时我们具体的使用流程就是 向 http://·········/api-token-auth/提交用户名和密码就会得到一个token。 数据的格式 然后是记得把Headers加上这个字段Content-Type: application/json 然后就会返回一个token3a5bfe2e6daaf83c483510b50461a1808cb39dcc 此时服务器已经做到了将这个token存储到你的数据库当中并且也存入了你的对应的use_id. 最后自然就是关于如何去使用随便查询一个页面并在header当中加上Authorization值为Token xxxxxxxxxx如此之后就可以进行识别了。 jwt token原理
使用了token之后我们还是觉得不太好上面的分布式的问题还是没有进行解决如果每一次登录我们都需要到分布式集群当中去一个个查找这样子效率就实在太低了所以自然诞生了jwt token说人话其实他就是不去查询数据库的做法他在你第一次登录的情况下已经做到了将你的部分不重要的注意不重要因为编码可破译base64但是可以标明你是谁的信息放到了这个token当中然后我们在访问服务器的时候就根据破译这个token就可以获取你登录的信息到底是什么了。
上面是部分比较碎嘴化的东西主要讲解的就是为什么会有这种东西后面讲解jwt token的格式。
参考链接
首先先明白jwt token其实分为三个部分 头部 负载 签名并且jwt其实存在一个私钥私钥的作用就是唯一标识这个服务器会隔一定的时间就进行修改。我们先生成一个jwt token再进行原理剖析。
然后就是关于对称加密非对称加密散列加密可见我的另一篇博客杂谈处。
使用流程 与 原理剖析
官网链接
下载
pip install djangorestframework-jwt
使用
REST_FRAMEWORK {DEFAULT_PERMISSION_CLASSES: (rest_framework.permissions.IsAuthenticated,),DEFAULT_AUTHENTICATION_CLASSES: (rest_framework_jwt.authentication.JSONWebTokenAuthentication,rest_framework.authentication.SessionAuthentication,rest_framework.authentication.BasicAuthentication,),
}# 以及设置过期时间
JWT_AUTH {JWT_EXPIRATION_DELTA: datetime.timedelta(days1),
}在 urls.py 添加以下 URL 路由以启用通过 POST 获取令牌时包含用户的用户名和密码。
from rest_framework_jwt.views import obtain_jwt_token
#...urlpatterns [,# ...url(r^jwt-auth/, obtain_jwt_token),
]向http://远程服务器:8000/jwt-auth/ 发送请求 然后就会接收到一个jwt-token
{token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ5MjE2ODkwLCJlbWFpbCI6IjEyM0BxcS5jb20ifQ.guIU-TJ6ECjNKUKxer6wki9mvj9Gb-duManEFwyKNbw
}
我们可以看出他明显分成三个部分 什么什么.什么什么.什么什么
其实就是一对一相对应的
第一部分header
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9我们拿base64解析一下 其实就告诉了我们他的type是jwt 他的加密算法是HS256
然后第二部分
eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNjQ5MjE2ODkwLCJlbWFpbCI6IjEyM0BxcS5jb20ifQ继续拿base64解析一下 此时就可以发现你个人的部分信息是存储到这里面了。
第三部分就没办法解析了第三部分就是拿前两部分之和加上服务器的一把私钥合起来一起加密的使用哪个HS256加密算法进行的加密这边演示不出来。
详细可见上面给的博客那边讲的很详细也很好。 然后接着讲使用
使用的化就是头部需要加上
Authorization:JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNTczMDQyNjAyLCJlbWFpbCI6ImRheHVlbHVAZm94bWFpbC5jb20ifQ.KReyfJjrARcHVw6bNc3FghnGLXAlhmuykszc63SoqTE然后进行模拟发包如此之后就可以做到大厂现在最常使用的jwt登录OvO。
然后就是关于登录的理解
由于前后端分离的性质这边给出vue的前端代码
login({username:this.userName, //当前页码password:this.parseWord
}).then((response) {console.log(response);//本地存储用户信息cookie.setCookie(name,this.userName,7);cookie.setCookie(token,response.data.token,7) //用来存储 token//存储在 store// 更新 store 数据that.$store.dispatch(setInfo);//跳转到首页页面this.$router.push({ name: index})
})整个流程我们可以看到实际上我们只需要把上面的jwt的url修改成login即可进行访问了整个登录的流程就是首先我们先打开一个点击网页当中的登录界面当然这个页面就是前端进行构建的 然后我们填写账号密码之后点击登录看那个前端代码我们会向后端提交账户名和密码然后后端会进行验证至于登录的验证这边由于我们使用的是jwt的view所以不需要我们写是否从数据库当中得到账号密码一致这样子我们除非我们需要重写一部分登录的东西就需要重写后文会进行介绍。
当我们后端判断无误后我们会返回一个jwt token给前端然后前端将之存储到token并且跳转到首页完成这一系列的功能然后之后的访问实际上浏览器就是通过这个token进行身份识别并且操作的。
下面进行介绍如何重写相关的检测函数首先就是由于user的检测是由框架自己去实现的所以这边的检测就需要我们去重写相关的函数以及将执行的那个函数指向到我们的函数
第一步自然是去setting当中去修改
AUTHENTICATION_BACKENDS (users.views.CustomBackend,
)这个的意思就是把这个身份验证的路径转到我们的users.view.CustomBackend当中也即到我们自己写的类当中去执行相对应的检测。
第二步也自然是去对应的view当中进行修改
from django.contrib.auth import get_user_model
from django.db.models import Q
User get_user_model()class CustomBackend(ModelBackend):自定义用户验证def authenticate(self, request,usernameNone, passwordNone, **kwargs):try:user User.objects.get(Q(usernameusername)|Q(mobileusername))if user.check_password(password):return userexcept Exception as e:return None也就是这边进行检测的我们修改了这个检测的逻辑从而让我们的项目更加完整。
用户手机注册
这边使用的是yunpian的验证码发送。
测试的代码
import json
import requestsclass YunPian(object):def __init__(self, api_key):self.api_key api_keyself.single_send_url https://sms.yunpian.com/v2/sms/single_send.jsondef send_sms(self, code, mobile):parmas {apikey: self.api_key,mobile: mobile,text: 【啥啥啥公司】您的验证码是{code}。如非本人操作请忽略本短信.format(codecode)}response requests.post(self.single_send_url, dataparmas)re_dict json.loads(response.text)return re_dictif __name__ __main__:yun_pian YunPian(阿巴阿巴)yun_pian.send_sms(8888, 110)这边给出发送手机短信的功能根据这个来即可。