• django简单使用


    django简单使用

     

     

    django简单使用

    安装

    查看版本

    创建项目,启动项目

    创建应用模块

    创建超级用户

    日期的处理

    配置数据库信息

    连接Mysql的驱动

    初始化数据库表

    模型迁移到数据库

    数据库表迁移到模型

    模型源信息

    模型的增删改查

    增删改查、且并、排序、Q运算等多种查询方式

    原始的查询方式

    不避开模型层

    避开模型层

    自定义SQL查询方式

    模型成员

    objects

    模型序列化成字典

    模型关系

    一对一

    一对多

    多对多

    模型继承

    模型附加

    文件上传

    模板的使用

    模板配置

    继承模板

    模板标签

    常见过滤器

    模板原理

    配置静态目录

    缺陷

    合并静态资源

    配置日志输出

    日志流程图

    日志级别

    日志配置

    只输出SQL日志

    django默认已有配置

    更多配置

    使用日志

    注意事项

    路由

    路由传参

    反向解析

    视图

    视图函数

    类视图

    视图响应

    HttpRequest

    HttpResponse

    子类

    Cookie

    Session

    Token

    Django缓存

    流程

    常见缓存

    缓存操作

    案例

    使用数据库进行缓存

    redis缓存

    AOP中间件

    基本使用

    案例

    更多使用

    内置middleware

    分页

    邮件

    消息

    使用Django Shell

    settings配置

    Django前端调试工具

    Django包结构拆分

     

     

    安装

    pip install Django

    查看版本

    >>> import django
    >>>django.get_version()

    创建项目,启动项目

    django-admin startproject mysite

    将该项目mark as Sources Root方便代码提示和导入路径

    生成的目录结构:

    mysite/:根目录,仅仅只是项目的容器【名称可以随意重命名】    

    manage.py:让你用各种方式管理Django项目的命令行工具    

    mysite/:一个纯的python包,它包含着你的项目       

    __init__.py:一个空文件,告诉Python该目录是一个Python包       

    settings.py:Django项目的配置文件        

    urls.py:Django项目的配置文件,就像你网站的“目录”         wsgi.py

    启动项目:

    python3 manage.py runserver 0.0.0.0:8000

    创建应用模块

    # 在主目录下
    python manage.py startapp SDD

    我创建得这个应用模块SDD,导致了项目启动报找不到SDD这个模块,然后我删掉这个模块使用:

    python manage.py startapp sdd

    就不出现这个问题了。估计是SDD名称不符合Django命名规范吧。

     

    创建超级用户

    python manage.py createsuperuser

    日期的处理

    // setting文件中加入:
    USE_L10N = False
    DATE_FORMAT = 'Y-m-d'
    DATETIME_FORMAT = 'Y-m-d H:i:s'
    
    // 注意事项:如果USE_L10N设置为了True,那么语言环境规定的格式具有更高的
    // 优先级并将被应用,即DATE_FORMAT不生效。

    配置数据库信息

    https://docs.djangoproject.com/zh-hans/2.1/intro/tutorial02/#database-setup

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'lcfzjc',
            'USER': 'root',
            'PASSWORD': 'root',
            'HOST': '127.0.0.1',
            'PORT': '3306',
        }
    }

    连接Mysql的驱动

     

     

    推荐使用pymysql

    如果按照好pymysql之后,依旧报是否安装来mysqlclient、mysql-db,这时候就需要我们使用pymysql伪装成其他两个包了,伪装的方式就如上图,在程序最开始执行的地方,执行:

    pymysql.install_as_MySQLdb()

    不一定是在init文件中,也可以在manage或settings中。

     

    初始化数据库表

    // 执行迁移,会创建django自带模块中的表
    python manage.py migrate

    执行这句话回初始化setting.py文件中INSTALLED_APPS中:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
    ]

    的所有模块需要的表。初始结果如下:

    模型迁移到数据库

    https://docs.djangoproject.com/zh-hans/2.1/intro/tutorial02/#creating-models

    // 创建polls模块的迁移文件
    python manage.py makemigrations polls
    // 执行迁移
    python manage.py migrate

    数据库表迁移到模型

    # 在命令行打印数据库对应的models模型文件
    python manage.py inspectdb
    
    # 将输出的模型输入到项目的models文件里
    python manage.py inspectdb > ./models00.py

    模型源信息

    class Interactive(models.Model):
        intention = models.TextField(blank=True, null=True))
    
        class Meta:
            managed = False // 不然django管理我们的模型,执行迁移无效
            db_table = 'zndz_manage_interactive'

    模型的增删改查

    class Garde(models.Model):
        className = models.CharField(max_length=32)
        
    class Student(models.Model):
        name = models.CharField(label='姓名', max_length=20, default='', help_text=_('帮助文档。'), )
        age = models.IntegerField(label='年龄')
        garde = models.ForeignKey(Garde,null=True,blank=True,on_delete=models.CASCADE)

    增删改查、且并、排序、Q运算等多种查询方式

    // python manage.py shell
    // 获取单个对象,使用get记得要是有try except
    Student.objects.get(ID=1)
    
    Student.objcets.all().first()
    
    Student.objcets.all().last()
    
    // 过滤
    Student.objects.filter(age__gt = 30)  // 获取年龄大于30的
    
    Student.objcets.exclude(age_gt = 30) // 除了年龄大于30的,其余的都获取
    
    // 排序
    Student.objects.all().order_by('-ID') // id倒序 
    
    // 计数 与 判断
    s = Student.objects.filter(age = 30)
    if s.count():
    if s.exists():
        
    // 切片
    Student.objcets.all()[0:5]
     // 会被转换为SQL limit 0,5,注意这和python里的切片不太一样,这里的切片代表的
     // 是limit,且不能为负数,python里的切片可以为负数
     
     // 缓存集
     filter、exclude、all都不会真正的去查询数据库,只有我们在迭代结果集,或
     者获取单个对象属性的时候,它才会去查询数据库。
     
     // 查询条件
    __gt
    __lt
    __grt
    __lte
    __in
    
    __contains // like
    __startswith // like
    __endswith // // like
    __exact // 精确等于,即=
     // 以上四种前面如果加i(即ignore)表示忽略大小写,例如iexact
     
    //  django 中的查询条件有时区问题,有两种解决方案
    1.关闭django自定义的时区,直接将setings里的USE_TZ改成False即可
    2.在数据库中创建时区表(麻烦,不推荐)
    
    // 聚合关系
    Student.objects.aggregate(Max("age")) // 最大年龄的学生
    .aggregate(Avg("age"))  .aggregate(Max("age"))  Count|Min|Sum
    
    
    // F对象:可以使用模型的A属性与B属性进行比较
    // 可以实现一个模型的不同属性的算符运算操作
    // 还可以支持算术运算
    Grades.objects.filter(ggirlnum__gt=F('gboynum')) // 找到女生人输大于男生人数的班级
    
    
    // Q对象:过滤器的方法中的关键字参数,条件为And模式,采用逻辑或引入Q对象
    // 可以对条件进行封装,封装之后,可以支持逻辑运算:
    // 与 &
    // 或 |
    // 非 ~
    studentList = Student.objects.filter(Q(pk__lt = 3)|Q(sage__gt=50))  pk_id小于3或年龄大于50岁
    models.User.objects.filter(Q(username='老王') & Q(userpass='admin'))   条件与组合
    models.User.objects.filter(~Q(username='老王'))   条件非表示取反
    可以使用 &(and) |(or)  ~(not) 结合括号进行分组,构造更复杂的Q对象
    filter函数可以传递一个或多个Q对象作为位置参数,如果有多个Q对象,这些参数的逻辑为and

    原始的查询方式

    学习网址:https://my.oschina.net/liuyuantao/blog/751902

    在模型查询API不够用的情况下,还可以使用原始的SQL语法。Django提供两种方法使用原始的SQL查询,一种是使用Manager.raw()方法,进行原始查询并返回模型实例;另一张是完全避开模型层,直接执行自定义SQL语句。【编写原始的SQL语句时,应该格外小心。 每次使用的时候,都要确保转义了参数中任何用户可以控制的字符,以防受到SQL注入攻击

     

    不避开模型层(参见随笔->Django模型)

    raw()管理器方法用于原始的SQL查询,并返回模型实例。

    Manager.raw(raw_query, params=None, translations=None)

    这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。这个RawQuerySet 实例可以像一般的查询集那样,通过迭代来提供对象实例。

     

    避开模型层(参见随笔->Django模型)

     

    自定义SQL查询方式(参见随笔->Django模型)

     

    模型成员

    隐性属性:

    开发者没有书写,ORM自动生成的

    如果你把隐性属性手动声明了,系统就不会为你产生隐性属性了。

     

    显性属性:

    开发者手动书写的属性

     

    objects

    objects为ORM的隐性属性,我们默认可以使用对象的objects去查询数据,当然我们也可以显示的声明别名:

    # Create your models here.
    class Student(models.Model):
        name = models.CharField(max_length=32)
    
        mk = models.Manager()
    >>> from demo.models import Student
    >>> Student.objects.all()
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
    AttributeError: type object 'Student' has no attribute 'objects'
    >>> Student.mk.all()
    <QuerySet [<Student: Student object (1)>]>

    我们还可以根据需求重写Manager的某些方法:

    class StudentManager(models.Manager):
    
        def get_queryset(self):
            """
            重写query_set
            :return: 
            """
            return super(StudentManager, self).get_queryset().filter(name__contains='mao')
    
    
    class Student(models.Model):
        name = models.CharField(max_length=32)
        objects = StudentManager()
        
    // 测试
    >>> from demo.models import Student
    >>> Student.objects.all()
    <QuerySet [<Student: Student object (1)>]>
    >>> Student.objects.all().first().name
    'maomao'

    模型序列化成字典

    from django.forms.models import model_to_dict
    from django.core.serializers import serialize
    
    
    # 序列化单个实体为json对象
    user = User()
    user.created_id = created_id
    user.name = request.POST['name']
    user.save()
    return JsonResponse({'code': 200, 'msg': "success", 'data': model_to_dict(user)})
    
    # 序列化多个实体为json对象
    uid = request.POST['id']
    users = User.objects.filter(created_id=uid, delated=0)
    return JsonResponse({'code': 200, 'msg': "success", 'data': serialize('json', users)})
    
    # 以上针对数据库操作发实体都是对象,所以可以使用model_to_dict或serizlize
    # 但如果从数据库查询的不是一个实体对象则不能使用上面两种方式
    users = User.objects.values('id', 'name', 'sex', 'age', 'phys', 'is_app_user').filter(Q(id=uid) & Q(deleted=1))
    # 如上users返回的结果已经是一个字典类型,或Queryset里面封装的是字典类型
    # 使用value或value_list查询的数据直接就是字典类型了

     

    模型关系

    一对一

    使用:

    models.OneToOneField()

    进行关联。

    // 需要其中一张表的主键作为另一张表的外键即可,保留外键的表称为从表
    class Person(models.Model): // 主表
        name = models.CharField(max_length=16)
    
    class Card(models.Model): // 从表
        number = models.CharField(max_length=32)
        person = models.OneToOneField(Person,blank=True,null=True)
        
    // 绑定卡与人的一对一关系,默认情况下,当人被删除的情况下,与人绑定的卡也被删除了,
    // 我们可以使用on_delete参数进行调整
    
    on_delete
        models.CASCADE 默认值
        models.PROTECT 保护模式
       models.SET_NULL 置空模式
       models.SET_DEFAULT 置默认值
       models.SET() 删除的时候重新动态指向一个实体

    应用场景

     用于复杂表的拆分

     扩展新功能

     

     

    实现

     使用外键实现的

     对外键添加了唯一约束

     

    数据删除

     级联表

     主表

     从表

     谁声明关系谁就是从表

     在开发中如何确认主从

     当系统遭遇不可避免毁灭时,只能保留一张表,这个表就是你的主表

     默认特性(CASCADE)

     从表数据删除,主表不受影响

     主表数据删除,从表数据直接删除

     受保护(PROTECT)

     开发中为了防止误操作,我们通常会设置为此模式

     主表如果存在级联数据,删除动作受保护,不能成功

     主表不存在级联数据,可以删除成功

     SET

     SET_NULL

     允许为NULL

     SET_DEFAULT

     存在默认值

     SET()

     指定值

     

    数据获取

     级联数据获取

     主获取从: 隐性属性 默认就是级联模型的名字

     从获取主: 显性属性,就是属性的名字

     

     

    一对多

    使用

    models.ForeginKey()

    数据获取

     级联数据获取

     主获取从: 隐性属性 默认就是级联模型的名字_set

     student_set  也就是Manager子类

     all

     filter

     exculde

     凡是Manager上能使用的函数都能使用

     从获取主: 显性属性,就是属性的名字

     

     

    多对多

     开发中很少直接使用多对多属性,而是自己维护多对多的关系

     产生表的时候会产生单独的关系表

     关系表中存储关联表的主键,通过多个外键实现的

     多个外键值不能相等

     级联数据获取

     从获取主

     使用显性属性,属性是Manager子类ManyRelateManager。从.主属性.操作

     主获取从

     也是Manager子类,操作和从操作主完全一样。使用隐性属性:主.从_set.操作

     级联数据

     add

     remove

     clear

     set等

     

     ManyRelateManager

     函数中定义的类

     并且父类时一个参数

     动态创建

     

    模型继承

     Django中模型支持继承

     默认继承会将通用字段放到父表中,特定字段放在自己的表中,中间用外键连接。

     关系型数据库关系越复杂,效率越低,查询越慢

     父类表中也会存储过多的数据(新增子类,父类也增加)

     使用元信息来解决上面这个问题

     使模型抽象化

     抽象的模型就不会在数据库中产生映射了

     子模型映射出来的表直接包含父模型的字段

     

    class Animal(models.Model):
        name = models.CharField(max_length=20)
    
    
    class Cat(Animal):
        like = models.CharField(max_length=10)
    
    
    class Dog(Animal):
        like = models.CharField(max_length=10)
        
    表结构:
    -- auto-generated definition
    create table demo_animal
    (
      id   integer     not null
        primary key autoincrement,
      name varchar(20) not null
    );
    
    -- auto-generated definition
    create table demo_cat
    (
      animal_ptr_id integer     not null
        primary key
        references demo_animal
          deferrable initially deferred,
      like          varchar(10) not null
    );
    
    -- auto-generated definition
    create table demo_dog
    (
      animal_ptr_id integer     not null
        primary key
        references demo_animal
          deferrable initially deferred,
      like          varchar(10) not null
    );
    
    // 可以看到在子类中都将父类Animal作为了他们的外键参考,且外键作为主键唯一,
    // 相当于一对一关联!这也就导致了新增一个子类,就新增一个父类!其实,我们只想
    // 将公共的字段抽象提取出来,并不想在数据库中也将父表建立出来,这时我们应该在
    // 父类的元信息上做说明

    抽象模型类记得要加抽象说明

    class Animal(models.Model):
        name = models.CharField(max_length=20)
        class Meta:
            abstract = True

    模型附加

    文件上传

    <form action="{% url 'demo:register' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        用户名:
        <input type="text" name="username">
        头像:
        <input type="file" name="img">
        <input type="submit">
    </form>
    
    // 万金油写法
    def register(request):
        if request.method == 'POST':
            # username = request.POST.get('username')
            
            
            
            img = request.FILES.get('img')
            BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            with open(os.path.join(BASE_DIR, 'static/upload/imgs/', img.name), 'wb') as f:
                for chunk in img.chunks():
                    f.write(chunk)
                    f.flush()
    
    
    
            return HttpResponse('注册成功!')
        else:
            return redirect(to=reverse('demo:register'))
    // 使用Django model模型提供的上传
    class User(models.Model):
        name = models.CharField(max_length=16)
        img = models.ImageField(upload_to='imgs/%Y')
        // 这里面的upload_to属性是以settings文件中MEDIA_ROOT属性指定的值
        // 为相对路径的,所有我们需要在settings文件中配置一下MEDIA_ROOT
        // 在upload_to中支持日期写法%Y年,%M月,日时分秒等
        // 需要安装Pillow包,用于Django验证图片
    
    // settings配置文件最后加入上传相对路径  
    MEDIA_ROOT = os.path.join(BASE_DIR, 'static/upload')
    
    def register(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            img = request.FILES.get('img')
    
            user = User()
            user.name = username
            user.img = img
            user.save()
            return HttpResponse('注册成功!')
        else:
            return redirect(to=reverse('demo:register'))

     

    //如果上传同名文件,Django还会帮我们在文件名后加入一些混淆码来区别,这给使用用户
    // 头像等带来了很大方便

     

    模板的使用

    模板配置

    使用模板时首先在根目录下创建templates目录,将该templates Mark as templates,方便我们在代码中给提示。然后配置settings.py:

    这里的DIRS指的是根目录下的templates,根目录下的templates一般放入一些基类html,如果没有应用模块,则:

    def index(request):
        return render(request,'sdd.html')

    就会去templates根目录下去找sdd.html

    为了更好的项目结构,我们一般在我们的应用模块内也建立一个自己的templates,在应用模块也添加一个sdd.html,依旧是上面的代码,Django并不会去找该应用模块内的templates下的html,要想让其寻找自己模块内的templates,首先要创建一个和应用文件夹名相同的文件,在把sdd.html丢进去,目录结构如下:

    这是Django约定成俗的东西。而且上面代码也要改一改:

    def index(request):
        return render(request,'sdd/sdd.html')

    这样就ok了,Django会先去自己ide应用中查找sdd.html如果没找到会去项目的templates目录里寻找sdd/sdd.html文件。

     

    继承模板

    {% extends 'base.html' %}
    
    {% block content %}
    2017-12-12
    {% endblock %}
    
    // include:加载模板进行渲染
    // 格式:{% include 'xxx' %}
    {% block content %}
        {% include 'header.html' %}
    {% endblock %}
    {% include 'footer.html' %}
    
    
    //可以覆盖父类中的内容,如果不想覆盖:
    {% block content %}
        {{ block.super }}
    {% endblock %}
    
    // 注意:能使用block+extedns解决的,就不要使用include

    模板标签

    模板语法以大括号开始,里面包裹着一对%

    // for循环:如果列表为空指向empty之后的语句,否则执行语句1
    {% for 变量 in 列表 %}
        语句1
        {% empty %}
        语句2
    {% endfor %}
    
    // forloop循环
    {{forloop.counter}}表示当前是第几次循环,从1开始数
    {{forloop.counter0}}表示当前是第几次循环,从0开始
    {{forloop.revcounter}}表示当前是第几次循环,倒着开始数,到1停止
    {{forloop.revconter0}}表示当前第几次循环,倒着数,到0停
    {{forloop.first}}是否是第一个  布尔类型
    {{forloop.last}}是否是最后一个  布尔类型
    
    // ifequal 如果相等,ifnotequal如果不相等
    {% ifequal value1 value2 %}
        语句
    {% endifequal %}
    
    // 使用static请求地址
    <img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">
    
    // 使用url编写链接,反向解析
    <a href="{{ url('admin:index') }}">Administration</a>
    
    // 调用方法
    {% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}
    
    // 模板过滤器
    {{ my_date|date:"Y-m-d" }}
    
    // 表单防止跨站请求伪造保护,在settings中要将配置开启:'django.middleware.csrf.Csre.CsreViewMiddleware'
    {% csrf_token %}
    
    
    
    
    // 模板注释
    {# <h1><h1> #} // 注释一行
    // 注释多行
    {# comment #}
    <h1><h1>
    {# endcomment #}
    
    // 更多语法参见
    https://docs.djangoproject.com/en/2.2/topics/templates/

    常见过滤器

    add  加法{{ value|add 1 }}  减法{{ value|add -1 }}
    lower 转小写{{ name|lower }}
    upper 转大写{{name|upper}}
    join 字符连接{{name|join '@'}}
    
    // HTML转义[谨慎使用]
    {{ code|safe }}
    // 还有一种可以转义
    // HTML渲染
    {% autoescape off %}
        {{code}}
    {% endautoescape %}
    
    // 不渲染HTML
    {% autoescape on %}
        {{code}}
    {% endautoescape %}

     

    模板原理

    from django.http import HttpResponse
    from django.template import loader
    
    
    def index(request):
        temp = loader.get_template('index.html')
        context = temp.render()
        return HttpResponse(context)

    其实我们使用模板也就是类似使用文件流对象读取index.html文件里的内容,然后直接拼接成字符串传递给HttpResponse对象,Django会将字符串中的标签渲染到页面。loader与我们直接使用文件流的方式不同的时,Django提供的loader不仅仅可以读取文件还能够帮助我们渲染模板标签。

     

     

    配置静态目录

    settings.py文件配置如下,然后在manage.py目录下新建static文件

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]

    html中引用如下:

    {% load static %}
    <link href="{% static 'bootstrap-4.1.1/css/bootstrap.min.css' %}" rel="stylesheet">

    不管使用static还是staticfiles都是可以的

    {% load staticfiles  %} <!--写static 和 staticfiles是一样的-->
    <script src="{% static 'jquery-3.3.1.js' %}"></script>
    <script src="{% static 'bootstrap-4.1.1/js/bootstrap.min.js' %}"></script>

    缺陷

    如果将setting配置文件中:

    DEBUG=False

    则就不在能访问到静态资源路径,开发者服务器不在提供访问静态资源。解决办法就是使用高性能的服务器如Nginx等来处理。

     

     

    合并静态资源

    在使用Nginx将项目部署在Linux上时,遇到一个问题,由于我的静态目录static有两块,一块时Django项目里的static,另一块是使用vue写的前端打包后的dist/static,我在项目的settings目录下直接在访问静态路径这块写了个数组,可以存放多个静态路径,但是到了把Nginx当作HTTP服务器去配置静态访问路径时,遇到了一个问题,就是怎么去在一个正则loaction内写两个静态路径,或者写具有相同正则的location去分开写两个静态路径:

    # 匹配静态目录路由
            location /static {
                alias /home/liuwei/PycharmProjects/DjangoProDemo/DjangoDemo/static;
            }

    我尝试使用逗号或空格隔开这两个路径都会在访问时访问不到一些静态文件,而分开写两个static location感觉又很不合适,所以我只好选择合并项目的static目录:

    - 根目录下执行
        python manage.py collectstatic
    
    - 如果报错提示:
        django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a file
        system path.
        
        - 你需要在setting.py中设置一下STATIC_ROOT        
             STATIC_ROOT = os.path.join(BASE_DIR, "static")
            STATIC_URL = '/static/'
            STATICFILES_DIRS = [
                os.path.join(BASE_DIR, "static"),
                os.path.join(BASE_DIR, "structfront/dist/static"),
            ]

    执行合并命令时,它只会关注STATIC_ROOT配置的目录地址,然后扫描整个项目的静态资源(包含依赖包中的),将静态资源写入到STATIC_ROOT配置的目录中。注意:执行完该命令后要将STATIC_ROOT注释掉。当你在使用Nginx作用HTTP服务器的时候我想在setting.py中所有有关静态资源的配置已经不在起作用了。

     

    配置日志输出

    参考这两篇文章:文章1文章2

     

    日志流程图

    日志级别

    # 日志优先级
    OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL

    日志配置

    只输出SQL日志

    # 在setting文件中添加:
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'handlers': {
            'console': {
                'class': 'logging.StreamHandler',
            },
        },
        'loggers': {
            'django.db.backends': {
                'handlers': ['console'],
                'level': 'DEBUG' if DEBUG else 'INFO',
            },
        },
    }

    django默认已有配置

    # 在setting文件中添加:
    # 输出到控制台
    LOGGING = {
        'disable_existing_loggers': False,
        'version': 1,
        'handlers': {
            'console': {
                # logging handler that outputs log messages to terminal
                'class': 'logging.StreamHandler',
                'level': 'DEBUG',  # message level to be written to console
            },
        },
        'loggers': {
            '': {
                # 这会将根级别记录器设置为记录调试和更高级别
                # 记录到控制台。 所有其他记录器都继承自
                # 根级记录器。
                'handlers': ['console'],
                'level': 'DEBUG',
                'propagate': False,  # this tells logger to send logging message
                # to its parent (will send if set to True)
            },
            'django': { # django自己的,我们只是重写一下
                'handlers': ['default', 'console'],
                'level': 'DEBUG',
                'propagate': False 
            },
            'django.request': { # django自己的,我们只是重写一下
                'handlers': ['request_handler'],
                'level': 'DEBUG',
                'propagate': False,
            },
            'django.db': {
                # # django also has database level logging
                'handlers': ['console'],
                'level': 'DEBUG',
                'propagate': False,
            },
        },
    }

    更多配置

    # 输出到日志文件
    #导入模块
    import logging
    import django.utils.log
    import logging.handlers
     
     
    LOGGING = {
        'version': 1, # 保留字
        'disable_existing_loggers': True, # 是否禁用已经存在的日志实例
        'formatters': { # 定义日志格式
           'standard': {
                'format': '%(asctime)s [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] [%(module)s:%(funcName)s] [%(levelname)s]- %(message)s'
            },  #日志格式
            'simple': {
                'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
            },
            'collect': {
                'format': '%(message)s'
            }    
        },
        'filters': {
            'require_debug_true': { # 定义日志的过滤器
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {
            'mail_admins': {
                'level': 'ERROR',
                'class': 'django.utils.log.AdminEmailHandler', # 将规定级别及以上的消息通过电子邮件发送
                'include_html': True,
            },
            'default': {
                'level':'DEBUG',
                'class':'logging.handlers.RotatingFileHandler', # 若日志超过指定文件的大小,会再生成一个新的日志文件保存日志信息
                'filename': '/sourceDns/log/all.log',     #日志输出文件
                'maxBytes': 1024*1024*5,                  #文件大小5M 最好不要超过1G
                'backupCount': 3,                         #备份份数 xx.log --> xx.log.1 --> xx.log.2 --> xx.log.3
                'formatter':'standard',                   #使用哪种formatters日志格式
                'encoding': 'utf-8',  # 文件记录的编码格式
            },
            'error': {
                'level': 'DEBUG',
                'class': 'logging.handlers.TimedRotatingFileHandler',  # 日志按照天数滚动
                'filename': '/'.join((log_path, 'log.txt')),
                'when': 'D', # 每天一切, 可选值有S/秒 M/分 H/小时 D/天 W0-W6/周(0=周一) midnight/如果没指定时间就默认在午夜
                'interval': 1,
                'backupCount': 5,
                'formatter': 'simple',
                'encoding': 'utf-8',
            },
            'console':{
                'level': 'DEBUG',
                'filters': ['require_debug_true'],  # 只有在Django debug为True时才在屏幕打印日志
                'class': 'logging.StreamHandler',
                'formatter': 'collect'
            }
        },
        'loggers': {
            'django': { # django自己的,我们只是重写一下
                'handlers': ['default', 'console'],
                'level': 'DEBUG',
                'propagate': False 
            },
            'django.request': { # django自己的,我们只是重写一下
                'handlers': ['request_handler'],
                'level': 'DEBUG',
                'propagate': False,
            },
            'scripts': { 
                'handlers': ['scprits_handler'],
                'level': 'INFO',
                'propagate': False
            },
            'sourceDns.webdns.views': {
                'handlers': ['default', 'error'],
                'level': 'DEBUG',
                'propagate': True
            },
            'sourceDns.webdns.util':{
                'handlers': ['error'],
                'level': 'ERROR',
                'propagate': True
            }
        } 
    }

    使用日志

    # django系统会默认自动输出程序运行的错误日志到指定的log_name配置的文件路径下,但有时候我们捕获的异常就不会输出,
    # 这时需要我们手动的去输出一些日志,输出方法如下:获取要输出的日志名称创建logger对象,然后就可以调用内部方法输出。
    # traceback 是用来获取异常的调用栈信息的对象
    
    #日志手动输出:
    import logging
    import traceback
    
    # logger= logging.getLogger("log_name")
    logger = logging.getLogger("django.request")
    
    def test():
        try:
            i = 8/0
        except Exception:
            logger.error(traceback.format_exc()) # 仅在异常发生时输入简要的调用栈信息(format_exc输出简要的调用栈信息)

    注意事项

    loggers的level与handlers中的level:需要先满足loggers的level要求,进而满足handlers的level要求的日志才会被打印。

     

    路由

     路由器:

     按照列表的书写顺序进行匹配的。

     从上到下匹配,没有最优匹配的概念

     路由规则编写

     通常直接指定以^开头

     在结尾处直接添加反斜线

     配置流程

     settings中指定根级url配置文件,对应的属性ROOT_URLCONF=’项目名.urls‘

     urlpatterns:一个url实例的列表,全都url需要注册在根配置urls中

     导入其他应用的urls配置

    from django.conf.urls import include
    urlpatterns = [
        url(r'^xxx/',include('app.urls'))
    ]

    路由传参

    from django.urls import include, path
    
    urlpatterns = [
        path('index/', views.index, name='main-view'),
        // 位置参数:bio/XXX/  与顺序有关【参数个数必须和视图函数中参数个数一致】
        path('bio/<username>/', views.bio, name='bio'),
        // 关键字参数:bio/username=XXX/  与顺序无关【参数个数必须和视图函数中参数个数一致】
        // P表示parmes
        path(r'^bio/(?P<username>w+)/$', views.bio, name='bio'),
       path('articles/<slug:title>/', views.article, name='article-detail'),
       path('articles/<slug:title>/<int:section>/', views.section, name='article-section'),
       path('weblog/', include('blog.urls')),
        ...
    ]
    
    
    // 前端使用
    <a href="bio/{{ user.username }}/">aa</a>

    反向解析

    前端访问使用后端指定的namespace+name锁定一个路由地址,而不在是固定写死的url,好处就是,如果修改了配置的url的话,无需动态修改页面中的url地址

    // 根项目的urls
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path(r'demo/', include('demo.urls'), namespace='demo'),
        path('admin/', admin.site.urls),
    ]
    
    // app的urls
    from django.conf.urls import url
    from demo import views
    
    urlpatterns = [
        url(r'index/', views.index, name='index')
    ]
    // 使用反向解析要加app_name属性
    app_name = 'demo'
    
    // 前端访问:
    <a href="{% url 'demo:index' %}">到index页面</a>
    // 如果带参
    urlpatterns = [
        url(r'index/(d+)', views.index, name='index'),
        url(r'index/(?P<id>d+)', views.index, name='index')
    ]
    app_name = 'demo'
    
    
    // 使用规则:
    // {% url 'namespace.name' 参数1 参数2 ... %}
    // {% url 'namespace.name' key1=value1 key2=value2 key3=value3 %}
    
    // 带参数的传递一个id为10的,相当于index/10
    <a href="{% url 'demo:index' 10 %}">到index页面</a>
    // 带参数的传递一个id为10的,相当于index/?id=10
    <a href="{% url 'demo:index' id=10 %}">到index页面</a>
    // 后端使用反向解析(一般重定向时使用):
    from django.urls import reverse
    
    def redir(request):
        //用法: reverse('namespace:name')
        url = reverse('demo:models')
        return HttpResponseRedirect(url)
        
        
        
    // 带参数的反向解析
    // 位置参数:reverse('namespace:name',args=(value1,value2,value3))
    // 关键字参数:reverse('namespace:name',kwargs={key1:value1,key2:value2})

     

    视图

     

    视图函数

     FBV

     function base view【基于函数的视图函数,我常用的】

     CBV【更适合前后端分离时使用】

     class base view【基于类的视图函数】

     优点是:基于面向对象设计理念,可以使用继承

     

    类视图

     CBV

     继承自View

     urls中注册时使用:views.类.as_view()

     as_view()

     不能使用请求方法的名字作为参数名字

     只能接受已经存在的属性对应的参数[定义在该视图类中的属性]

    # urls
    urlpatterns = [
        url(r'^getbooks/', views.BookController.as_view(msg='hello'), name='getbooks')
    ]
    
    # views
    from django.http import HttpResponse
    from django.views import View
    
    class BookController(View):
        msg = None
    
        def get(self, request):
            """
                访问后页面返回hello
            """
            return HttpResponse(self.msg)

     定义了一个view

     创建了一个类视图对象

     保留,拷贝传递进来的属性和参数

     调用dispatch方法

     分发

     如果请求方法在我们允许的列表中

     从自己这个对象中获取请求方法名字小写对应的属性,如果没找到,会给1一个默认http_method_not_allowed

     如果请求方法不在我们的列表中,直接就是http_method_not_allowed

     之后将参数传递,调用函数

     默认实现了options

     获取接口信息,可以获取接收都允许的请求方式。

     

     

    TemplateView

    # urls
    urlpatterns = [
        # as_view中参数即为TemplateView父类的TemplateResponseMixin
        url(r'^getbooks/', views.BookTemplateView.as_view(template_name='hello.html'), name='getbooks')
    ]
    
    # views
    class BookTemplateView(TemplateView):
        """
            继承自TemplateView,访问即可渲染html
        """
        pass

     TemplateView

     多继承的子类

     继承

     View

     分发

     函数dispatch

     ContextMixin

     接收上下文

     从视图函数传递到模板内容

     函数get_context_data

     TemplateResponseMixin

     将内容渲染到模板中

     template_name

     template_engine

     response_class

     content_type

     函数 render_to_response

     

    ListView

    # urls
    urlpatterns = [
        url(r'^getbooks/', views.BookDataListView.as_view(), name='getbooks')
    ]
    
    # views
    from django.views.generic import ListView
    
    from book.models import Book
    
    
    class BookDataListView(ListView):
        model = Book
        template_name = 'book.html'
        
    # models
    class Book(models.Model):
        name = models.CharField(max_length=12)
        
    # templates
    {% for book in object_list %}
        <p>{{ book.name }}</p>
    {% endfor %}

    视图响应

     视图的响应分为两大类:

     json数据形式返回

     以网页形式返回

     重定向到另一个网页

     错误视图(40X,50X)

     

    HttpRequest

    GET和POST都是QueryDict类字典结构

    // 类字典结构 不会拿出所有,只会拿最后一个 
    // { 'hobby' :['k1','k2','k3'] }
    request.GET.get('hobby') // 只能拿到hobby的k3
    request.GET.getlist('hobby') // 可以拿到一个hobby的所有
    
    // 内置属性:
    method、path、POST、GET、META(各种客户端的元信息)
    encoding、FILES、COOKIES、session、is_ajax()

    HttpResponse

    // 内置属性
    content 返回内容
    charset 编码格式
    status_code 响应状态码
    content-type MIME类型
    
    // 方法
    init 初始化方法
    write(xxx) 直接写出文本
    flush() 冲刷缓冲区
    set_cookie(key,value,max_age=None,exprise=None)
    delete_cookie(key) 删除cookie

    子类

    HttpResponseRedirect

    响应重定向:可以实现服务器内部跳转

    return HttpResponseRedirect('/models/nomodel/')
    // 也可以简写成
    return redirect('/models/nomodel/')

    使用的时候推荐使用反向解析。

    from django.http import HttpResponse,HttpResponseRedirect
    from django.urls import reverse
    from django.shortcuts import redirect
    
    def get_method(request):
        url = reverse('demo:index')
        return HttpResponseRedirect(url)
      //return redirect(url)

    JsonResponse

    返回Json数据的请求,通常用在异步请求上

    from django.http import JsonResponse
    
    def get_method(request):
        url = reverse('demo:index')
        return JsonResponse(dict)
        
    //也可以使用__init__(self,data)设置数据
    Content-type是:application/json

     

    Cookie

    // 加密
    response.set_signed_cookie('name','maomao','slat')

    Session

    Django中Session过期时间默认为14天

     

    session操作

    // 登陆退出时使用,同时清空session和cookie
    request.session.flush()
    
    // 根据键获取会话的值
    request.session.get(key,default)
    
    // 清楚所有会话信息,但是当前sessionid依然存在
    .clear()
    
    // 删除会话
    delete request['session_id']
    
    // 获取session的key
    session.session_key
    
    // 设置session
    request.session['xxx'] = 'xx'
    // 数据存储到数据库中会进行编码十一点饿是Base64

    Token

    // 服务端会话技术
    // 自定义session
    // 如果Web页面开发中,使用起来和Session基本一致
    // 如果使用在移动端或客户端开发中,通常以Json形式传输,需要移动端自己存储Token,需要获取
    // Token关联数据的时候,主动传递Token
    
    // 步骤:1、在用户表中创建token字段,用于存储token
    // 2、用户登陆时,创建唯一Token值,存储在用户字段中
    // 3、用户退出时,删除用户的Token值
    
    // Cookie和Session,Token对比
        // Cookie使用更简洁,服务器压力更小,数据不是很安全
        // Session服务器要为啥Session,相对安全
        // Token拥有Session的所有优点,自己维护略微麻烦,支持更多的终端

    Django缓存

    官网

    流程

     未使用缓存

     请求到视图后,视图内的orm直接查询数据库,然后将数据返回。

     使用缓存

     请求到视图后,先去缓存里找,找该请求(针对所有用户)或者请求的用户(针对当前用户)请求当前页面的数据,如果找到了,直接返回缓存里的数据。如果没找到在去查找数据库,查找完后进行两步操作:

     将查询后的数据存入缓存中

     将查询后的数据返回给页面

     

     

    常见缓存

     Django内置了缓存框架,并提供了几种常用的缓存

     基于Memcached缓存

     优点

     Django原生支持的最快高效的缓存框架

     基于内存的缓存

     支持分布式

     缺点

     基于内存的缓存,内存中的数据可能丢失,一旦服务器崩溃,数据就没了

     解决方案

     对于一些交易类的抢购等,你要考虑使用基于内存的缓存带来的灾难,所以不去依赖基于内存的缓存作为您的唯一数据存储,Django内部支持多缓存框架的,对于交易类的你可以使用非内存缓存,对于普通数据类,你可以使用基于内存的缓存。

     使用数据库进行缓存

     优点

     数据不会因为宕机而丢失,但是速度比基于内存的缓存要慢

     使用文件系统进行缓存

     使用本地内存进行缓存

     提供缓存扩展接口(redis)

     操作浏览器或其他应用去缓存(下游缓存)

     可以缓存哪些内容?【缓存的内容越少越好】

     缓存整个网站

     缓存一个页面(某个视图函数)

     模板片段缓存

     更细粒度的缓存【手动调用cache设置键值】

     字符串

     列表

     字典等

     

    缓存操作

     cache.set

     key

     value

     timeout

     get

     add

     get_or _set

     get_many

     set_many

     delete

     delete_many

     clear

     incr 增加

     incr(key, value) key对应的值上添加 value

     decr 减少

     decr(key, value) key对应的值上减少value

     如果value不写,默认变更为1

     

     

    案例

    使用数据库进行缓存

    // settings.py配置
    CACHES = {
        'default': {
            'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
            #'LOCATION': 'my_cache_table',
            'LOCATION': 'django_cache',
        }
    }
    
    // 配置案例:
    // CACHES = {
    //    'default': {
    //        'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
    //        'LOCATION': 'my_cache_table',
    //        'TIMEOUT': '60',
    //        'OPTIONS': {
    //            'MAX_ENTRIES': '300',
    //        },
    //        'KEY_PREFIX': 'suit',
    //       'VERSION': '1',
    //   }
    // }
    // 根据我们的配置去创建这么一张表,执行以下命令
    python manage.py createcachetable

    查看数据库表,发现多了一张django_cache表后,然后我们就可以直接使用缓存了。

    from django.views.decorators.cache import cache_page
    
    @cache_page(60 * 3) // 缓存时间是3分钟
    def cache_db(request):
        """
        缓存某个视图
        :param request: 
        :return: 
        """
        context = {
            'data': ['news' + str(x) for x in range(10)]
        }
        sleep(5)
        return render(request, 'cache_page.html', context=context)
        
        
    def cache_db_by_hand(request):
        """
        手动缓存细粒度的数据
        :param request:
        :return:
        """
        cache_context = cache.get('cache_context')
        if cache_context:
            return render(request, 'cache_page.html', context=cache_context)
    
        context = {
            'data': ['news' + str(x) for x in range(10)]
        }
        sleep(5)
        cache.set('cache_context', context, 60)
        return render(request, 'cache_page.html', context=context)
    def cache_db_by_template(request):
        """
        前端模板缓存
        :param request:
        :return:
        """
        context = {
            'data': ['news' + str(x) for x in range(10)]
        }
        # context['data'].append(10)
        sleep(5)
        return render(request, 'cache_page.html', context=context)
    
    
    // 前端模板缓存
    {% load cache %}
    {% cache 60 ul %} {# ul只是一个名称,相当于cache中的键 #}
        <ul>
            {% for d in data %}
                <li>{{ d }}</li>
            {% endfor %}
        </ul>
    {% endcache %}
    
    // 访问页面你会发现页面没有展示10
    // 然后立刻将代码10行的注释取消,reload项目,
    // 再次刷新页面访问还是没有10,然后去查看数据库超时时间,等超时后
    // 再次刷新页面访问,此时出现了10这条数据。
    
    // 注意页面缓存只是缓存片段,但是请求依旧是要到前端执行的,而且数据也被更新了!
    // 只是页面没更改过来而已:
    {% load cache %}
    {% cache 60 ul %} {# ul只是一个名称,相当于cache中的键 #}
        <ul>
            {% for d in data %}
                <li>{{ d }}</li>
            {% endfor %}
        </ul>
    {% endcache %}
    
    <hr>
    <ul>
        {% for d in data %}
            <li>{{ d }}</li>
        {% endfor %}
    </ul>

    如上我们使用一段缓存一段不缓存,刷第一次展示结果是一样的,开启后台第十行注释,然后在刷:

    此时缓存时间还没过,你会发现,模板缓存并不是真正的查询缓存,它并不能帮我们提示很高的效率,只是在渲染页面时快那么一丢丢。

     

    redis缓存

    参见:https://django-redis-chs.readthedocs.io/zh_CN/latest/#cache-backend

    先安装好redis,Django使用redis 做缓存是第三方扩展了缓存接口实现的,有两个包可以选择,二者选其一吧

    - 推荐使用,官网:https://django-redis-chs.readthedocs.io/zh_CN/latest/#django
    pip install django-redis
    
    - 官网:https://pypi.python.org/pypi/django-redis-cache/
    // pip install django-redis-cache

    settings中配置:

    CACHES = {
        "default": {
            "BACKEND": "django_redis.cache.RedisCache",
            "LOCATION": "redis://127.0.0.1:6379/1",
            "OPTIONS": {
                "CLIENT_CLASS": "django_redis.client.DefaultClient",
            }
        }
    }

    启动redis服务

    # 启动redis
    liuwei@liuwei-virtual-machine / $ /usr/local/soft/redis-5.0.5/src/redis-server
    
    # 启动redis cli测试是否启动
    liuwei@liuwei-virtual-machine ~ $ /usr/local/soft/redis-5.0.5/src/redis-cli 
    127.0.0.1:6379> ping
    PONG
    // 测试redis缓存
    @cache_page(60)
    def cache_db(request):
        context = {
            'data': ['news' + str(x) for x in range(10)]
        }
        sleep(5)
        return render(request, 'cache_page.html', context=context)

    打开redis,查看数据是否存入:

    - 选择1号库
        select 1
    
    - 查看所有的key
        keys *
        - 即可查看到所有缓存信息,缓存超时后,redis内的数据将被自动清除
    127.0.0.1:6379[1]> select 1
    127.0.0.1:6379[1]> keys *
    1) ":1:views.decorators.cache.cache_header..cf4c4a88910e57cbd31a3b501aeeb91f.zh-hans"
    2) ":1:views.decorators.cache.cache_page..GET.cf4c4a88910e57cbd31a3b501aeeb91f.d41d8cd98f00b204e9800998ecf8427e.zh-hans"
    # 超时后再查
    127.0.0.1:6379[1]> keys *
    (empty list or set)

    AOP中间件

    基本使用

     在项目的根目录下新建文件夹middleware

     middleware内创建自己的中间件拦截器(.py文件)

     在创建的拦截器文件中定义一个拦截器类,该类继承自MiddlewareMixin,混合中间件

     Django中内置的AOP切点的位置

     process_request:在浏览器的request请求到urls分发器之间的位置

     process_view:在urls分发器和视图views之间

     process_template_response:在views视图和templates之间

     process_response:在templates和浏览器之间

     process_exception:发生异常时调用

     执行顺序

     按照settings中书写顺序调用

     如果其中某个中间件没有在切点处进行返回,则依次执行,否则的话之间返回,后面的中间件不在执行。

    案例

    # 创建middleware文件夹,创建AOP的py文件,定义继承自MiddlewareMixin
    from django.http import HttpResponse
    from django.utils.deprecation import MiddlewareMixin
    
    
    class CountMiddleWare(MiddlewareMixin):
        def process_request(self, request):
            print(request)
    
        def process_view(self, request, methods, args, *kwargs):
            print('process_view:', request)
            print('process_view:', methods)
            print('process_view:', args)
            print('process_view:', *kwargs)
    
        def process_template_response(self, request, response):
            print("process_template_response:", request)
            return response
    
        def process_response(self, request, response):
            print('process_response:', request)
            print('process_response:', response)
            return response
    
        def process_exception(self, request, exception):
            # 只捕获views里抛出异常
            print("process_exception:", request)
            print("process_exception:", exception)
            return HttpResponse("hello")
    // 在settings中配置中间件
    MIDDLEWARE = [
        'middleware.count.CountMiddleWare',
        'django.middleware.security.SecurityMiddleware',
        ...
    ]

    这样我们的中间件就开启起作用了。

    访问我们的index测试:

    def index(request):
        return render(request, 'index.html')  
        
    //  输出结果如下:
    process_request: <WSGIRequest: GET '/demo/index/'>
    process_view: <WSGIRequest: GET '/demo/index/'>
    process_view: <function index at 0x000001E7197FC488>
    process_view: ()
    process_view: {}
    process_response: <WSGIRequest: GET '/demo/index/'>
    process_response: <HttpResponse status_code=200, "text/html; charset=utf-8">

    你会发现,这个过程中并没有走我们的模板这一块,这是因为要想对视图进行增强(Advice),需要你views中返回的response对象具有一个render函数:

    def index(request):
        resp = HttpResponse()
        context = {}
    
        def render():
            return django.shortcuts.render(request, 'index.html', context=context)
    
        # 或者
        # def render():
        #     template = get_template('index.html')
        #     content = template.render(context, request)
        #     return HttpResponse(content)
    
        resp.render = render
        return resp
        
    // 再次访问index,返回结果如下
    process_request: <WSGIRequest: GET '/demo/index/'>
    process_view: <WSGIRequest: GET '/demo/index/'>
    process_view: <function index at 0x0000019534F6C488>
    process_view: ()
    process_view: {}
    process_template_response: <WSGIRequest: GET '/demo/index/'>
    process_response: <WSGIRequest: GET '/demo/index/'>
    process_response: <HttpResponse status_code=200, "text/html; charset=utf-8">

    异常测试,这里的异常捕获是全局异常捕获:

    def index(request):
        temp = 10 / 0
        return render(request, 'index.html')
        
    // 访问:输出结果如下
    process_request: <WSGIRequest: GET '/demo/index/'>
    process_view: <WSGIRequest: GET '/demo/index/'>
    process_view: <function index at 0x000001659D37C488>
    process_view: ()
    process_view: {}
    process_exception: <WSGIRequest: GET '/demo/index/'>
    process_exception: division by zero
    process_response: <WSGIRequest: GET '/demo/index/'>
    process_response: <HttpResponse status_code=200, "text/html; charset=utf-8">
    // 页面显示内容:
    division by zero

    这里我们的process_view里并没有参数,参数需要从前端路由传递时才会打印出结果。

     

     

    更多使用

     实现统计功能

     统计IP

     统计浏览器

     实现权重控制(例如:抢票)

     黑名单

     白名单

     实现反爬

     反爬虫

     十秒内只能搜索一次

     一分钟内最多访问10次

     实现频率控制

     

    案例:一分钟内最多访问十次

    从每次访问的当前时间往前推算一分钟内是否访问超过了十次,如果超过就拒绝访问。

    def process_request(self, request):
        ip = request.META.get("REMOTE_ADDR")
        date_list = cache.get(ip, [])
        # 先将超过当前时间的时间移除
        now = time.time()
        while date_list and now - date_list[-1] > 60:
            date_list.pop(-1)
    
        if len(date_list) > 10:
            return HttpResponse("请求过于频繁,请稍后再试")
    
        date_list.insert(0, now)
        cache.set(ip, date_list, 60)
        return HttpResponse("这是news")

    细想:一分钟访问10次也就相当于每6秒访问一次,那不就可以永久性的访问的?

     

    内置middleware

    @csrf_exempt
    def abc(request):
    // 表示豁免,就是说这个方法abc不进行csrf验证

    分页

     django提供了分页的工具,存在于diango.core中

     Paginator

     数据分页工具

     Page

     具体的某一个页面

     

     

    使用【参见官网】:

    - 创建分页对象
        Paginator(数据集,每一页数据数)
            - 属性
                count 对象总数
                num_pages 页面总数
                page_range 页码列表,从1开始
                
            - 方法
                page(整数) 获得一个page对象

    邮件

    django内置了邮件发送,除了发送普通文本外,还可以发送HTML元素。发送邮件需要指定发送方的邮箱地址(如果使用SMTP还需指定端口等)、接收方的邮箱地址(可以是多个)。

    - 具体使用
        - 在settings中配置邮件服务器地址,以及端口,以网易邮箱为例
            EMAIL_HOST = 'smtp.163.com'
            EMAIL_PORT = 25 # 非ssl
            
        - 在settings中配置验证邮箱服务器的账号和授权码
            EMAIL_HOST_USER = '18621651706@163.com'
            EMAIL_HOST_PASSWORD = 'liuwei123'  # 这个授权码需要自己到网易上开通,在设置-POP3/SMTP/IMAP中-客户端授权密码,开启即可。授权密码仅具有有限权限的操作:发邮件   
    
    
    - 发送邮件
        from django.core.mail import send_mail
        def send_email(request):
            """
                默认会读取settings中配置文件中设置的以上信息,发送邮件。
            """
            send_mail(
                subject='Hello',
                message='', # 发送普通文本
                html_message='<h1>I love you!</h1>',# 发送HTML
                from_email='18621651706@163.com',
                recipient_list=['18621651706@163.com'],
                fail_silently=False,
            )
            return HttpResponse('<h1>发送成功!</h1>')

    邮件发送的原理:

    邮件发送并不是你直接发送给接收方的,而是由你发送给服务器,然后服务器转发给接收方的,所以也就是上面为什么需要配置邮件服务器的地址和端口。

     

    需求:用户注册后,需要发送邮件激活账号。

    方案:在用户表中存储一个激活的字段,注册后默认为未激活,然后将用户的uuid【转为字符串之后的-uuid.hex】作为参数,拼接在激活的url之后。邮件的内容即为:点击该url地址,查询该用户将该用户激活。注意uuid.hex转为字符串后(移除了-),是没办法直接作为查询条件查询数据库的,而且激活也要有个时间限制,所以我们使用缓存,将uuid.hex作为键,uuid作为值,缓存时间为一天,当用户去点击激活时,就去缓存中查找uuid.hex对应的键,找到了就激活,没找到就提示激活邮件过期,重新发邮件激活。

     

     

    消息

    当你在后台做完一件事之后,总要给用户一些提示,这种不跳转页面的提示我们可以直接在操作最后返回HttpResponse对象封装提示信息即可。但是如果在redirect之后呢?查阅官方的redirect方法发现它是可以传递一些参数的,也就是说这种情况下也不一定非要搭建一个消息框架。如果是为了将业务逻辑区分开呢?视图中返回的仅仅是和业务逻辑相关的数据,消息仅仅作为一种状态的话,单独的将消息封装起来是有必要的。那什么时候真实迫切的需要消息框架呢?如果服务器想主动给客户端推送信息怎么办?消息队列又是干啥的?

     

     

    使用Django Shell

    //在django项目中直接使用命令:
    python manage.py shell

    会启动一个shell的交互环境,这个交互环境不仅仅只是python环境还有我们当前项目的django环境,你可以直接在shell命令行闸中使用:

    from xx import xx
    // 或调用当前django环境里的一些模块
    Student.objects.all()
    // 可以很方便我们调试一些数据

    settings配置

    DEBUG = False
    
    STATIC_URL = '/static/'
    # STATIC_ROOT = os.path.join(BASE_DIR, "static")
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, "static"),
        os.path.join(BASE_DIR, "structfront/dist/static"),
    ]
    
    LANGUAGE_CODE = 'zh-hans'
    
    # 设置时区为中国的,不然存储在库中的日期会和当前日期有偏差
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    # 关闭django默认时区,更改日期格式
    USE_L10N = False
    DATE_FORMAT = 'Y-m-d'
    DATETIME_FORMAT = 'Y-m-d H:i:s'
    
    USE_TZ = False
    
    # 配置文件上传最大支持
    DATA_UPLOAD_MAX_NUMBER_FIELDS = 10240

    debug=False时,Django将不再为您处理静态文件访问,启动项目会访问不到静态文件,你应该使用生产服务器(Nginx或Apache)去处理,如果你仍想使用本地服务器使用以下命令启动:

    python manage.py runserver --insecure

    Django前端调试工具

    - API
        https://django-debug-toolbar.readthedocs.io/en/latest/installation.html
    
    - 安装
        pip install django-debug-toolbar
        
    - 使用的前置条件
        INSTALLED_APPS = [
            # ...
            'django.contrib.staticfiles',
            # ...
            'debug_toolbar',
        ]
    
        STATIC_URL = '/static/'
        
    - 配置根目录下二点urls
        from django.conf import settings
        from django.conf.urls import include, url  # For django versions before 2.0
        from django.urls import include, path  # For django versions from 2.0 and up
        
        if settings.DEBUG:
            import debug_toolbar
            urlpatterns = [
                path('__debug__/', include(debug_toolbar.urls)),
        
                # For django versions before 2.0:
                # url(r'^__debug__/', include(debug_toolbar.urls)),
        
            ] + urlpatterns
            
            
    - 启用middleware,推荐置前
        MIDDLEWARE = [
            # ...
            'debug_toolbar.middleware.DebugToolbarMiddleware',
            # ...
        ]
        
    - settings中配置ip
        INTERNAL_IPS = [
            # ...
            '127.0.0.1',
            # ...
        ]

    访问页面即可看到:

    Django包结构拆分

    当一个项目很大时,我们把所有的内容都写着一个views或model中,这显然是不合理的,如果内容不太多也没必要单独用包拆分。python中也有包的概念,我们可以使用包的形式,将views中的内容拆分。然后使用插拔的方式,将urls中所需引入的views接口,引入在views包下的__init__.py文件中。

     views文件转为一个包文件:views右击-> Refactor -> Convert to python package

     此时views文件原有的内容会全部移到__init__.py中,我们只需创建一些列controller来单独封装某个业务逻辑即可。

     然后清空__init__.py,只在其中按需引入一些controller模块即可。

     

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    oracle中文乱码问题
    并发登录查询
    AJAX 笔记
    jQuery笔记
    js BOM 笔记
    HTML DOM笔记
    JS函数笔记
    js笔记
    json笔记
    css3笔记
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/13463013.html
Copyright © 2020-2023  润新知