需求:
设计一个登陆接口,返回一个json格式响应。
分析:
在视图层完成登陆请求的用户名和密码的数据库数据校验,然后返回对应的json格式结果。
结果可能性:
- 第一:用户名重名-bug,需返回一个状态,数据库中存在两个重名的用户
- 第二:用户名不存在,在数据库中查询不到
- 第三:密码错误
- 第四:都正确
定义json的格式:
- "reason": "数据库中存在同名的用户","result": [],"error_code": 2000
- "reason": "用户名不存在","result": [],"error_code": 2001
- "reason": "密码错误","result": [],"error_code": 2002
- "reason": "数据库服务错误","result": [],"error_code": 2003
{
"reason": "登陆成功",
"result": {
username:请求的用户名
password:登陆成功
},
"error_code": 0 / * 登陆成功* /
}
第一步:数据库创建和连接
以sqlite为例:https://blog.51cto.com/blogger/publish?old=1
创建以下数据库:库名my.db,建表userinfo,一条记录用户名:Test1234,密码:123456
(base) PS E:\learn\djangotest> sqlite3
SQLite version 3.36.0 2021-06-18 18:36:39
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .open my.db
sqlite> .tables
sqlite> create table userinfo(username varchar(10),password varchar(10));
sqlite> .tables
userinfo
sqlite> insert into userinfo values("Test1234","123456")
...> ;
sqlite> select * from userinfo;
Test1234|123456
sqlite>
在模型层中创建一个模块连接sqlite数据库,并完成数据的读取操作:
# Conn_Sqlite.py
#该文件完成sqlite数据库中的数据读取操作,任何一个数据库操作其主要都是基于链接对象的创建
import sqlite3
from djangotest.settings import BASE_DIR
class ConnSqlite(object):
def __init__(self):
print("基本地址", BASE_DIR) # 基本地址 E:\learn\C10\InterfaceProgram
self.conn = sqlite3.connect(BASE_DIR + "/Login/Model/my.db")
self.cursor = self.conn.cursor()
# 读取数据
def read_data(self, str_sql):
self.cursor.execute(str_sql)
get_result = self.cursor.fetchall()
return get_result
if __name__ == '__main__':
print(BASE_DIR + "/Login/Model/my.db")
conn = ConnSqlite()
str_sql = "select password from userinfo where username='%s'" % 'Test1234'
print(conn.read_data(str_sql))
第二步:获取请求中的用户名和密码
#Login_Check_Action.py
# 从页面上获取数据,并封装在一个方法中
def get_userinfo(self,method_obj):
#与前端页面进行交互的,同样可以设定默认值,如果前端没有对应的属性时,则取默认值,如果有值则取前端所获取到属性所对应的值
get_username=method_obj.get("username","wangwu") #如果携带的参数不是username的话,则默认值是wangwu
get_password=method_obj.get('password')
return {"password":get_password,"username":get_username}
第三步:完成从请求数据与数据库中数据进行对比:
##Login_Check_Action.py
# 获取数据库连接对象
def get_database(self, username):
conn = ConnSqlite()
str_sql = "select password from userinfo where username='%s'" % username
return conn.read_data(str_sql)
#实现比较,并设定返回指定内容的格式数据。
def compare_data(self, method_obj):
get_page_info = self.get_userinfo(method_obj)
get_database_info = self.get_database(get_page_info["username"])
print("获取的页面信息:", get_page_info)
return_result = {"reason": [], "result": [], "error_code": []}
if len(get_database_info) > 1:
return_result["reason"] = "数据库中存在同名的用户"
return_result["error_code"] = 2000 # return {"reason": "数据库中存在同名的用户","result": get_page_info,"error_code": 2000}
elif len(get_database_info) == 0:
return_result["reason"] = "用户名不存在"
return_result["error_code"] = 2001 # return {"reason":"用户名不存在","result": get_page_info,"error_code": 2001}
elif get_database_info[0][0] != get_page_info["password"]:
return_result["reason"] = "密码错误"
return_result["error_code"] = 2002 # return {"reason": "密码错误","result": get_page_info,"error_code": 2002}
else:
return_result["reason"] = "登陆成功"
return_result["error_code"] = 0
return_result["result"] = get_page_info # return { "reason": "登陆成功","result":get_page_info,"error_code": 0 }
return return_result
第四步:通过指定的请求方法将结果响应给客户端,
例如,声明get和post方法
#Login_Check_Action.py
def get(self,request):
#return HttpResponse(self.get_userinfo(request.GET))
return HttpResponse(json.dumps(self.compare_data(request.GET),ensure_ascii=False))
def post(self,request):
return HttpResponse(json.dumps(self.compare_data(request.POST),ensure_ascii=False))
第五步:完成urls路由映射操作
from django.contrib import admin
from django.urls import path
from Login.View.Login_Check_Action import LoginCheckAction
urlpatterns = [
path('admin/', admin.site.urls),
path("loginAction",LoginCheckAction.as_view()),
]
第1-5步总结:Login_Check_Action.py
那么以上所有的代码就构成了视图层的整个业务逻辑处理实现,将上面的所有方法声明在一个类中即可
# 该模块完成数据库的校验,从页面上传入的登陆数据实现后台数据库中的数据进行比较校验,如果存在且一致则表示登陆成功,否则登陆失败
from django.views import View
from django.http import HttpResponse
from Login7.Model.Conn_Sqlite import ConnSqlite
import json
#from TestOrm.models import UserInfo
class LoginCheckAction(View):
# 根据页面传入的用户名与数据库中的数据进行比较密码、检测用户是否存在
# 第一步:先获取到请求中的用户名和密码数据
# 先获取到请求中的用户名和密码数据,从页面上获取数据,并封装在一个方法中
def get_userinfo(self, method_obj):
# 与前端页面进行交互的,同样可以设定默认值,如果前端没有对应的属性时,则取默认值,如果有值则取前端所获取到属性所对应的值
get_username = method_obj.get("username", "wangwu")
get_password = method_obj.get('password')
return {"password": get_password, "username": get_username}
# 第三步:(1)获取数据库中数据,此处以django自带的sqlite的数据库为实例:,为了获取数据库连接对象
def get_database(self, username):
conn = ConnSqlite()
str_sql = "select password from userinfo where username='%s'" % username
return conn.read_data(
str_sql) # 真正完成页面数据与数据库中数据进行比较的操作 # 分析:第一:用户名重名,这是个bug需要返回一个状态,数据库中存在两个重名的用户 # 第二:用户名不存在,在数据库中查询不到 # 第三:密码错误 # 第三:都正确 # 定义json的格式:"reason": "数据库中存在同名的用户","result": [],"error_code": 2000 # "reason": "用户名不存在","result": [],"error_code": 2001 # "reason": "密码错误","result": [],"error_code": 2002 # "reason": "数据库服务错误","result": [],"error_code": 2003 # { # "reason": "登陆成功", # "result": { # username: # password: # }, # "error_code": 0 / * 发送成功 * / # } # 第三步:(2)完成从请求中获取的数据与数据库中数据进行对比,
def compare_data(self, method_obj):
get_page_info = self.get_userinfo(method_obj)
get_database_info = self.get_database(get_page_info["username"])
print("获取的页面信息:", get_page_info)
return_result = {"reason": [], "result": [], "error_code": []}
if len(get_database_info) > 1:
return_result["reason"] = "数据库中存在同名的用户"
return_result[
"error_code"] = 2000 # return {"reason": "数据库中存在同名的用户","result": get_page_info,"error_code": 2000}
elif len(get_database_info) == 0:
return_result["reason"] = "用户名不存在"
return_result["error_code"] = 2001 # return {"reason":"用户名不存在","result": get_page_info,"error_code": 2001}
elif get_database_info[0][0] != get_page_info["password"]:
return_result["reason"] = "密码错误"
return_result["error_code"] = 2002 # return {"reason": "密码错误","result": get_page_info,"error_code": 2002}
else:
return_result["reason"] = "登陆成功"
return_result["error_code"] = 0
return_result["result"] = get_page_info # return { "reason": "登陆成功","result":get_page_info,"error_code": 0 }
return return_result
# 第四步:通过指定的请求方法将结果响应给客户端,例如,声明get和post方法
def get(self, request):
# return HttpResponse(self.get_userinfo(request.GET))
return HttpResponse(json.dumps(self.compare_data(request.GET), ensure_ascii=False))
def post(self, request):
return HttpResponse(json.dumps(self.compare_data(request.POST), ensure_ascii=False))
解决错误1
执行报错: D:\Anaconda3\python.exe E:\learn\testProject\Login\Modle\Conn_Sqlite.py Traceback (most recent call last): File "E:\learn\testProject\Login\Modle\Conn_Sqlite.py", line 22, in print(BASE_DIR + "Login/Modle/my.db") TypeError: unsupported operand type(s) for +: 'WindowsPath' and 'str'
解决:修改settings.py
"Login",
#BASE_DIR = Path(__file__).resolve().parent.parent
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#"DIRS": [BASE_DIR / 'Login/Template'],
"DIRS":[os.path.join(BASE_DIR,'/Template')],
# "NAME": BASE_DIR / "db.sqlite3",
"NAME": os.path.join(BASE_DIR, "/my.db"),
解决错误2
执行报错:post请求,在请求过程中出现403forbidden的话,
解决:修改django中的设置,如下图:
因为CSRF 表示django全局发送post请求均需要字符串验证
功能:防止跨站请求伪造的功能
工作原理:客户端访问服务器端,在服务器端正常返回给客户端数据的时候,而外返回给客户端一段字符串,等到客户端下次访问服务器端时,服务器端会到客户端查找先前返回的字符串,如果找到则继续,找不到就拒绝。
如果就是不想注释的话,可以通过修改header,在views打印cookie可以得到csrftoken
def index(request):
print("cookie",request.COOKIES)
#cookie {
# 'csrftoken': 'AB9v1MGTbdpSGg3FaGCIiUxrKVR8zKSqgdGFDn5E0ADsJ2ST7N2zgW6KboQ8G31x',
# 'sessionid': 'eexw5p38vky9qo38nf372dz5lj1br6xf'
# }
#cookie 是浏览器给的,
return HttpResponse("index")