django里的那些事儿

一、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)

    • 用户是否登陆:request.user.is_authenticated

    • 验证用户名密码是否正确: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循环

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352