一、CBV版视图
二、给视图加装饰器
三、request对象常用属性
四、url:路由系统
五、命名url和url的反向解析
六、session
七、使用 pymysql 连接 mysql
八、内置auth认证
九、模板语法中的for循环
django官方文档:django-documentation
-
一、CBV版视图
veiw中写函数有两种写法,之前写的def类都是FBV版。还有一种写法是CBV版。就是将之前函数的写法变类的写法,url中也要相应变动一下。
例:
视图中定义: from django.views import View Class AddBook(View): def dispatch(self, request, *args, **kwargs): print('处理请求之前') ret = super().dispatch(request, *args, **kwargs) print('处理请求之后') return ret def get(self,request): pass def post(self,request): pass
url中使用: url(r'^add_book/$',views.AddBook.as_view())
执行过程: 1,AddBook这个类,执行了as_view()这个方法,但是,我们自定义的类里没有写这个方法; 这就要看继承的父类里,View类有没有了,查看,有此方法,是类方法。 2,这个类方法执行到最后返回了一个view函数,不带括号,没有执行,等待连接后才执行。 3,view函数执行到最后返回了一个dispatch函数且执行后的结果。 self = cls(**initkwargs) 实例化了一个对象 return self.dispatch(request, *args, **kwargs) 此时如果自己写的类里有dispatch方法则会执行自己的,没有则会执行父类的 4,dispatch(派遣的意思)函数过程中对接收到的请求方式进行了判断,并对其进行了反射, handler = getattr(self, request.method.lower(), self.http_method_not_allowed) 反射得出方法 如果是get则执行get函数,如果是post则执行post函数,并将执行结果返回。 return handler(request, *args, **kwargs) 执行并返回结果 父类的dispatch(self, request, *args, **kwargs):方法 是执行自己写的get/post函数的一个过程。 理解dispatch: 上述分析,函数走到dispatch后,是一个对象方法,如果在自己的类里写了此方法,那么他会走自己的方法。 但是,父类有这个方法为什么还要自己写呢?走自己的方法有什么用呢? 那就要看你的需求了,函数走到那一步就会走dispatch这个函数, 如果自己感觉累了,想要先停一会儿,你就停几分钟,过一会儿再拿过来父类的dispatch运行, 因为你自己的dispatch方法没有父类的功能,所以还得借用父类的dispatch方法。 简版的流程: AddPublisher.as_view() ——》 view 函数 当请求来的时候才执行view view中执行: 1. 先实例化AddPublisher,给self 2. 执行self.dispatch() self 有dispatch 就执行自己的 没有就是执行 父类(View)的dispatch方法 3. dispatch中执行: 先通过反射获取到AddPublisher中定义get或者post方法。 执行get或者post方法 返回httpresponse对象 4. view接收到dispatch的返回值——httpresponse对象 5. view返回httpresponse对象
-
二、给视图加装饰器
装饰器: def wrapper(func): def inner(*args, **kwargs): start_time = time.time() ret = func(*args, **kwargs) end_time = time.time() print("used:", end_time-start_time) return ret return inner
# FBV版添加装饰器 @wrapper def add_class(request): if request.method == "POST": class_name = request.POST.get("class_name") models.Classes.objects.create(name=class_name) return redirect("/class_list/") return render(request, "add_class.html")
# CBV版添加装饰器 from django.views import View from django.utils.decorators import method_decorator 第一种:(给各自加) class AddClass(View): @method_decorator(wrapper) def get(self, request): pass @method_decorator(wrapper) def post(self, request): pass 第二种:(给dispatch加) class AddClass(View): @method_decorator(wrapper) def dispatch(self, request, *args, **kwargs): print('before') obj = super(Login,self).dispatch(request, *args, **kwargs) print('after') return obj def get(self, request): pass def post(self, request): pass 第三种:(给类加) @method_decorator(wrapper,name='get') @method_decorator(wrapper,name='post') class AddClass(View): def get(self, request): pass def post(self, request): pass
-
三、request对象常用属性
request.method 请求方式 GET POST request.GET 字典,URL上传的参数 request.POST 字典,form表单传的数据 request.COOKIES.get 获取cookie信息 request.session.get 获取session信息 request.is_ajax() 判断请求是不是ajax request.path_info ---》/add_publisher/ 用户访问的URL,不包括域名 request.body ---》一个字符串,代表请求报文的主体。在处理非 HTTP 形式的报文时非常有用,例如:二进制图片、XML,Json等。 但是,如果要处理表单数据的时候,推荐还是使用 HttpRequest.POST 。 request.FILES ---》一个类似于字典的对象,包含所有的上传文件信息。 FILES 中的每个键为<input type="file" name="" /> 中的name,值则为对应的数据。 注意:FILES 只有在请求的方法为POST 且提交的<form> 带有enctype="multipart/form-data" 的情况下才会 包含数据。否则,FILES 将为一个空的类似于字典的对象。 request.get_full_path() -->/test/?id=1 参数也拿到了 request.path_info -->/test/ 只拿到了url request.get_host -->IP+端口 127.0.0.1:8000
-
四、url:路由系统
(1) urlpatterns = [ url(正则表达式,views视图,参数,别名) ] (2) ^ 以...为开头,$以...为结尾 (3) 如果项目中没有按照自己以为的去执行函数,可能是因为正则匹配过程中匹配到了别的。 匹配时从上往下依次匹配,如果上面有符合要求的,就不会走下面的。 url(r'^book/',) url(r'^book/asd') 一般可写成url(r'^book/$')规定开头结尾,约束 (4) url位置参数说明: # 用户输入url,满足条件匹配上之后,才能将参数传进test函数,让test函数对url中分组内的参数进行处理。 # 即用户可通过url输入参数,url匹配成功后,通过url进行传参,传括号里的内容传给函数进行处理。 无名分组: 实例1: 用户输入: 127.0.0.1:8000/test/2018/08 url: url(r'^test/([0-9]{4})/[0-9]{2}',views.test) 函数: def test(request,*args,**kwargs): print(args) 输出结果:('2018',) return HttpResponse('你自己输入的{},你还愁啥'.format(args)) 页面显示:你自己输入的('2018',),你还愁啥 实例2: 用户输入: 127.0.0.1:8000/test/嘀嗒嘀嗒嘀嗒嘀。。。。(随便输入) url: url(r'^test/([0-9]{4})/[0-9]{2}',views.test),不走这个 url(r'^test/(.+)',views.test1) 走这个 函数: def test1(request,*args): return HttpResponse(args[0]) 页面显示:输入什么就显示什么 url(r'^test/(.{3})',views.test1),这个是不管输入多少,只显示3个字 (5) # 是否开启URL访问地址后面不为/,也能跳转至带有/的路径的配置项: 配置中写入这个,不加/也能自动添加/再走一次 APPEND_SLASH=True (默认为True,所以不写也能自动添加) 如果不想让其自动添加,就改为 False (6)分组命名匹配:(区别在于:函数中只接收正则中命名的名字作为参数,将匹配到的结果传给函数) 有名分组: 实例: 用户输入: http://127.0.0.1:8000/test/阿斯顿法国红酒看来 url中: url(r'^test/(?P<nei_rong>.{2})',views.test2) views中: def test2(request,nei_rong): print(nei_rong) 输出结果:阿斯 return HttpResponse('输入的内容为:{}'.format(nei_rong)) 页面显示:输入的内容为:阿斯 (7) 后端接收到的url中的参数都是字符串! (8) 视图函数中指定默认值 # urls.py中 from django.conf.urls import url from . import views urlpatterns = [ url(r'^blog/$', views.page), url(r'^blog/page(?P<num>[0-9]+)/$', views.page), ] # views.py中,可以为num指定默认值 def page(request, num="1"): pass (9) 包含其他的url:(可将不同url写进不同的业务线下,然后用总的包含) from django.conf.urls import include, url from app01 import urls urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^app01/', include('app01.urls')), # 可以包含其他的URLconfs文件 【1,不导入,写字符串,反射找到】 from app01 import urls as app01_urls url(r'^app01/', include(app01_urls)) 【2,导入,不用写成字符串】 ] (10) 当两个app写了两个相同的url,反向解析时就会冲突,所以要将不同的app下的url写不同的标记,即 在总的url中,命名空间。 url(r'^app01/', include('app01.urls'),namespace='app01') url(r'^app02/', include('app02.urls'),namespace='app02') 反向解析时: url = reverse('app01:index') url = reverse('app02:index') 起了namespace,都要用app01:加前缀 url = reverse('app01:book') (11) url中可传参数(可以在url中自己写一个参数,字典中的,传给views中的相应函数) urlpatterns = [ url(r'^blog/(?P<year>[0-9]{4})/$', views.year_archive, {'foo': 'bar'}), ]
-
五、命名url和url的反向解析
1,起名字: url(r'^json/',views.json_data,name='js') 2,用(可在视图中用,可在模板中用) 视图中用: 导入reverse: from django.shortcuts import render,HttpResponse,reverse url = reverse('js') 结果是/json/,即使改为/jsonfgieue/,也能通过js找到。通过名字就能找到这个url, 比较灵活(不带参数的),通过名字,找到url,字符串类型 模板中用: url(r'baidusss/',views.baidu,name='baidu') <a href="/baidu/">这是跳转到一个自定义的百度路由(写死的),然后返回一个百度的连接</a> <a href="{% url 'baidu' %}">这个连接用的是反向解析,如果views中的url变了,上面就找不到了</a> 3,作用:不怕url改名字。不管怎么改,我的模板中,函数中,都能找到此路由, 这就给自己提供了方便,即使路由改了,html页面中或函数中要跳转的路由不用改,只是用户输入时需要改。 因为程序内部有这个反向解析的方法能找到,而用户只能是提供什么url就写什么url 4,带参数的用法(无名分组) url(r'^testtttttt/([0-9]{4})/([0-9]{2})',views.test,name='test') 视图中,需传参 url = reverse('test',args=('1998','08)) 模板中,需传参 <a href="{% url 'test' '1998' '08' %}">测试</a> 5,带有名分组的参数 url(r'^testtttttt/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})',views.test,name='test') 视图中,传位置参数 url = reverse('test',kwargs={'year':'1998','month':'08'}) 模板中,传参 <a href="{% url 'test' '1998' '08' %}">测试</a> (对应先后顺序) 或者 <a href="{% url 'test' month='08' year='1998' %}">测试</a> (可换位置)
举个栗子:
总路由: urlpatterns = [ url(r'^goods/', include('df_goods.urls', namespace='df_goods')), # 添加实例命名空间 ] 子路由: urlpatterns = [ url('^index/$', views.index, name="index"), ] 视图应用: print(reverse("df_goods:index")) # 利用reverse函数反向解析url # 打印结果为/goods/index/ 页面应用: <a href="{% url "df_goods:index" %}">反向解析</a>
-
六、session
Cookie虽然解决了“保持状态”的需求,但是cookie最大支持4096字节,且cookie保存在客户端,可能被拦截或窃取。 因此就需要有一种新的东西,支持更多字节,并且保存在服务器,有较高的安全性,这就是Session。 Session 1,接收请求,返回响应 2,返回响应时,生成一个特殊的字符串 3,在后端开一块空间,保存请求相关数据 4,返回响应 下一次请求时: 1,带着之前发给浏览器的字符串 2,拿着字符串找相应的箱子 3,在箱子中根据key取值 前提:django存储session是在内置的 django_session 表中,所以必须先生成其表。 1. 设置Session: 不需要在响应中设置,直接request.session['key']='value' 1. request.session[key] = value 2. request.session.setdefault(key, value) --> 有就啥都不干,没有就赋值 2. 获取session: if request.session.get('key','')== 'value': pass 1. request.session[key] 2. request.session.get(key) 推荐 3. 删除Session 1. request.session.delete() 2. request.session.flush() --> 删除Cookie和Session 4. 清空已经过期的session数据 request.session.clear_expired() 5. 设置Session过期时间 request.session.set_expiry(过期时间) session中的有效日期,默认是两周 6. settings配置中: SESSION_SAVE_EVERY_REQUEST = True # 一般设为True # 每次请求都更新超时时间,超时时间从最近的一次请求算起。 SESSION_COOKIE_AGE = 129600 --->默认两周
-
七、使用 pymysql 连接 mysql
1,创建mysql数据库 2,settings配置: DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'chubanshe_book', 'HOST': '127.0.0.1', 'POST': 3306, 'USER': 'root', 'PASSWORD': '', } } 3,告诉django使用pymysql来连接数据库: 在与项目同名的文件夹下的__init__.py文件中写: import pymysql pymysql.install_as_MySQLdb() 4,在app01/models.py写类(必须继承models.Model) class Publisher(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32,null=False,unique=True) 5,执行数据库迁移两条命令 python manage.py makemigrations # 保存models修改记录 python manage.py migrate # 操作数据表 1 2 3 4 5 创表, 我要连(配置), 我要用什么连(pymysql), 形成映射关系(model), 两条命令
-
八、内置auth认证
auth认证:默认django内置的认证系统,默认在数据库使用auth_user表
-
扩展内置user表
models.py: from django.contrib.auth.models import AbstractUser class UserInfo(AbstractUser): phone = models.CharField(max_length=11) settings.py: AUTH_USER_MODEL = "app01.UserInfo" views.py: models.UserInfo.objects.create_user(password=pwd,username=user,phone=pho)
django的登录:
auth.login(request,user)
django的注销:
auth.logout(request)
验证用户名密码是否正确:
auth.authenticate(request, username=username, password=pwd)
登录认证装饰器:
login_required
LOGIN_URL = '/login/' # 配置将内置登录路由替换成自己的登录路由登录用户显示用户名,非登录用户请登录:
user.is_authenticated
检查原密码:
check_password(password)
---> 修改密码时使用
auth
提供的一个检查密码是否正确的方法,需要提供当前请求用户的密码。
密码正确返回True,否则返回False。
ok = request.user.check_password('密码')
重置新密码:
set_password(password)
---> 修改密码时使用
auth
提供的一个修改密码的方法,接收 要设置的新密码 作为参数。
注意:设置完一定要调用用户对象的save
方法!!!
request.user.set_password(密码)
request.user.save()
-
九、模板语法中的for循环