Django基础篇:虚拟环境、路由层版本区别、视图函数返回值、JsonResponse对象、from表单、request其他方法、FBV与CBV、模板语法
2022.5.13 diango虚拟环境、路由层版本区别、视图层基础操作
- 虚拟环境
- django路由层版本区别
- 视图函数返回值
- JsonResponse对象
- django接收form表单上传文件
- request其他方法
- FBV与CBV
- CBV源码剖析
- 模板语法传值
一、虚拟环境
在实际开发工作中,针对不同的项目需要为其配备对应的解释器环境>>>:
eg:
项目1
django2.2 pymysql3.3 requests1.1
项目2
django1.1
项目3
flask
问:如何让诸多项目使用不同环境并且无障碍地打开运行?
方式1:下载所有模块,如果有相同模块不同版本每次都重新下载替换 >>> 不合理
方式2:提前准备好多个解释器环境,针对不同的项目切换即可 >>> 虚拟环境
# 创建虚拟环境
相当于在下载一个全新的解释器
new project
pure python
new environment using
选择解释器版本
不勾选 Inherit global site-packages(继承本地解释器环境)
勾选 Make avaiable to all projects(让所有项目使用该环境)
create
# 识别虚拟环境
文件目录中有一个venv文件夹
# 如何切换环境
选择不同的解释器即可,全文不要再次勾选new environment using
二、django路由层版本区别
# django1.x 与 2.x、3.x有些许区别
1.路由匹配方法不一样
1.x >>> url() 支持正则
2.x/3.x >>> path() 不支持正则
如果想使用正则,也提供了方法:
from dnango.urls import path,re_path
re_path() # 方法与url完全一致
2.path方法提供了转换器功能
path('index/<int:id>/', index)
<> # 匹配对应位置的数据并自动转换类型,共五种转换类型
# 五种转换类型
str:匹配任何非空字符串,但不含斜杠/,如果你没有专门指定转换器,那么这个是默认使用的;
int:匹配0和正整数,返回一个int类型
slug:可理解为注释、后缀、附属等概念,是url拖在最后的一部分解释性字符。该转换器匹配任何ASCII字符以及连接符和下划线,比如“ building-your-1st-django-site”;
uuid:匹配一个uuid格式的对象。为了防止冲突,规定必须使用破折号,所有字母必须小写,例如’075194d3-6885-417e-a8a8-6c931e272f00‘ 。返回一个UUID对象;
path:匹配任何非空字符串,重点是可以包含路径分隔符“/”。这个转换器可以帮助你匹配整个url而不是一段一段的url字符串
三、视图函数返回值
在django框架中,调用视图函数必须有返回值,且必须返回一个HttpResponse对象;
在视图层中,HttpResponse、render、redirect,这三个都是返回HttpResponse对象;
HttpResponse
class HttpResponse(HttpResponseBase):
streaming = False
render
def render(request, template_name, context=None, content_type=None, status=None, using=None):
content = loader.render_to_string(template_name, context, request, using=using)
return HttpResponse(content, content_type, status)
redirect
def redirect(to, *args, **kwargs):
if kwargs.pop('permanent', False):
redirect_class = HttpResponsePermanentRedirect
else:
redirect_class = HttpResponseRedirect
return redirect_class(resolve_url(to, *args, **kwargs))
# 查看源码得知,redirect通过层层嵌套也是间接继承和返回了HttpResponse
总结:视图层自定义函数必须返回带有HttpResponse对象的返回值
四、JsonResponse对象
django中的JsonResponse模块相当于对json序列化的数据类型的范围做了扩充,查看源码,其实还是使用的原始的json模块;
# JsonResponse源码
class JsonResponse(HttpResponse):
def __init__(self, data, encoder=DjangoJSONEncoder, safe=True,
json_dumps_params=None, **kwargs):
if safe and not isinstance(data, dict):
raise TypeError(
'In order to allow non-dict objects to be serialized set the '
'safe parameter to False.'
)
if json_dumps_params is None:
json_dumps_params = {}
kwargs.setdefault('content_type', 'application/json')
data = json.dumps(data, cls=encoder, **json_dumps_params) # 还是使用json方法
super(JsonResponse, self).__init__(content=data, **kwargs)
代码验证
from django.http import JsonResponse
def ab_json(request):
1.HttpResponse传递字典 >>> 无法json序列化
user_dict = {"name":'jason','pwd':123,'hobby':'好好学习'}
dict_json = json.dumps(user_dict,ensure_ascii=False)
return HttpResponse(dict_json)
2.JsonResponse传递字典
user_dict = {'name': 'jason', 'pwd': 123, 'hobby': '好好学习'}
return JsonResponse(user_dict,json_dumps_params={'ensure_ascii':False}) # 为防止乱码,需要设置json_dumps_params
3.JsonResponse传递列表
user_list = [11, 22, 33, 44, 55]
return JsonResponse(user_list, safe=False) # 为防止乱码,需要设置safe=False
五、django接收form表单上传文件
form表单上传的数据中如果含有文件,那么需要做以下几件事:
1.method必须是post
<form action="" method="post" enctype="multipart/form-data">2.enctype必须修改为multipart/form-data(默认是application/x-www-form-urlencoded)
<form action="" method="post" enctype="multipart/form-data"> <p>username: <input type="text" name="username" class="form-control"> </p> <p>files: <input type="file" name="my_file" class="form-control" multiple> </p> <input type="submit" class="btn btn-success btn-block"> </form>3.后端需要使用request.FILES获取
def ab_form(request): if request.method == 'POST': print(request.POST) # 不能获取文件数据 print(request.FILES) # 专门获取文件数据 file_obj = request.FILES.get('my_file') # 'my_file'是input的name值 print(file_obj.name) # 查看文件名 with open(file_obj.name,'wb') as f: for line in file_obj: # 相当于粘贴文件到当前文件夹 f.write(line)
六、request其他方法
request.method
request.POST
request.GET
request.FILES
request.body # 存放的是接收过来的最原始的二进制数据
ps:request.POST、request.GET、request.FILES这些获取数据的方法其实都从body中获取数据并解析存放的;
request.path
获取路径即url
request.path_info
获取路径即url
request.get_full_path()
获取路径(url)并且还可以获取到路径(?...)后面携带的参数
七、FBV与CBV
1、FBV
基于函数的视图
2、CBV
基于类的视图
# 视图层 from django import views class MyLoginView(views.View): def get(self, request): return HttpResponse("from CBV get view") def post(self, request): return HttpResponse("from CBV post view") # 路由层 url(r'^ab_cbv/', views.MyLoginView.as_view()) """ 如果请求方式是GET,则会自动执行类里面的get方法; 如果请求方式是POST,则会自动执行类里面的post方法; """
3、CBV源码剖析
(1)路由层创建关系
url(r'^ab_cbv/', views.MyLoginView.as_view()) # MyLoginView是即将要创建的类名
分析:
as_view可以是普通的静态方法;
as_view可以是绑定给类的方法;
项目启动后,访问此路由,由于函数加括号调用优先级最高,会先执行as_view(),可以看成一个返回值;
(2)在视图层创建类
class MyLoginView(views.View):
def get(self, request):
return HttpResponse("from CBV get view")
def post(self, request):
return HttpResponse("from CBV post view")
# 使用get请求后,结果为:from CBV get view
# 使用post请求后,结果为:from CBV post view
分析:
类MyLoginView并没有相关方法能够识别get和post请求,那么必定在继承的父类中,我们接下来查看View源码
(3)查看View源码
class View(object):
# 1、请求方式
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'] # 共八种
# 2.as_view函数
@classonlymethod
def as_view(cls, **initkwargs):
... # 跳过部分代码
def view(request, *args, **kwargs):
... # 跳过部分代码
return self.dispatch(request, *args, **kwargs)
return view
def dispatch(self, request, *args, **kwargs):
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
分析:
运行步骤:
(1)url调用views.MyLoginView.as_view(),先执行as_view;
(2)as_view()的返回值为view,因此:
views.MyLoginView.as_view() == views.MyLoginView.view;
(3)路由匹配成功后执行view,执行类的dispatch方法(self.dispatch())
(4)执行dispatch,判断如果请求方式在八种方式中,获取该请求方式的对应字符(handler)后,并加括号运行该函数:return handler()
因此:
as_view() == view;
view() == dispatch() == handler();
总结:
views.MyLoginView.as_view() == handler(request, *args, **kwargs) # handler相当于不同的请求方式 >>> post,get...
八、模板语法传值
"""
django提供的模板语法只有两个符号
{{}}:主要用于变量相关操作(引用)
{%%}:主要用于逻辑相关操作(循环、判断)
"""
1.传值的两种方式
# 传值方式1:指名道姓的传,适用于数据量较少的情况,节省资源
return render(request, 'ab.html', {'name':name})
# 传值方式2:打包传值,适用于数据量较多的情况(偷懒),浪费资源
'''locals() 将当前名称空间中所有的名字全部传递给html页面'''
return render(request, 'ab_temp.html', locals())
2.传值的范围
基本数据类型都可以;
函数名:
模板语法会自动加括号执行并将函数的返回值展示到页面上;
不支持传参(模板语法会自动忽略有参函数);
文件名:
直接显示文件IO对象;
类名:
自动加括号实例化成对象;
对象名:
直接显示对象的地址,并且具备调用属性和方法的能力;
# django模板语法针对容器类型的取值 只有一种方式>>>:句点符
data1 = {'info':{'pro':[11, 22, 33, {'name':'jason','msg':'努力就有收获'}]}};
既可以点key值也可以点索引,django内部会自动识别;
{{ data1.info.pro.3.msg }}
昨日作业
1.数据展示
2.给按钮附加功能
3.如何明确用户到底想要编辑哪条数据
在路由匹配中就应该获取到用户想要编辑的数据主键值
4.点击编辑按钮 应该展示当前数据的编辑页面
通过无名或者有名分组获取到用户想要编辑的数据主键值
获取对应的数据对象传递给页面 展示给用户看并提供编辑功能
5.编写删除功能
路由设计跟编辑功能一致
def home(request):
data_queryset = models.User.objects.filter() # [obj1,obj2,obj3]
return render(request,'home.html',{'data_queryset':data_queryset})
def edit_data(request,edit_id):
# 获取用户编辑的数据对象
edit_obj = models.User.objects.filter(id=edit_id).first()
if not edit_obj:
return HttpResponse('当前用户编号不存在')
if request.method == 'POST':
# 获取新的数据
username = request.POST.get('username')
password = request.POST.get('password')
# 修改原数据 方式1
# models.User.objects.filter(id=edit_id).update(name=username,pwd=password)
# 修改原数据 方式2
edit_obj.name = username
edit_obj.pwd = password
edit_obj.save()
# 重定向到展示页
return redirect('home_view') # 括号内也可以直接写反向解析的别名 不适用于无名有名反向解析
# 将待编辑的数据对象传递给页面展示给用户看
return render(request,'edit.html',{'edit_obj':edit_obj})
def delete_data(request,delete_id):
# 获取想要删除的对象数据
edit_queryset = models.User.objects.filter(id=delete_id)
if not edit_queryset:
return HttpResponse("用户编号不存在")
edit_queryset.delete()
return redirect('home_view')
浙公网安备 33010602011771号