Django常用知识点

 

1,命令行语句

  http://www.cnblogs.com/wupeiqi/articles/5237704.html

  创建djangoproject:django-admin.py startproject sitename

  创建app:python manage.py startapp appname

  运行django server:python manage.py runserver 0.0.0.0:8000

  (python manage.py createsuperuser    创建超级用户,登陆admin时使用)


2,数据库配置及命令:

    a,创建数据库

    b, 配置文件settings(django中)里DATABASES连接数据库,并将app添加到INSTALLED_APPS  ('app.apps.AppConfig')    

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'blog',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '',
        'PORT': '',
     #'CHARSET':'utf8',
     #'COLLATION':'utf8_general_ci',
     #注意有中文时设置该utf8格式,同时建立数据库时:CREATE DATABASE mydb DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
} }

    c, 文件model里创建类,继承models.Model类
          静态字段 = models.CharField(max_lengtth=50)

    d, 执行下面命令,创建表(每次更新表时需要执行)
          python manage.py makemigrations
          python manage.py migrate 

       (python manage.py sqlall appname     能打印出models中新建表对应的sql语句?)

3,静态文件路径配置及引用

  a,首先保证 'django.contrib.staticfiles'安装:
        INSTALLED_APPS = [
          'django.contrib.admin',
          'django.contrib.auth',
          'django.contrib.contenttypes',
          'django.contrib.sessions',
          'django.contrib.messages',
          'django.contrib.staticfiles',
          'rbac.apps.RbacConfig'
       'app01.apps.App01Config'],

     b,然后设置STATICFILES_DIRS:
             STATIC_URL = '/static/'
          STATICFILES_DIRS = [
              os.path.join(BASE_DIR,'static')  ]
       c, 最后可以引用jQuery:
      <script type="text/javascript" src="/static/jquery-3.3.1.js"></script> 
4. url路由配置

include()函数

动态路由参数

命名

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^register/', views.register),
    url(r'^blog/', include('blogHome.urls')),    #引入blogHome.urls中的路由规则,url中blog/后面的部分由blogHome.urls进行匹配
    url(r'^article/comment_tree/(\d+)/',views.getCommentTree),  #(\d+)匹配数字作为视图函数的参数传入
    url(r'^get_valid_img.png/', views.get_valid_img),
    url(r'^pc-geetest/register', views.pcgetcaptcha, name='pcgetcaptcha'), #
    url(r'^media/(?P<path>.*)$', serve, {"document_root": settings.MEDIA_ROOT}),
    url(r'(\w+)/article/(\d+)/',views.getArticle),
]

url反向解析

https://www.jianshu.com/p/e69b1a86c94b

使用:在定义url 时为include 定义namespace 属性,为url 定义name 属性
在模板中使用url 标签:{% url 'namespace_value:name_value'%}
在视图中使用reverse 函数:redirect(reverse('namespce_value:name_value’))

带参数的设置:<a href="{% url 'booktest:fan2' 2 3 %}">位置参数</a>

url反向解析
使用场景:模板中的超链接,视图中的重定向
使用:在定义url 时为include 定义namespace 属性,为url 定义name 属性
在模板中使用url 标签:{% url 'namespace_value:name_value'%}
在视图中使用reverse 函数:redirect(reverse('namespce_value:name_value’))
根据正则表达式动态生成地址,减轻后期维护成本。
注意反向解析传参数,主要是在我们的反向解析的规则后面天界了两个参数,两个参数之间使用空格隔开:<a href="{% url 'booktest:fan2' 2 3 %}">位置参数</a>



1)在项目urls.py中为include定义namespace属性。
url(r'^',include('booktest.urls',namespace='booktest')),

2)在应用的urls.py中为url定义name属性,并修改为fan2。

url(r'^fan2/$', views.fan2,name='fan2'),

在模板中使用url标签做超链接
<a href="{%url 'booktest:fan2'%}">反向解析fan2</a>
视图函数中:
return redirect(reverse('booktest:fan2'))


带位置参数
url(r'^fan(\d+)_(\d+)/$', views.fan2,name='fan2'),
反向解析:<a href="{%url 'booktest:fan2' 2 3%}">fan2</a>
return redirect(reverse('booktest:fan', args=(2,3)))

带关键字参数
 url(r'^fan(?P<id>\d+)_(?P<age>\d+)/$', views.fan2,name='fan2'),
<a href="{%url 'booktest:fan2' id=100 age=18%}">fan2</a>
return redirect(reverse('booktest:fan2', kwargs={'id':110,'age':26}))
反向解析

 

7. django运行流程

    当访问 URL /hello/ 时,Django 根据 ROOT_URLCONF 的设置装载 URLconf 。 然后按顺序逐个匹配URLconf里的URLpatterns,直到找到一个匹配的。 当找到这个匹配 的URLpatterns就调用相关联的view函数,并把 HttpRequest 对象作为第一个参数。总结一下:

  1. 进来的请求转入/hello/.  

  1. Django通过在ROOT_URLCONF配置来决定根URLconf.

  1. Django在URLconf中的所有URL模式中,查找第一个匹配/hello/的条目。

  1. 如果找到匹配,将调用相应的视图函数

  1. 视图函数返回一个HttpResponse

    6. Django转换HttpResponse为一个适合的HTTP response, 以Web page显示出来

 

5.模板系统Template

5.1 模板变量

"The name of function is {{foo.bar}} ."

模板进行渲染时,上面字符窜{{foo.bar}}中的变量会被替代,其中句点查找规则可概括为: 当模板系统在变量名中遇到点时,按照以下顺序尝试进行查找:

  • 字典类型查找 (比如 foo["bar"]  

  • 属性查找 (比如 foo.bar
  • 方法调用 (比如 foo.bar() )

  • 列表类型索引查找 (比如 foo[bar] )

  系统使用找到的第一个有效类型。 这是一种短路逻辑

5.2 模板标签

{% if %}  { % endif %}

#没有elif,用嵌套if语句
{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% else %} <p>Get back to work.</p> {% endif %}
#支持and, or, not, 但不能同时使用
{% if athlete_list %} {% if coach_list or cheerleader_list %} We have athletes, and either coaches or cheerleaders! {% endif %} {% endif %}

{% ifequal %} {% endifequal %}  :只有模板变量,字符串,整数和小数可以作为 {% ifequal %} 标签的参数,不支持python的字典类型、列表类型、布尔类型。

{% ifnotequal %} {% endifnotequal %}

#支持else语句
{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

{% for %} {% endfor %}

#支持嵌套的for,也可以和if嵌套
{% for athlete in athlete_list %} <h1>{{ athlete.name }}</h1> <ul> {% for sport in athlete.sports_played %} <li>{{ sport }}</li> {% endfor %} </ul> {% endfor %}
#empty语句表示列表为空时的输出
{% for athlete in athlete_list %} <p>{{ athlete.name }}</p> {% empty %} <p>There are no athletes. Only computer programmers.</p> {% endfor %}

在每个`` {% for %}``循环里有一个称为`` forloop`` 的模板变量。这个变量有一些提示循环进度信息的属性,包括counter, counter0, revcounter,revcounter0,first,last等。(嵌套循环时,forloop.parentloop 是一个指向当前循环的上一级循环的 forloop 对象的引用,如forloop.parentloop.counter)

# forloop.counter循环的第几项
{% for item in todo_list %}
    <p>{{ forloop.counter }}: {{ item }}</p>
{% endfor %}

#forloop.first循环第一项是值为True
{% for object in objects %}
    {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
    {{ object }}
    </li>
{% endfor %}

5.3 过滤器

支持的过滤器参考:https://docs.djangoproject.com/en/2.1/ref/templates/builtins/

过滤器:模板过滤器是在变量被显示前修改它的值的一个简单方法,用管道符表示,支持连续的过滤处理

{{ my_list|first|upper }} #my_list中的第一项并大写
{{ value|date:"D d M Y" }} #按指定格式显示日期,格式参见https://docs.djangoproject.com/en/2.1/ref/templates/builtins/

前端时间格式化:create_date|date:"Y-m-d H:i:s"
后端时间格式化 create_date.strftime("%Y-%m-%d %H:%M:%S")

5.4模板存放位置

  当模板进行渲染时,会去settings.py文件中查看TEMPLATES目录设置(模板文件存放在templates文件夹下),然后在templates文件夹中寻找模板文件。

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')]
        ,
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

当templates文件夹中有子文件夹时,获取模板文件时加上子文件夹名称,后端获取如下:

#current_datetime.html存放在templates/dateapp文件夹下
t = get_template('dateapp/current_datetime.html') return render(request,'dateapp/current_datetime.html', {'current_date': now})

在网页前端中使用{% include %} 获取嵌套模板文件,如下:

{% include 'nav.html' %}
{% include "nav.html" %}
{% include 'dateapp/nav.html' %}

5.5 模板继承

  除了使用{% include %} 嵌套模板文件外,还可以通过模板继承来复用模板文件。

母模板

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>
base.html

子模板

{% extends "base.html" %}

{% block title %}The current time{% endblock %}

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}
View Code

  注意事项:

    {% extends "base.html" %} 必须放在第一行。

    {% block name %} {% endblock %}语句,在同一个模板文件中不能出现同名.

    {% block name %}<p>content</p> {% endblock %}, 对于其中间的内容,如果子模板不覆盖时,会按母模板内容显示

 5.6自定义tag和filter

http://www.liujiangblog.com/course/django/150

https://docs.djangoproject.com/zh-hans/2.0/howto/custom-template-tags/

         1. 首先得在相应的app下建立templatetags文件夹(包含__init__.py),再建立包含自定义tag或filter的代码文件。如下图中的poll_extras.py中定义自己的tag或filter。前端加载该文件格式: {% load poll_extras %}

    2.自定义filter    

#poll_extras.py文件内容

from django import template

register = template.Library()  #register名字固定

@register.filter(name='cut')   
def cut(value, arg):                 
    return value.replace(arg, '')

@register.filter
def lower(value):
    return value.lower()

#或者按如下注册
#register.filter('cut', cut)
#register.filter('lower', lower)


#前端调用
{{ somevariable|cut:"0" }}         # somevariable为需要过滤的字段,cut为自定义的filter名称,“0”为参数
 # somevariable对应上面cut函数中value,“0”对应arg
poll_extras.py

    3. 自定义tag:  simple_tag 和inclusion_tag

#simple_tag

@register.simple_tag(name='minustwo')
def some_function(value):
    return value - 2

@register.simple_tag
def my_tag(a, b, *args, **kwargs):
    warning = kwargs['warning']
    profile = kwargs['profile']
    ...
    return ...

{% my_tag 123 "abcd" book.title warning=message|lower profile=user.profile %}


#inclusion_tag
@register.inclusion_tag('results.html')
def show_results(poll):
    choices = poll.choice_set.all()
    return {'choices': choices}

#results.html
<ul>
{% for choice in choices %}
    <li> {{ choice }} </li>
{% endfor %}
</ul>

#前端使用
{% show_results poll %}
poll_extras.py

4,数据库model操作

https://docs.djangoproject.com/en/2.1/topics/db/models/

每一个model继承django.db.models.Model,对应数据库中一张表,model的每一个属性对应表中一列。

field的种类:https://docs.djangoproject.com/en/2.1/ref/models/fields/#field-types

<1> CharField
        #字符串字段, 用于较短的字符串.
        #CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.

<2> IntegerField
       #用于保存一个整数.

<3> FloatField
        # 一个浮点数. 必须 提供两个参数:
        #
        # 参数    描述
        # max_digits    总位数(不包括小数点和符号)
        # decimal_places    小数位数
                # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
                #
                # models.FloatField(..., max_digits=5, decimal_places=2)
                # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
                #
                # models.FloatField(..., max_digits=19, decimal_places=10)
                # admin 用一个文本框(<input type="text">)表示该字段保存的数据.

<4> AutoField
        # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 
        # 自定义一个主键:my_id=models.AutoField(primary_key=True)
        # 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.

<5> BooleanField
        # A true/false field. admin 用 checkbox 来表示此类字段.

<6> TextField
        # 一个容量很大的文本字段.
        # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框).

<7> EmailField
        # 一个带有检查Email合法性的 CharField,不接受 maxlength 参数.

<8> DateField
        # 一个日期字段. 共有下列额外的可选参数:
        # Argument    描述
        # auto_now    当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳.
        # auto_now_add    当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间.
        #(仅仅在admin中有意义...)

<9> DateTimeField
        #  一个日期时间字段. 类似 DateField 支持同样的附加选项.

<10> ImageField
        # 类似 FileField, 不过要校验上传对象是否是一个合法图片.#它有两个可选参数:height_field和width_field,
        # 如果提供这两个参数,则图片将按提供的高度和宽度规格保存.     
<11> FileField
     # 一个文件上传字段.
     #要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime #formatting, 
     #该格式将被上载文件的 date/time 
     #替换(so that uploaded files don't fill up the given directory).
     # admin 用一个<input type="file">部件表示该字段保存的数据(一个文件上传部件) .

     #注意:在一个 model 中使用 FileField 或 ImageField 需要以下步骤:
            #(1)在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. 
            # (出于性能考虑,这些文件并不保存到数据库.) 定义MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 
            #  WEB服务器用户帐号是可写的.
            #(2) 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django
            # 使用 MEDIA_ROOT 的哪个子目录保存上传文件.你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 
            # 出于习惯你一定很想使用 Django 提供的 get_<#fieldname>_url 函数.举例来说,如果你的 ImageField 
            # 叫作 mug_shot, 你就可以在模板中以 {{ object.#get_mug_shot_url }} 这样的方式得到图像的绝对路径.

<12> URLField
      # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在( 即URL是否被有效装入且
      # 没有返回404响应).
      # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框)

<13> NullBooleanField
       # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项
       # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据.

<14> SlugField
       # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.#它们通常用于URLs
       # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50.  #在
       # 以前的 Django 版本,没有任何办法改变50 这个长度.
       # 这暗示了 db_index=True.
       # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-#populate 
       # the slug, via JavaScript,in the object's admin form: models.SlugField
       # (prepopulate_from=("pre_name", "name"))prepopulate_from 不接受 DateTimeFields.

<13> XMLField
        #一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema #的文件系统路径.

<14> FilePathField
        # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的.
        # 参数    描述
        # path    必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. 
        # Example: "/home/images".
        # match    可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名.  
        # 注意这个正则表达式只会应用到 base filename 而不是
        # 路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif.
        # recursive可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录.
        # 这三个参数可以同时使用.
        # match 仅应用于 base filename, 而不是路径全名. 那么,这个例子:
        # FilePathField(path="/home/images", match="foo.*", recursive=True)
        # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif

<15> IPAddressField
        # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30").
<16># CommaSeparatedIntegerField
        # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有maxlength参数.
Field types

field的参数:https://docs.djangoproject.com/en/2.1/ref/models/fields/#field-options

<1> null : 数据库中字段是否可以为空

    <2> blank: django的 Admin 中添加数据时是否可允许空值

    <3> default:设定缺省值

    <4> editable:如果为假,admin模式下将不能改写。缺省为真

    <5> primary_key:设置主键,如果没有设置django创建表时会自动加上:
        id = meta.AutoField('ID', primary_key=True)
        primary_key=True implies blank=False, null=False and unique=True. Only one
        primary key is allowed on an object.

    <6> unique:数据唯一

    <7> verbose_name  Admin中字段的显示名称

    <8> validator_list:有效性检查。非有效产生 django.core.validators.ValidationError 错误


    <9> db_column,db_index 如果为真将为此字段创建索引

    <10>choices:一个用来选择值的2维元组。第一个值是实际存储的值,第二个用来方便进行选择。
                如SEX_CHOICES= (( ‘F’,'Female’),(‘M’,'Male’),)
                gender = models.CharField(max_length=2,choices = SEX_CHOICES)
Field options

  图片和文件上传路径(upload_to): 对于upload_to参数的路径是相对路径(相对于MEDIA_ROOT的设置值,如MEDIA_ROOT="media", upload_to="avatar/",则最后保存的路径为 media/avatar/);  

  自定义上传路径:upload_to可以接受一个函数user_directory_path,来对路径进行自定义(由于所有用户的文件都上传到一个文件夹,会出现同名文件夹的覆盖)

参考:
https://docs.djangoproject.com/en/2.1/ref/models/fields/#django.db.models.FileField.upload_to
https://blog.csdn.net/weixin_42134789/article/details/80753051


#示例一, user_directory_path函数必须接受两个参数,model实例和文件名
def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
    return 'user_{0}/{1}'.format(instance.user.id, filename)

class MyModel(models.Model):
    upload = models.FileField(upload_to=user_directory_path)



#示例二
from django.contrib.auth.models import User
import uuid

def user_directory_path(instance, filename):
   ext = filename.split('.')[-1]
   filename = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
   # return the whole path to the file
    return os.path.join(instance.user.id, "avatar", filename)

class UserProfile(models.Model):
   user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
   avatar = models.ImageField(upload_to=user_directory_path, verbose_name="头像")


#示例三
def user_directory_path(instance, filename):
   ext = filename.split('.')[-1]
   filename = '{}.{}'.format(uuid.uuid4().hex[:8], ext)
   sub_folder = 'file'
    if ext.lower() in ["jpg", "png", "gif"]:
       sub_folder = "avatar"
    if ext.lower() in ["pdf", "docx"]:
       sub_folder = "document"
    return os.path.join(instance.user.id, sub_folder, filename)
自定义上传路径

  另外对于用户上传的文件也可以在视图函数中进行重命名后再保存,来防止文件覆盖,如下:

@login_required
def ajax_avatar_upload(request):
    user = request.user
    user_profile = get_object_or_404(UserProfile, user=user)

    if request.method == "POST":
        form = AvatarUploadForm(request.POST, request.FILES)
        if form.is_valid():
            img = request.FILES['avatar_file']  # 获取上传图片
            cropped_avatar = crop_image(img, user.id)
            user_profile.avatar = cropped_avatar  # 将图片路径修改到当前会员数据库
         user_profile.save()
    return HttpResponseRedirect(reverse('myaccount:profile'))


def crop_image(file, uid):

    # 随机生成新的图片名,自定义路径。
   ext = file.name.split('.')[-1]
    file_name = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
    cropped_avatar = os.path.join(uid, "avatar", file_name)
    # 相对根目录路径
   file_path = os.path.join("media", uid, "avatar", file_name)

    # 裁剪图片,压缩尺寸为200*200。
   img = Image.open(file)
    crop_im = img.crop((50,50,300, 300)).resize((200, 200), Image.ANTIALIAS)
    crop_im.save(file_path)

    return cropped_avatar

参考:https://blog.csdn.net/weixin_42134789/article/details/80753051 
View Code

model之间的三种关系

一对多关系:django.db.models.Foreign

https://docs.djangoproject.com/en/2.1/ref/models/fields/#django.db.models.ForeignKey

多对多关系:django.db.models.ManyToManyField

https://docs.djangoproject.com/en/2.1/ref/models/fields/#django.db.models.ManyToManyField

一对一关系:django.db.models.OneToOneField

https://docs.djangoproject.com/en/2.1/ref/models/fields/#django.db.models.OneToOneField

 

model操作:增删改查

https://docs.djangoproject.com/en/2.1/topics/db/queries/

建立表:

from django.db import models<br>
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市',max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
 
    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    def __str__(self):
        return self.name
 
class Author(models.Model):
    name = models.CharField(max_length=30)
    def __str__(self):
        return self.name
 
class AuthorDetail(models.Model):
    sex = models.BooleanField(max_length=1, choices=((0, ''),(1, ''),))
    email = models.EmailField()
    address = models.CharField(max_length=50)
    birthday = models.DateField()
    author = models.OneToOneField(Author)
 
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title
View Code

每次创建一个对象,显示对应的sql语句,需要在settings.py中配置logging

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

LOGGING
logging

增:create, save

from app01.models import Author

    #create方式一:   author = Author.objects.create(name='Alvin') #自动创建并保存

    #create方式二:   author = Author.objects.create(**{"name":"alex"})

    #save方式一:     author=Author(name="alvin")
                            author.save()

    #save方式二:     author=Author()
                            author.name="alvin"
                             author.save()    
View Code

增加一对多和多对多数据:

#一对多(ForeignKey):

    #方式一: 由于绑定一对多的字段,比如publish,存到数据库中的字段名叫publish_id,所以我们可以直接给这个字段设定对应值:
           Book.objects.create(title='php',
                               publisher_id=2,   #这里的2是指为该book对象绑定了Publisher表中id=2的行对象
                               publication_date='2017-7-7',
                               price=99)


    #方式二:
       <1> 先获取要绑定的Publisher对象:
        pub_obj=Publisher(name='河大出版社',address='保定',city='保定',
                state_province='河北',country='China',website='http://www.hbu.com')
    或者 pub_obj=Publisher.objects.get(id=1)

        <2>将 publisher_id=2 改为  publisher=pub_obj

    #方式三: 分别获取两个对象,进行赋值
        book_obj=Book.objects.get(name='河大出版社')
        pub_obj = Publisher.objects.get(id=2)
        book_obj.publish = pub_obj
        book.save()


#多对多(ManyToManyField()):
  #正向添加,通过book表设置
        author1=Author.objects.get(id=1)
        author2=Author.objects.filter(name='alvin')[0]
        book=Book.objects.get(id=1)
        book.authors.add(author1,author2) #等同于: book.authors.add(*[author1,author2])
    删除多对多关系:book.authors.remove(*[author1,author2])
    
    #反向添加,通过authors表设置
    book=models.Book.objects.filter(id__gt=1)
    authors=models.Author.objects.filter(id=1)[0]
    authors.book_set.add(*book)
    删除多对多:关系authors.book_set.remove(*book)


#注意: 如果第三张表是通过models.ManyToManyField()自动创建的,那么绑定关系只有上面一种方式
#     如果第三张表是自己创建的:
     class Book2Author(models.Model):
            author=models.ForeignKey("Author")
            Book=  models.ForeignKey("Book")
#     那么就还有一种方式:
            author_obj=models.Author.objects.filter(id=2)[0]
            book_obj  =models.Book.objects.filter(id=3)[0]

            s=models.Book2Author.objects.create(author_id=1,Book_id=2)
            s.save()
            s=models.Book2Author(author=author_obj,Book_id=1)
            s.save()
View Code

删:delete

>>> Book.objects.filter(id=1).delete()   # 删除id=1的所有book数据,由于级联删除,其对应的外键关联记录也会被删除
(3, {'app01.Book_authors': 2, 'app01.Book': 1}) #返回删除记录数,及删除的记录对象

  删除一对多或多对多关系: remove和clear

https://docs.djangoproject.com/en/1.11/ref/models/relations/

#正向
book = models.Book.objects.filter(id=1)

#删除第三张表中和id=1关联的所有关联信息
book.author.clear()        #清空与book中id=1 关联的所有数据
book.author.remove(author_obj) 
book.author.remove(*obj)     #可以为列表,前面加*

#反向
author = models.Author.objects.filter(id=1)
author.book_set.clear() #清空与author中id=1 关联的所有数据

 改:update和save

https://docs.djangoproject.com/en/2.1/ref/models/querysets/#django.db.models.query.QuerySet.update

update

Publisher.objects.filter(id=3).update(name="zack") #filter不能替换为get
不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象


Book.objects.update(author__name='zack') # 会报错,不支持跨表更新,应该用下面filter跨表筛选再更新
Book.objects.filter(author__id=1).update(name="zack")

save

b = Book.objects.get(id=10)
b.title = "python"
b.save()

 save方法会更新一行里的所有列,update只更新选定列,若只需要更新一行里的某几列,update更加高效,如下代码:

#---------------- update方法直接设定对应属性----------------
    models.Book.objects.filter(id=3).update(title="PHP")
    ##sql:
    ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)


#--------------- save方法会将所有属性重新设定一遍,效率低-----------
    obj=models.Book.objects.filter(id=3)[0]
    obj.title="Python"
    obj.save()
# SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", 
# "app01_book"."color", "app01_book"."page_num", 
# "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; 
# 
# UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556,
# "publisher_id" = 1 WHERE "app01_book"."id" = 3;
update与save

 查:filter,all,exclude,get

https://docs.djangoproject.com/en/2.1/topics/db/queries/

https://docs.djangoproject.com/en/2.1/ref/models/querysets/#queryset-api

查询API:

# 查询相关API:

#  <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象,返回Queryset

#  <2>all():                 查询所有结果,返回Queryset

#  <3>get(**kwargs):         返回与所给筛选条件相匹配的model对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。

#-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()--------

#  <4>values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
                                     
#  <5>exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象

#  <6>order_by(*field):      对查询结果排序

#  <7>reverse():             对查询结果反向排序

#  <8>distinct():            从返回结果中剔除重复纪录

#  <9>values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

#  <10>count():              返回数据库中匹配查询(QuerySet)的对象数量。

# <11>first():               返回第一条记录

# <12>last():                返回最后一条记录

#  <13>exists():             如果QuerySet包含数据,就返回True,否则返回False。
View Code
#扩展查询,有时候DJANGO的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra:
#extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None

(1)  Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
(2)  Blog.objects.extra(
        select=SortedDict([('a', '%s'), ('b', '%s')]),
        select_params=('one', 'two'))

(3)  q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})
     q = q.extra(order_by = ['-is_recent'])

(4)  Entry.objects.extra(where=['headline=%s'], params=['Lennon'])  

extra
extra

查询数据排序:order_by

#根据单个字段排序
>>> Publisher.objects.order_by("state_province")
[<Publisher: Apress>, <Publisher: O'Reilly>]

#根据多个字段排序
>>> Publisher.objects.order_by("state_province", "address")
 [<Publisher: Apress>, <Publisher: O'Reilly>]

#逆序
>>> Publisher.objects.order_by("-name")
[<Publisher: O'Reilly>, <Publisher: Apress>]


#设置默认排序字段
class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()

    def __unicode__(self):
        return self.name

       class Meta:
          ordering = ['name']
View Code

QuerySet惰性机制

所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。

QuerySet特点:

       <1>  可迭代的

       <2>  可切片

#objs=models.Book.objects.all()#[obj1,obj2,ob3...]

    #QuerySet:   可迭代
    # for obj in objs:#每一obj就是一个行对象
    #     print("obj:",obj)

    # QuerySet:  可切片
    # print(objs[1])      #不支持objs[-1]
    # print(objs[1:4])
    # print(objs[1:10:2])
View Code

QuerySet的缓存机制:

<1>Django的queryset是惰性的

     Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
     到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
     上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
     这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。

<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.
   为了验证这些,需要在settings里加入 LOGGING(验证方式)
        obj=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)

        # if obj:
        #     print("ok")

<3>queryset是具有cache的
     当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
    (evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
     你不需要重复运行通用的查询。
        obj=models.Book.objects.filter(id=3)
        第一次遍历时,查询数据库
        # for i in obj:
        #     print(i)
                          
        第二次遍历时,从cache中得到,不查询数据库
        # for i in obj:
        #     print(i)   #LOGGING只会打印一次
    
       # models.Book.objects.filter(id=3).update(title="GO")??
       # obj_new=models.Book.objects.filter(id=3)??
<4>
     简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些
     数据!为了避免这个,可以用exists()方法来检查是否有数据:

            obj = Book.objects.filter(id=4)
            #  exists()的检查可以避免数据放入queryset的cache。
            if obj.exists():
                print("hello world!")

<5>当queryset非常巨大时,cache会成为问题

     处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
     进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
     来获取数据,处理完数据就将其丢弃。
        objs = Book.objects.all().iterator()
        # iterator()可以一次只从数据库获取少量数据,这样可以节省内存
        for obj in objs:
            print(obj.name)
        #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
        for obj in objs:
            print(obj.name)

     #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
     #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询

总结:
    queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。 
View Code

对象查询:注意反向查找跨表book_set

条件查询(单表条件查询,跨表条件查询):注意__的含义(book__title__contains)

#--------------------对象形式的查找--------------------------
    # 正向查找
    ret1=models.Book.objects.first()
    print(ret1.title)
    print(ret1.price)
    print(ret1.publisher)
    print(ret1.publisher.name)  #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合

    # 反向查找
    ret2=models.Publish.objects.last()
    print(ret2.name)
    print(ret2.city)
    #如何拿到与它绑定的Book对象呢?
    print(ret2.book_set.all()) #ret2.book_set是一个queryset集合

#---------------了不起的双下划线(__)之单表条件查询----------------

#    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
#
#    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
#    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
#
#    models.Tb1.objects.filter(name__contains="ven")
#    models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
#
#    models.Tb1.objects.filter(id__range=[1, 2])   # 范围bettwen and
#
#    startswith,istartswith, endswith, iendswith,

#----------------了不起的双下划线(__)之多表条件关联查询---------------

# 正向查找(条件)

#     ret3=models.Book.objects.filter(title='Python').values('id')
#     print(ret3)#[{'id': 1}]

      #正向查找(条件)之一对多

      ret4=models.Book.objects.filter(title='Python').values('publisher__city')
      print(ret4)  #[{'publisher__city': '北京'}]

      #正向查找(条件)之多对多
      ret5=models.Book.objects.filter(title='Python').values('author__name')
      print(ret5)
      ret6=models.Book.objects.filter(author__name="alex").values('title')
      print(ret6)

      #注意
      #正向查找的publisher__city或者author__name中的publisher,author是book表中绑定的字段
      #一对多和多对多在这里用法没区别

# 反向查找(条件)

    #反向查找之一对多:
    ret8=models.Publisher.objects.filter(book__title='Python').values('name')
    print(ret8)#[{'name': '人大出版社'}]  注意,book__title中的book就是Publisher的关联表名

    ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors')
    print(ret9)#[{'book__authors': 1}, {'book__authors': 2}]

    #反向查找之多对多:
    ret10=models.Author.objects.filter(book__title='Python').values('name')
    print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]

    #注意
    #正向查找的book__title中的book是表名Book
    #一对多和多对多在这里用法没区别
View Code
查询参数对照:
    operators = {      
          'exact': '= %s',    
          'iexact': 'LIKE %s',    
          'contains': 'LIKE BINARY %s',      
          'icontains': 'LIKE %s',      
          'regex': 'REGEXP BINARY %s',     
          'iregex': 'REGEXP %s',       
           'gt': '> %s',    
           'gte': '>= %s',   
          'lt': '< %s',    
          'lte': '<= %s',     
          'startswith': 'LIKE BINARY %s',     
          'endswith': 'LIKE BINARY %s',      
          'istartswith': 'LIKE %s',    
          'iendswith': 'LIKE %s',    }
常用条件查询参数对照 

聚合查询和分组查询

aggregate():通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。

from django.db.models import Avg,Min,Sum,Max

从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有
图书的集合。

>>> Book.objects.all().aggregate(Avg('price'))
{'price__avg': 34.35}

aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值

aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的
标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定
一个名称,可以向聚合子句提供它:
>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}


如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
View Code

annotate():可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合.

      

F查询和Q查询

F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。

# F 使用查询条件的值,专门取对象中某列值的操作
# from django.db.models import F 
# models.Tb1.objects.update(num=F('num')+1)

# 查询书籍id大于\小于其价格的书籍
models.Book.objects.filter(id__gt=F("price")) <QuerySet []> models.Book.objects.filter(id__lt=F("price")) <QuerySet [<Book: 书一>, <Book: 书二>, <Book: 书三>, <Book: 书四>, <Book: 书五>, <Book: 书六>]>


#Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作
models.Book.objects.filter(id__lt=F("price")/2)
<QuerySet [<Book: 书一>, <Book: 书二>, <Book: 书三>, <Book: 书四>, <Book: 书五>]>

#修改操作也可以使用F函数,比如将每一本书的价格提高30元
models.Book.objects.all().update(price=F("price")+30)

#Concat连接
from django.db.models.functions import Concat from django.db.models import Value models.Book.objects.update(title=Concat(F("title"),Value("("),Value(""),Value(")")))        #连接后为title=书名(第)
View Code

Q()构造查询条件

# Q 构建搜索条件
    from django.db.models import Q

    #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询
    q1=models.Book.objects.filter(Q(title__startswith='P')).all()
    print(q1)#[<Book: Python>, <Book: Perl>]

    # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。
    Q(title__startswith='P') | Q(title__startswith='J')

    # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合
    Q(title__startswith='P') | ~Q(pub_date__year=2005)

    # 4、应用范围:

    # Each lookup function that takes keyword-arguments (e.g. filter(),
    #  exclude(), get()) can also be passed one or more Q objects as
    # positional (not-named) arguments. If you provide multiple Q object
    # arguments to a lookup function, the arguments will be “AND”ed
    # together. For example:

    Book.objects.get(
        Q(title__startswith='P'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )

    #sql:
    # SELECT * from polls WHERE title LIKE 'P%'
    #     AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

  
    # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。
    # 正确:
    Book.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        title__startswith='P')
    # 错误:
    Book.objects.get(
        question__startswith='P',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
View Code
            del_objects = Q()
            for room_id, time_list in del_list.items():
                temp = Q()
                for time_id in time_list:
                    temp.children.append(('room_id',room_id))
                    temp.children.append(('time',time_id))
                    temp.children.append(('user',request.user))
                    temp.children.append(('date',choose_date))
                    del_objects.add(temp,'OR')
            if del_objects:
                models.Order.objects.filter(del_objects).delete()
View Code

原生sql语句Person.objects.raw('SELECT * FROM myapp_person'):

https://docs.djangoproject.com/en/2.1/topics/db/sql/

 6.auth认证系统和User model

https://docs.djangoproject.com/en/1.11/topics/auth/

https://docs.djangoproject.com/zh-hans/2.1/topics/auth/customizing/

https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html

#继承AbstractUser
from django.contrib.auth.models import AbstractUser

# Create your models here.

class UserInfo(AbstractUser):
    phone = models.CharField(max_length=11,null=True,unique=True)
    avatar = models.ImageField(upload_to='avatars/',default='avatars/default.png', verbose_name='头像')#储存在设置的MEDIA_ROOT下的avatars文件夹下
    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)
    def __str__(self):
        return self.username
    class Meta:
        verbose_name = '用户'
        verbose_name_plural = verbose_name

#在settings.py文件中进行设置
AUTH_USER_MODEL = "blogHome.UserInfo"

#用户注册时,创建用户   (注意是creat_user,不是creat)
from django.contrib import auth
def register(request):
    if request.method=='POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = models.UserInfo.objects.create_user(username=username,password=password)
        print user
        if user:
            return redirect('/login/')

    return render(request,'register.html')

#用户登录时进行认证  
def login(request):
    if request.method=='POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        print username,password
        user = auth.authenticate(username=username,password=password)
        print user
        if user:
            auth.login(request,user)   #auth.logout(request)注销
            return redirect('/order_list/')
    return render(request,'login.html')
View Code

7. django中间件

http://djangobook.py3k.cn/2.0/chapter17/

https://zhuanlan.zhihu.com/p/39275116

新建项目的settings.py中,django默认的中间件如下:

执行流程如下:    1. 有请求时,从上到下依次执行中间件的process_request方法,再从上到下依次执行process_view方法,

         2. 执行视图函数view,

         3. 从下到上依次执行中间件的process_template_response, 再从下到下依次执行process_response

         4.上述执行过程中出现异常,会从下至上依次执行中间件的process_exception

    

自定义中间件:
  1. 实现中间件六个方法中至少一个,分别是:(主要的是process_request和process_response)

  • __init__(self): 仅在服务进程启动的时候调用,而在针对单个request处理时并不执行。
  • process_request(self,request)
  • process_view(self, request, view_func, view_args, view_kwargs)
  • process_template_response(self,request,response)
  • process_exception(self, request, exception)
  • process_response(self, request, response)

  以上方法的返回值可以是None或一个HttpResponse对象,如果是None,则继续按django定义的规则向后继续执行,如果是HttpResponse对象,则直接将该对象返回给用户。

   2. 在settings.py文件中的MIDDLEWARE中进行配置,注意顺序。

#coding:utf-8

#rbac:role-based access control 基于角色的权限控制

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse,redirect
import re
#自定义中间件
class ValidPermission(MiddlewareMixin):

    def process_request(self,request):

        current_path = request.path  # 拿到当前请求路径  '/user/'

        #设置白名单,允许任何人访问的url
        valid_urls = ['/login/','/admin/(.*)']
        for url in valid_urls:
            path = '^%s$'%url
            ret = re.match(path, current_path)
            if ret:
                return None

        #对于没有登陆的用户重定向至登陆页面
        user_id = request.session.get('user',[])
        if not user_id:
            return redirect('/login/')

        #根据权限来匹配url,决定当前用户是否有访问权限
        permission_list = request.session['permission_list']
        # print permission_list,current_path
        for permission in permission_list.values():
            urls = permission['permission__url']
            for url in urls:
                path = '^%s$'%url
                ret = re.match(path,current_path)
                if ret:
                    request.actions = permission['permission__action']   #[u'list', u'edit', u'add']
                    # print request.actions
                    return None

        return HttpResponse('没有访问权限')
View Code

 8.django测试

在单元测试方面,Django 继承python 的unittest.TestCase 实现了自己的django.test.TestCase,编写测试用例通常从这里开始。测试代码通常位于app 的tests.py 文件中(也可以在models.py 中编写,一般不建议)。在Django 生成的depotapp 中,已经包含了这个文件,并且其中包含了一个测试。(测试工具:unittest 或者pytest)

  1. python manage.py test:执行所有的测试用例
  2. python manage.py test app_name, 执行该app 的所有测试用例
  3. python manage.py test app_name.case_name: 执行指定的测试用例

9. django的cookie和session

https://segmentfault.com/a/1190000016041458

Session 依赖于Cookie,如果浏览器不能保存cookie 那么session 就失效了。因为它需要浏览器的cookie 值去session 里做对比。session 就是用来在服务器端保存用户的会话状态。

      

设置cookie

rep = HttpResponse(...) 或 rep = render(request, ...)
rep.set_cookie(key,value,...)

def cookie_set(request):
    response = HttpResponse("<h1>设置Cookie,请查看响应报文头</h1>")
    response.set_cookie('h1', 'hello django')
    return response


# 给cookie签名
rep.set_signed_cookie(key,value,salt='加密盐',...)

    参数:

        key,              键
        value='',         值
        max_age=None,     超时时间
        expires=None,     超时时间(IE requires expires, so set it if hasn't been already.)
        path='/',         Cookie生效的路径,/ 表示根路径,特殊的:跟路径的cookie可以被任何url的页面访问
        domain=None,      Cookie生效的域名
        secure=False,     https传输
        httponly=False    True只能http协议传输,无法被JavaScript获取(不是绝对,底层抓包可以获取到也可以被覆盖),防止xss攻击
View Code

获取cookie

request.COOKIES[key]
request.COOKIES.get(key)
# 普通cookie是明文传输的,可以直接在客户端直接打开,所以需要加盐,解盐之后才能查看
request.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)


参数:
        default: 默认值
        salt: 加密盐
        max_age: 后台控制过期时间
View Code

Django中默认支持Session,其内部提供了5种类型的Session供开发者使用:数据库(默认,django_session表中),缓存,文件,缓存+数据库,加密cookie

session使用:

Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。

a. 配置 settings.py

    SESSION_ENGINE = 'django.contrib.sessions.backends.db'   # 引擎(默认)

    SESSION_COOKIE_NAME = "sessionid"                       # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)

    SESSION_COOKIE_PATH = "/"                               # Session的cookie保存的路径(默认)

    SESSION_COOKIE_DOMAIN = None                             # Session的cookie保存的域名(默认)

    SESSION_COOKIE_SECURE = False                            # 是否Https传输cookie(默认)

    SESSION_COOKIE_HTTPONLY = True                           # 是否Session的cookie只支持http传输(默认)

    SESSION_COOKIE_AGE = 1209600                             # Session的cookie失效日期(2周)(默认)

    SESSION_EXPIRE_AT_BROWSER_CLOSE = False                  # 是否关闭浏览器使得Session过期(默认)

    SESSION_SAVE_EVERY_REQUEST = False                       # 是否每次请求都保存Session,默认修改之后才保存(默认)
    
    
    
    
b. 使用

    def index(request):
        # 获取、设置、删除Session中数据
        request.session['k1']
        request.session.get('k1',None)
        request.session['k1'] = 123
        request.session.setdefault('k1',123) # 存在则不设置
        del request.session['k1']

        # 所有 键、值、键值对
        request.session.keys()
        request.session.values()
        request.session.items()
        request.session.iterkeys()
        request.session.itervalues()
        request.session.iteritems()

        # 用户session的随机字符串
        request.session.session_key

        # 将所有Session失效日期小于当前日期的数据删除
        request.session.clear_expired()

        # 检查 用户session的随机字符串 在数据库中是否
        request.session.exists("session_key")

        # 删除当前用户的所有Session数据
        request.session.delete("session_key")
        #删除
        request.session.clear()
        request.session.flush()
        del request.session['']
View Code

session和cookie过期时间设置:

  1.函数设置:request.session.set_expiry(value)

  设置会话的超时时间,如果没有指定过期时间则两个星期后过期。

    如果value 是一个整数,会话将在value 秒没有活动后过期。
    如果value 为0,那么用户会话的Cookie 将在用户的浏览器关闭时过期。
    如果value 为None,那么会话永不过期。

  2. settings.py文件中配置

    SESSION_COOKIE_AGE:设置cookie 在浏览器中存活的时间。

    SESSION_EXPIRE_AT_BROWSER_CLOSE:默认情况下为False,会话cookie 可以在用户浏览器中保持有效达SESSION_COOKIE_AGE 秒(缺省设    置是两周,即1,209,600 秒)。如果SESSION_EXPIRE_AT_BROWSER_CLOSE设置为True,当浏览器关闭时,Django 会使cookie 失效。
10. django的缓存框架

  https://zhuanlan.zhihu.com/p/40864335

  https://www.jianshu.com/p/b4e243834715

11. django跨域问题解决

  https://www.jianshu.com/p/63fb55bee142

  https://www.cnblogs.com/silence-cho/p/10202854.html

  • 使用django-cors-headers全局控制
  • 使用JsonP,只能用于Get方法
  • views.py里设置响应头,只能控制单个接口  (CORS: access-control-allow-origin

12.Django日志管理

http://www.liujiangblog.com/course/django/176

  • Loggers: 记录器
  • Handlers:处理器
  • Filters: 过滤器
  • Formatters: 格式化器
  • logger.debug()
  • logger.info()
  • logger.warning()
  • logger.error()
  • logger.critical()

13.HttpRequest和HttpResponse对象

 HttpRequest 是django 接受用户发送多来的请求报文后,将报文封装到HttpRequest 对象中去。
HttpResponse 返回的是一个应答的数据报文。render 内部已经封装好了HttpResponse 类。

 request和response的属性及方法:

#request 属性
request.path 请求页面的路径,不包含域名
request.get_full_path 获取带参数的路径
request.method 页面的请求方式
request.GET GET 请求方式的数据
request.POST POST 请求方式的数据
request.COOKIES 获取cookie
request.session 获取session
request.FILES 上传图片(请求页面有enctype="multipart/form-data"属性时FILES 才有数据)
request.GET.get()取值时如果一键多值情况,get 是覆盖的方式获取的。getlist()可以获取多个值。


#response属性
content: 表示返回的内容
charset: 表示response 采用的编码字符集,默认是utf-8
status_code:返回的HTTP 响应状态码,3XX 是对请求继续进一步处理,常见的是重定向。

init:创建httpResponse 对象完成返回内容的初始化
set_cookie:设置Cookie 信息(格式:set_cookies('key','value',max_age=None,expires=None)
max_age 是一个整数,表示指定秒数后过期,expires 指定过期时间,默认两个星期后过期。)
write 向响应体中写数据
View Code

14. form表单

           django提供了两个django.forms.Form 和django.forms.ModelForm,可以方便根据model构造前端表单和后端数据验证和处理。其使用参考:https://www.cnblogs.com/silence-cho/p/10202634.html

https://docs.djangoproject.com/en/2.1/ref/forms/fields/#built-in-field-classes

BooleanField
CharField
ChoiceField   单选框
TypedChoiceField
DateField
DateTimeField
DecimalField
DurationField
EmailField
FileField
FilePathFiled
FloatField
ImageField
IntegerField
GenericIPAddressField
MultipleChoiceField  多选框
TypedMultipleChoiceField
NullBooleanField
RegexField
SlugField
TimeField
URLField
UUIDField
ComboField
MultiValueField
SplitDateTimeField

#下面两个可以接受queryset对象,用于处理外键,多对多等关系 
ModelChoiceField   下拉单选框
ModelMultipleChoiceField    下拉多选框

自定义field:
If the built-in Field classes don’t meet your needs, you can easily create custom Field classes. To do this, just create a subclass of django.forms.Field. Its only requirements are that it implement a clean() method and that its __init__() method accept the core arguments mentioned above (required, label, initial, widget, help_text).

You can also customize how a field will be accessed by overriding get_bound_field().
forms中自带的field
https://docs.djangoproject.com/en/2.1/ref/forms/fields/

每个field有些自己特殊的参数,如max_length, min_length等,下面为一些通用的参数
required  默认为false
label  前端显示标签
label_suffix  标签后缀
initial 初始值

widget  前端显示的html元素   
# widget=forms.widgets.TextInput(attrs={'type':'number','class':'form-control'}

help_text  提示文字,显示在field旁边

error_messages  字典,每个field自带的clean函数验证出错时,显示的错误信息
# error_messages={'required': 'Please enter your name'}, 每个field可设置的key不一样

validators  列表,加入一系列的函数来验证该字段,参考https://docs.djangoproject.com/en/2.1/ref/validators/

localize    参考https://docs.djangoproject.com/en/2.1/topics/i18n/formatting/

disabled 设置为True时,不可编辑
field对应的参数
class BookForm(forms.Form):
    title=forms.CharField(
        label='书籍标题',
        max_length=32,
        widget=forms.widgets.TextInput(attrs={'class':'form-control'})
    )
    price = forms.DecimalField(
        label='价格',
        widget=forms.widgets.TextInput(attrs={'type':'number','class':'form-control'})
    )  # 设置type来改变类型,显示数字输入框

    publish = forms.ModelChoiceField(
        label='出版社',
        queryset=models.Publish.objects.all(),
        widget=forms.widgets.Select(attrs={'class': 'form-control'})
    )
    # gender=forms.ChoiceField(choices=((1,""),(2,""),(3,"其他")))
    # publish=forms.ChoiceField(choices=Publish.objects.all().values_list("pk","title"))
    #ModelChoiceField继承了ChoiceField
    author = forms.ModelMultipleChoiceField(
        label='作者',
        queryset=models.Author.objects.all(),
        widget=forms.widgets.SelectMultiple(attrs={'class': 'form-control'})
    )
使用示例

         form表单提供了接口对数据进行验证,包括每个field自带的clean(), 为某个field添加的clean_field(),以及整个表单的clean(). 其执行顺序如下,参考:https://www.cnblogs.com/ccorz/p/5868380.html

验证流程:

#每个field自带的clean
 1. 函数full_clean()依次调用每个field的clean()函数,该函数针对field的max_length,unique等约束进行验证,如果验证成功则返回值,否则抛出ValidationError错误。如果有值返回,则放入form的cleaned_data字典中。

#为某个field添加的clean_field()  
2.  如果每个field的内置clean()函数没有抛出ValidationError错误,则调用以clean_开头,以field名字结尾的自定义field验证函数。验证成功和失败的处理方式同步骤1。

#整个form表单的clean()
 3. 最后,调用form的clean()函数——注意,这里是form的clean(),而不是field的clean()——如果clean没有错误,那么它将返回cleaned_data字典。

4. 如果到这一步没有ValidationError抛出,那么cleaned_data字典就填满了有效数据。否则cleaned_data不存在,form的另外一个字典errors填上验证错误。在template中,每个field获取自己错误的方式是:{{ form.username.errors }}。
 
5. 最后,如果有错误is_valid()返回False,否则返回True。

注意一点:自定义验证机制时:clean()和clean_&()的最后必须返回验证完毕或修改后的值.
验证流程

from django import forms
from django.core.exceptions import ValidationError
import re


def mobile_validate(value):
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')


class LoginForm(forms.Form):
    user = forms.CharField(required=True, error_messages={'required': '用户名不能为空.'})
    pwd = forms.CharField(required=True,
                          min_length=6,
                          max_length=10,
                          error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})

    pwd2 = forms.CharField(required=True,
                          min_length=6,
                          max_length=10,
                          error_messages={'required': '密码不能为空.', 'min_length': "至少6位"})


    num = forms.IntegerField(error_messages={'required': '数字不能空.', 'invalid': '必须输入数字'})

    phone = forms.CharField(validators=[mobile_validate, ], )

    def clean_user(self):
        user = self.cleaned_data.get('user')
        if user == 'cc':
            raise forms.ValidationError('用户名是我的!')

        return user

    def clean(self):
        cleaned_data = self.cleaned_data
        pwd = cleaned_data['pwd']

        pwd2 = cleaned_data['pwd2']
        print(pwd,pwd2)
        if pwd != pwd2:
            raise forms.ValidationError('二次输入密码不匹配')
        return cleaned_data #注意此处一定要return clean_data,否则会报错
        
        


def login(request):
    if request.POST:
        objPost = LoginForm(request.POST)
        ret = objPost.is_valid()
        if ret:
            print(objPost.clean())
        else:
            from django.forms.utils import ErrorDict
            print(objPost.non_field_errors())

            pass
        return render(request, 'login.html', {'obj1': objPost})
    else:
        objGet = LoginForm()
        return render(request, 'login.html', {'obj1': objGet})
使用示例一:后端
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .error_msg{
            color: red;
        }
    </style>
</head>
<body>
    <form action="/login/" method="POST">
        <div>用户名:
            {{ obj1.user }}
            {%  if obj1.errors.user %}
                <span class="error_msg">{{ obj1.errors.user.0 }}</span>
            {% endif %}
        </div>
        <div>密码:
            {{ obj1.pwd }}
            {%  if obj1.errors.pwd %}
                <span class="error_msg">{{ obj1.errors.pwd.0 }}</span>
            {% endif %}
        </div>
        <div>确认密码:
            {{ obj1.pwd2 }}
            {%  if obj1.errors.pwd2 %}
                <span class="error_msg">{{ obj1.errors.pwd2.0 }}</span>
            {% endif %}
        </div>
        <div>数字:
            {{ obj1.num }}
            {%  if obj1.errors.num %}
                <span class="error_msg">{{ obj1.errors.num.0 }}</span>
            {% endif %}
        </div>
        <div>电话:
            {{ obj1.phone }}
            {%  if obj1.errors.phone %}
                <span class="error_msg">{{ obj1.errors.phone.0 }}</span>
            {% endif %}
        </div>
        <div>
            {%  if obj1.non_field_errors %}
                {% for item in obj1.non_field_errors %}
                    <span class="error_msg">{{ item }}</span>
                {% endfor %}
            {% endif %}
        </div>
        <input type="submit" value="提交"/>

    </form>


</body>
</html>
使用示例一:前端
#coding:utf-8

from django import forms
from django.core.exceptions import ValidationError
from blogHome import models

class RegisterForm(forms.Form):
    username = forms.CharField(
        max_length=16,
        label='用户名',
        error_messages={
            'max_length':'用户名最长16位',
            'required':'用户名不能为空',
        },
        widget=forms.widgets.TextInput(
            attrs={'class':'form-control'}
        ),
    )
    password = forms.CharField(
        min_length=6,
        label='密码',
        error_messages={
            'min_length':'密码至少6位',
            'required':'密码不能为空',
        },
        widget=forms.widgets.PasswordInput(
            attrs={'class':'form-control'},
        ),
    )
    confirmPassword = forms.CharField(
        min_length=6,
        label='确认密码',
        error_messages={
            'min_length':'确认密码至少6位',
            'required':'确认密码不能为空',
        },
        widget=forms.widgets.PasswordInput(
            attrs={'class':'form-control'},
        ),
    )
    email = forms.EmailField(
        label='邮箱',
        widget=forms.widgets.EmailInput(
            attrs={'class':'form-control'},
        ),
        error_messages={
            'invalid':'邮箱格式不正确',
            'required':'邮箱不能为空',
        },
    )
    #重写用户名钩子函数,验证用户名是否已经存在
    def clean_username(self):
        username = self.cleaned_data.get('username')
        is_exist = models.UserInfo.objects.filter(username=username)
        if is_exist:
            self.add_error('username',ValidationError('用户名已注册'))
        else:
            return username

    #重写邮箱钩子函数,验证邮箱是否已经存在
    def clean_email(self):
        email = self.cleaned_data.get('email')
        is_exist = models.UserInfo.objects.filter(email=email)
        if is_exist:
            self.add_error('email',ValidationError('邮箱已被注册'))
        else:
            return email
    #重写form全局钩子函数,判断两次密码一致
    def clean(self):
        password = self.cleaned_data.get('password')
        confirmPassword = self.cleaned_data.get('confirmPassword')
        if confirmPassword and password != confirmPassword:
            self.add_error('confirmPassword', ValidationError('两次密码不一致'))
        else:
            return self.cleaned_data   #重写后必须返回cleaned_data数据
使用示例二:后端
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>注册 </title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/myCSS.css">
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-6 col-md-offset-3 ">
            <form class="form-horizontal" action="/register/" method="post" enctype="multipart/form-data">
                {% csrf_token %}
                <div class="form-group">
                    <label for="{{ form_obj.username.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.username.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.username }}
                        <div class="has-error">
                            <span class="help-block">{{ form_obj.username.errors.0 }}</span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.password.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.password.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.password }}
                        <div class="has-error">
                            <span class="help-block">{{ form_obj.password.errors.0 }}</span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.confirmPassword.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.confirmPassword.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.confirmPassword }}
                        <div class="has-error">
                            <span class="help-block">{{ form_obj.confirmPassword.errors.0 }}</span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <label for="{{ form_obj.email.id_for_label }}"
                           class="col-sm-2 control-label">{{ form_obj.email.label }}</label>
                    <div class="col-sm-8">
                        {{ form_obj.email }}
                        <div class="has-error">
                            <span class="help-block">{{ form_obj.email.errors.0 }}</span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <label for="InputFile" class="col-sm-2 control-label">头像</label>
                    <div class="col-sm-8">
                        <input type="file" id="InputFile" name="avatar">
                    </div>
                </div>
                <div class="form-group">
                    <div class="col-sm-offset-4 col-sm-8">
                        <button type="submit" class="btn btn-info">提交</button>
                    </div>
                </div>
            </form>
        </div>
    </div>
</div>
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>
使用示例二:前端

15,后端生成简单图片验证码

(参考:https://www.cnblogs.com/alex3714/articles/6662365.html)

#生成验证码,保存到session中,并传给前端

def get_valid_img(request):
    from PIL import Image, ImageDraw, ImageFont
    import random

    #随机背景颜色
    def get_random_bgcolor():
        return random.randint(0,255), random.randint(0,255), random.randint(0,255)
    image_size = (220,35)  # 图片大小
    image = Image.new('RGB',image_size,get_random_bgcolor())  # 生成图片对象
    draw = ImageDraw.Draw(image)  # 生成画笔
    font = ImageFont.truetype('static/font/kumo.ttf',35)  # 字体类型和大小
    temp_list = []
    for i in range(5):
        u = chr(random.randint(65, 90))  # 生成大写字母
        l = chr(random.randint(97, 122))  # 生成小写字母
        n = str(random.randint(0, 9))  # 生成数字,注意要转换成字符串类型
        temp = random.choice([u,l,n])
        temp_list.append(temp)
    text = ''.join(temp_list)
    request.session['valid_code'] = text
    font_width, font_height = font.getsize(text)
    draw.text(((220-font_width)/4,(35-font_height)/4),text,font=font,fill=get_random_bgcolor())

    #绘制干扰线
    # for i in range(5):
    #     x1 = random.randint(0, 220)
    #     x2 = random.randint(0, 220)
    #     y1 = random.randint(0, 35)
    #     y2 = random.randint(0, 35)
    #     draw.line((x1, y1, x2, y2), fill=get_random_bgcolor())
    #
    # #加干扰点
    # for i in range(40):
    #     draw.point((random.randint(0, 220), random.randint(0, 35)), fill=get_random_bgcolor())
    #     x = random.randint(0, 220)
    #     y = random.randint(0, 35)
    #     draw.arc((x, y, x+4, y+4), 0, 90, fill=get_random_bgcolor())

    # 不需要在硬盘上保存文件,直接在内存中加载就可以
    from cStringIO import StringIO
    from io import BytesIO
    io_object = StringIO()
    image.save(io_object,'png')  # 将生成的图片数据保存在io对象中
    data = io_object.getvalue()  # 从io对象里面取上一步保存的数据
    return HttpResponse(data)
生成验证码函数
#参考:https://www.cnblogs.com/alex3714/articles/6662365.html

1 #_*_coding:utf-8_*_
 2 
 3 from PIL import Image,ImageDraw,ImageFont,ImageFilter
 4 
 5 import random
 6 import math, string
 7 
 8 
 9 #字体的位置,不同版本的系统会有不同
10 font_path = '/Library/Fonts/Arial.ttf'
11 #font_path = '/Library/Fonts/Hanzipen.ttc'
12 #生成几位数的验证码
13 number = 4
14 #生成验证码图片的高度和宽度
15 size = (100,30)
16 #背景颜色,默认为白色
17 bgcolor = (255,255,255)
18 #字体颜色,默认为蓝色
19 fontcolor = (0,0,255)
20 #干扰线颜色。默认为红色
21 linecolor = (255,0,0)
22 #是否要加入干扰线
23 draw_line = True
24 #加入干扰线条数的上下限
25 line_number = (1,5)
26 
27 
28 
29 
30 def gen_text():
31     source = list(string.ascii_letters)
32     for index in range(0,10):
33         source.append(str(index))
34     return ''.join(random.sample(source,number))#number是生成验证码的位数
35 
36 
37 #用来绘制干扰线
38 def gene_line(draw,width,height):
39     begin = (random.randint(0, width), random.randint(0, height))
40     end = (random.randint(0, width), random.randint(0, height))
41     draw.line([begin, end], fill = linecolor)
42 
43 def gene_code(save_path,filename):
44     width,height = size #宽和高
45     image = Image.new('RGBA',(width,height),bgcolor) #创建图片
46 
47     font = ImageFont.truetype(font_path,25) #验证码的字体和字体大小
48     #font = ImageFont.truetype(25) #验证码的字体和字体大小
49     draw = ImageDraw.Draw(image) #创建画笔
50     #text = "我是中国人" #生成字符串
51     text = gen_text() #生成字符串
52     print(text)
53     font_width, font_height = font.getsize(text)
54     draw.text(((width - font_width) / number, (height - font_height) / number),text,\
55         font= font,fill=fontcolor) #填充字符串
56 
57     if draw_line:
58         gene_line(draw, width, height)
59         gene_line(draw, width, height)
60         gene_line(draw, width, height)
61         gene_line(draw, width, height)
62 
63     image = image.transform((width + 20, height +10), Image.AFFINE, (1, -0.3, 0, -0.1, 1, 0), Image.BILINEAR)  # 创建扭曲
64     image = image.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强
65     image.save('%s/%s.png' %(save_path,filename))  # 保存验证码图片
66     print("savepath:",save_path)
67     return text
68 
69 if __name__ == "__main__":
70     gene_code('/tmp','test') #会把生成的图片存成/tmp/test.png
类似函数

url配置:

url(r'^get_valid_img.png/', views.get_valid_img),

前端使用:验证码显示,并且点击验证码图片更新验证码(通过绑定一个click函数改变img的src属性,从而向后端重新发送请求)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登陆</title>
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/myCSS.css">
</head>
<body>
    <form class="form-horizontal" action="/login/" method="post">
        {% csrf_token %}
        <div class="form-group">
            <label for="inputUsername3" class="col-sm-4 control-label">用户名</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="inputUsername3" placeholder="Username" name="username">
            </div>
        </div>
        <div class="form-group">
            <label for="inputPassword3" class="col-sm-4 control-label">密码</label>
            <div class="col-sm-4">
                <input type="password" class="form-control" id="inputPassword3" placeholder="Password" name="password">
            </div>
        </div>
        <div class="form-group">
            <label for="validCode" class="col-sm-4 control-label">验证码</label>
            <div class="col-sm-4">
                <input type="password" class="form-control" id="validCode" placeholder="验证码" name="validCode">
                <img id="valid-img" class="valid-img" src="/get_valid_img.png/" alt="">
                <span style="margin-left: 10px;color: green;">看不清,点击验证码刷新</span>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-4 col-sm-8">
                <div class="checkbox">
                    <label>
                        <input type="checkbox"> 记住我
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group has-error">
            <div class="col-sm-offset-4 col-sm-8">
                {% if result.status %}
                <span class="help-block">{{ result.msg }}</span>
                {% endif %}
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-4 col-sm-8">
                <button type="submit" class="btn btn-info">登陆</button>
            </div>
        </div>
    </form>
<script src="/static/jquery-3.3.1.min.js"></script>
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
<script>
    $('#valid-img').click(function () {
        //console.log($(this));
        //console.log($(this)[0]);
        $(this)[0].src+='?';   //变化src属性值不一样即可以重复提交url
    });
</script>
</body>
</html>
前端验证码图片展示

后端验证码验证:将前端传过来的验证码和session中保存的验证码进行对比

def login(request):
    ret = {'status': 0, 'msg': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        valid_code = request.POST.get("validCode")
        if valid_code and valid_code.upper() == request.session['valid_code'].upper():
            user = auth.authenticate(username=username,password=password)
            if user:
                auth.login(request,user)
                return redirect('/index')
            else:
                ret['msg'] = '用户名或密码错误!'
                ret['status'] = 1
                return render(request, 'login.html',{'result':ret})
        else:
            ret['msg'] = '验证码错误!'
            ret['status'] = 1
            return render(request, 'login.html', {'result': ret})
    else:
        return render(request, 'login.html',{'result': ret})
验证验证码

 

 

 

 

admin后台 unicodeerror报错时,参考在manage.py, admin.py中添加

import sys

reload(sys)

sys.setdefaultencoding("utf8")

 

 

参考博客:http://www.cnblogs.com/yuanchenqi/articles/6083427.html

    https://simpleisbetterthancomplex.com/series/2017/09/11/a-complete-beginners-guide-to-django-part-2.html

 

 






 

 

3 django admin后台添加中文报错:
'ascii' codec can't encode characters in position 0-3: ordinal not in range(128)

解决方案:
在manage.py 文件中加入 reload(sys)和sys.setdefaultencoding('utf8'),如下:
#coding:utf-8
#!/usr/bin/env python
import os
import sys
reload(sys)
sys.setdefaultencoding('utf8')


 

关于DateTimeInput(DateTimeField)

https://stackoverflow.com/questions/1450463/django-datetimewidget-not-showing-up

  
  

5  json.dumps只能序列化python的数据格式,无法序列化Django提供的特殊数据格式,如auto_now_and产生的时间
    5.1 ValuesQuerySet序列化

      models.Reply.objects.filter(new__id=nid).values('content','id')

         values获得的数据,如果返回类型为ValuesQuerySet,先转化为list,再json.dumps()


       5.2 QuerySet序列化

      models.Reply.objects.filter(new__id=nid)

        filter获得的数据,如果返回类型为QuerySet,得用serializers.serialize() 序列化(from django.core import serializers)

6. 扩展Django的User Model

  https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html

 

posted @ 2024-09-21 14:20  silence_cho  阅读(99)  评论(0)    收藏  举报