Django内容整理

  • Post author:
  • Post category:其他




Django基本知识

  1. django的命令

  2. 下载安装


    pip install django==1.11.28 -i 源

    1. 创建django项目


    django-admin startproject 项目名称

    1. 启动项目

    切换到项目的根目录


    python manage.py runserver

    127.0.0.1:8000


    python manage.py runserver 80

    127.0.0.1:80


    python manage.py runserver 0.0.0.0:80

    0.0.0.0:80

    1. 创建app


    python manage.py startapp app名称

    1. 数据库迁移的命令
    python manage.py makemigrations # 制作迁移文件  检测所有注册的APP下的models的变更,记录下变更记录
    python manage.py migrate # 迁移 将变更记录同步到数据库中
    

2.django的配置settings.py

​ BASE_DIR 项目的根目录

​ INSTALLED_APPS = [

​ ‘app01.apps.App01Config’

]

​ MIDDLEWARE

​ 注释掉一个csrf的中间件,可以提交POST请求

​ TEMPLATES

​ DIRS [ os.path.join(BASE_DIR,‘tempaltes’) ]

​ DATABASES 数据库

​ 静态文件

​ STATIC_URL = ‘/static/’ 访问静态文件的路径

​ STATICFILES_DIRS = [ 包含静态资源的文件

​ os.path.join(BASE_DIR,‘static’)

]

3.django项目使用mysql数据库的流程

  1. 手动创建一个mysql数据库

  2. 在settings.py中配置数据库的连接

    ENGINE  引擎  mysql
    NAME    数据库的名字
    HOST    数据库的所在的IP
    PORT    3306
    USER    用户名
    PASSWORD  密码
    
    1. 告诉django使用pymysql连接mysql数据库

    写在与项目同名的目录下的

    __init__.py

    import pymysql
    pymysql.install_as_MySQLdb()
    
    1. 在app下的models.py中写类
    class User(models.Model):
    	username = models.CharField(max_length=32) # varchar(32)
    
    1. 执行数据库迁移的命令
    python manage.py makemigrations
    python manage.py migrate
    

4.request

​ request.method 请求方法 GET POST

​ request.GET URL上携带的参数 ?k1=v1 {}

​ request.POST post请求提交的数据 {}

5.response

​ HttpResponse(‘字符串’) 返回的是字符串

​ render(request,‘模板的名字’,{‘k1’:v1}) 返回一个完整的HTML页面

​ redirect(‘地址’) 重定向

6.ORM

​ 对象关系映射

​ 对应关系

​ 类 ——》 表

​ 对象 ——》 数据行(记录)

​ 属性 ——》 字段

class Publisher(models.Model):
    name = models.CharField(max_length=32)

class Book(models.Model):
    name = models.CharField(max_length=32)
    pub = models.ForeginKey('Publisher',on_delete=models.CASCADE)
    
class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book') # 不创建字段,创建第三张表
    

ORM操作:

1.查询

from app01 import models

models.Publisher.objects.all()  # 查询所有的数据 QuerySet  对象列表
models.Publisher.objects.get(name='xx',pk='1')  # 获取有且唯一的一个的对象  
models.Publisher.objects.filter(name='xx',pk='1')  # 获取多个对象  对象列表

pub_obj.pk    pub_obj.name  

book_obj.pub        # 外键  所关联的对象Publisher对象
book_obj.pub_id     # 所关联对象的id

author_obj.books   # 多对多字段   关系管理对象
author_obj.books.all()   # 关联的所有的对象  对象列表

2.新增

models.Publisher.objects.create(name='xxx')

models.Book.objects.create(name='xxx',pub=出版社对象)
models.Book.objects.create(name='xxx',pub_id=出版社对象的id)

author_obj = models.Author.objects.create(name='xxx')
author_obj.books.set([1,2])   # 设置多对多关系

3.删除

models.Publisher.objects.get(pk=1).delete() # 通过对象进行删除 
models.Publisher.objects.filter(pk=1).delete() # 批量删除

4.修改

pub_obj.name = 'xxxx'
pub_obj.save()   # 提交到数据库中保存

models.Publisher.objects.filter(pk=1).update(name='xxxx')

7.模板

render(request,‘模板的名字’,{‘k1’:v1})

{{ k1 }}  



{% for i in list  %}
	
	{{ forloop.counter  }}
	{{ i }}

{% endfor %}



{% if 条件 %}
	x1
{% elif 条件1 %}
	x2
{% else %}
	else
{% endif %}

MVC:

M:model 模型 操作数据库

V:view 视图 展示数据 HTML

C:controller 控制器 流程 业务逻辑

MTV:

M:model ORM

T: template 模板

V:view 视图 业务逻辑

模板语法

变量 {

{ 变量 }}

.
.索引  .key  .属性  .方法   方法后不加括号 
优先级:
.key  >  .属性 .方法 >  .索引

过滤器:

{

{ 变量|过滤器:‘参数’ }}

default:

变量不存在或者为空时使用默认值

add +

数字的加法,字符串和列表的拼接

date

{{ now|date:'Y-m-d H:i:s' }}

settings

USE_L10N = False
DATETIME_FORMAT = 'Y-m-d H:i:s'
TIME_FORMAT = 'H:i:s'
DATE_FORMAT = 'Y-m-d'


自定义过滤器
  1. 在一个已经注册的app下创建一个名为templatetags的python包 (包的名字不能错)

  2. 创建一个python文件,文件名自定义(mytags.py)

  3. 在python文件中写:

    from django import template
    
    register = template.Library()  # register的名字不能错
    
  4. 写函数 + 加装饰器

    @register.filter
    def add_arg(value, arg):
        # 功能
        return "{}_{}".format(value, arg)
    
@register.filter
def add_arg(value,arg):
	return  'xxxx'

@register.simple_tag
def str_join(*args, **kwargs):
    return "{}_{}".format('_'.join(args), '*'.join(kwargs.values()))


@register.inclusion_tag('page.html')
def pagination(num):
    return {'num': range(1, num + 1)}

page.html

<nav aria-label="Page navigation">
    <ul class="pagination">
        <li>
            <a href="#" aria-label="Previous">
                <span aria-hidden="true">&laquo;</span>
            </a>
        </li>
        {% for li in num %}
            <li><a href="#">{{ li }}</a></li>
        {% endfor %}

        <li>
            <a href="#" aria-label="Next">
                <span aria-hidden="true">&raquo;</span>
            </a>
        </li>
    </ul>
</nav>

5.使用

{% load mytags %}

{{ 'alex'|add_arg:'dsb' }}

{% str_join 'a' 'b' 'c' k1='d' k2='e' k3='f' %}

{% pagination 10 %}

mark_safe

from django.utils.safestring import mark_safe


@register.filter
def show_a(name, url):
    return mark_safe('<a href="http://{}">{}</a>'.format(url, name))

for

<ul>
    {% for name in name_list %}
        <li>{{ forloop.counter }}-{{ name }}</li>
    {% endfor %}

</ul>

forloop.counter 当前循环的序号 从1开始

forloop.counter0 当前循环的序号 从0开始

forloop.revcounter 当前循环的序号(倒序) 到1结束

forloop.revcounter0 当前循环的序号(倒序) 到0结束

forloop.first 是否是第一次循环 布尔值

forloop.last 是否是最后一次循环 布尔值

forloop.parentloop 当前循环的外层循环的变量

{% for foo in kong %}
    {{ foo }}
{% empty %}
    空空如也
{% endfor %}

if

{% if alex.age < 73 %}
    alex正迈向第一个坎

{% elif alex.age == 73 %}
    alex正在第一个坎上

{% elif alex.age < 84 %}
    alex正迈向第二个坎上

{% elif alex.age == 84 %}
    alex正在第二个坎上

{% elif alex.age > 84 %}
    差不多了

{% endif %}

注意:

  1. 不支持算术运算
  2. 不支持连续判断
{% if 10 > 5 > 1 %}
    正确1
{% else %}
    错误
{% endif %}

with

{% with p_list.0.name as alex %}

    {{ alex }}
    {{ alex }}
    {{ alex }}

{% endwith %}

{% with alex=p_list.0.name %}

    {{ alex }}
    {{ alex }}
    {{ alex }}

{% endwith %}

csrf_token

{% csrf_token %}写在form标签中,form标签中就有一个隐藏的input标签,name=‘csrfmiddlewaretoken’

<form action="" method="post">
    {% csrf_token %}
    <input type="text" name="k1">
    <button>提交</button>
</form>



图书管理页面示例

  1. django处理浏览器的请求的流程:

    1. 请求发送到了wsgi,wsgi封装请求的相关数据(request)
    2. django去匹配路径,根据路径判断要执行哪个函数
    3. 执行函数,函数中处理具体的业务逻辑
    4. 函数返回响应,django按照HTTP协议的响应的格式进行返回
  2. 发请求的途径

    1. 在浏览器的地址栏中输入地址 回车 ——》 get
    2. a标签 ——》 get
    3. form表单 post/get
  3. get和post请求的区别

    get 获取一个资源

    ?k1=v1&k2=v2 request.GET

    get请求没有请求体

    post 提交数据

    request.POST

    数据在请求体中

  4. views.py

    
    
    def login(request):
    	# 业务逻辑
    	
    	return 响应
    
    
    HttpResponse('字符串')   # 返回一个字符串
    render(request,'模板的文件名'{‘k1’:v1})
    redirect('地址')  # 重定向
    
    request.method  # 请求方式  GET POST 
    
  5. ORM

    models.py

    
    class Publisher(models.Model):
        name = models.CharField(max_length=32)  # varchar(32)
    	
    
    class Book(models.Model):
        name = models.CharField(max_length=32)  # varchar(32)
        pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
        """
        on_delete  2.0版本后是必填的 
            models.CASCADE  级联删除
            models.PROTECT  保护
            models.SET(v)   删除后设置为某个值
            models.SETDEFAULT   删除后设置为默认值
            models.SET_NULL     删除后设置为Null
            models.DO_NOTHING  什么都不做
        """
        
    

    orm的操作:

    from app01 import models
    models.Publisher.objects.all()  # 获取所有的数据  QuerySet  对象列表
    models.Publisher.objects.get(name='xxx',id='1')  # 获取一条存在且唯一的数据  对象
    models.Publisher.objects.filter(name='xxx',id='1')  # 获取多条的数据  对象列表
    
    ret = models.Book.objects.all() # 对象列表
    for book in ret:
    	print(book)
    	print(book.id,book.pk)
    	print(book.name)
    	print(book.pub)      # 书籍所关联的出版社的对象
    	print(book.pub_id)   # 书籍所关联的出版社的id
    
    

    新增:

    models.Publisher.objects.create(name='xxx')  # 新增的对象
    
    models.Book.objects.create(name='xxx',pub=出版社的对象)   # 新增的对象
    models.Book.objects.create(name='xxx',pub_id=出版社的id)  # 新增的对象
    

    删除

    models.Publisher.objects.get(pk=1).delete()
    models.Publisher.objects.filter(pk=1).delete()  # 批量删除
    

    编辑

    book_obj.name = 'xxx'
    # book_obj.pub= 出版社对象  
    # book_obj.pub_id= 出版社的id
    book_obj.save()  # 保存到数据库
    
    
    models.Book.objects.filter(pk=1).update(name='xx',pub_id=出版社的id) # 批量更新
    
    
  6. 模板的语法

    return render(request,‘模板的文件名’,{‘k1’:v1,‘k2’:v2})

    {{ k1 }}  {{ k2 }}  
    
    
    for 
    {% for i in k1   %}
    	
    	{{ forloop.counter }}
    	{{ i }}
    
    {% endfor %}
    
    
    if  
    {% if 条件 %}
    	xxx
    {% endif %}
    
    
    {% if 条件 %}
    	xxx
    {% else %}
    	x1
    {% endif %}
    
    
    {% if 条件 %}
    	xxx
    {% elif 条件1 %}	
    	xx
    {% else %}
    	x1
    {% endif %}
    

作者表增删改查

# 作者表
class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book')  # 不在Author表中新增字段,会创建第三章表

    all_authors = models.Author.objects.all()
    for author in all_authors:
        print(author)
        print(author.id)
        print(author.name)
        print(author.books)  # 关系管理对象
        print(author.books.all())  # 所关联的所有的对象
        print('*' * 40)

展示

<table border="1">
    <thead>
    <tr>
        <th>序号</th>
        <th>ID</th>
        <th>姓名</th>
        <th>代表作</th>
    </tr>

    </thead>
    <tbody>

    {% for author in all_authors %}
        <tr>
            <td>{{ forloop.counter }}</td>
            <td>{{ author.pk }}</td>
            <td>{{ author.name }}</td>
            <td>
                {% for book in author.books.all %}
                     {{ book.name }} 
                {% endfor %}
            </td>
        </tr>
    {% endfor %}
    
    </tbody>
</table>

新增

author_name = request.POST.get('author_name')
book_ids = request.POST.getlist('book_ids')  # 获取多个数据 list类型
# print(book_ids,type(book_ids))

# 向作者表插入了作者的信息
author_obj = models.Author.objects.create(name=author_name)
# 该作者和提交的书籍绑定多对多的关系
author_obj.books.set(book_ids)  # 设置多对多关系

编辑

author_name = request.POST.get('author_name')
book_ids = request.POST.getlist('book_ids')
# 给该对象修改数据
# 修改作者的姓名
author_obj.name = author_name
author_obj.save()
# 修改作者和书的多对多关系
author_obj.books.set(book_ids)  # 重新设置



内容整理

{

{ }} 变量

{% %} 标签

.

{

{ 变量.索引 }}

{

{ 变量.key }}

{

{ 变量.属性 }}

{

{ 变量.方法 }}

优先级:

.key > .属性和方法 > .索引

过滤器

{

{ 变量|filter }} {

{ 变量|filter:参数 }}

内置的过滤器

default 给变量加默认值

add + 数字的加法 字符串的拼接 列表的拼接

safe 告诉django前面的字符串不需要做转义

upper lower title

filesizeformat 文件的大小显示成带单位的

slice 切片 {

{ list|slice:‘::’ }}

length 返回变量的长度

truncatechars 根据字符的长度进行截断

truncatewords 根据单词的长度进行截断

join 字符串的拼接

date 日期时间的格式化 ‘Y-m-d H:i:s’

​ TIME_ZONE = ‘Asia/Shanghai’

​ USE_L10N = False

​ DATETIME_FORMAT = ‘Y-m-d H:i:s’

​ DATE_FORMAT = ‘Y-m-d’

​ TIME_FORMAT = ‘H:i:s’



母版和继承

母版:

  1. 一个包含多个页面的公共部分
  2. 定义多个block块,让子页面进行覆盖

继承:

1. {% extends  '母版的名字' %}
2. 重新复写block块

注意点:

  1. {% extends ‘母版的名字’ %} 母版的名字带引号

  2. {% extends ‘母版的名字’ %}写在第一行,上面不再写内容

  3. 要显示的内容要写在block块中

  4. 多写一个css/js的block块

组件

  1. 把一小段公用的HTML文本写入一个HTML文件 , nav.html

  2. 在需要该组件的模板中

    {% include 'nav.html' %}
    

静态文件相关:

{% load static %}

<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.3.7-dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'css/dsb.css' %}">

静态文件

配置

STATIC_URL = ‘/static/’ 访问静态文件的路径

STATICFILES_DIRS = [

​ os.path.join(BASE_DIR,‘static’) 包含静态文件的文件名

]

引入

<link rel="stylesheet" href="/static/相对路径">
<script src="/static/相对路径"></script>

{% load static %}

<link rel="stylesheet" href="{% static '相对路径' %}">



CBV和FBV

FBV function based view

def xxx(request):
	# 业务逻辑的处理
	
	return response

# urls.py
url(r'xxx/',xxx)

CBV class based view

from django.views import View

class Xxx(View):
    
    def get(self,request)
    	# 专门处理get请求
        return response
    
	def post(self,request)
    	# 专门处理post请求
        return response

 url(r'xx/',Xxx.as_view())

as_view()的流程

  1. 项目运行时加载urls.py的文件,执行类.as_view()方法

  2. as_view()执行后,内部定义了一个view函数,并且返回。

  3. 请求到来的时候,执行view函数:

    1. 实例化类 —— 》 self

    2. self.request = request

    3. 执行self.dispatch(request, *args, **kwargs)的方法

      1. 判断请求方式是否被允许

        1. 允许:

          通过反射获取请求方式对应的请求方法 ——》 handler

          获取不到 self.http_method_not_allowed ——》 handler

        2. 不允许:

          self.http_method_not_allowed ——》 handler

      2. 执行handler,返回结果

from functools import wraps

def timer(func):
    @wraps(func)
    def inner(request, *args, **kwargs):
        
        start = time.time()
        ret = func(request, *args, **kwargs)
        print('执行的时间是:{}'.format(time.time() - start))
        return ret

    return inner

FBV

直接加在函数上就行

CBV加装饰器:

需要使用一个装饰器

from django.utils.decorators import method_decorator
  1. 加在方法上

    @method_decorator(timer)
    def get(self, request):
    
  2. 加在dispatch方法上

    @method_decorator(timer)
    def dispatch(self, request, *args, **kwargs):
        # 之前的操作
        ret = super().dispatch(request, *args, **kwargs)  # 执行View中的dispatch方法
        # 之后的操作
        return ret3
    
    @method_decorator(timer,name='dispatch')
    class PublisherAdd(View):
    
  3. 加在类上

    @method_decorator(timer,name='post')
    @method_decorator(timer,name='get')
    class PublisherAdd(View):
    


request
request.method  请求方法  GET POST 
request.GET    URL上携带的参数   ?k1=v1&k2=v2   { }
request.POST  post请求提交的数据  {}     编码方式是URLencode
request.path_info  路径信息   不包含IP和端口 也不包含参数
request.body  请求体   bytes类型
request.COOKIES   cookie
request.session    session
request.FILES   长传的文件
request.META    头的信息     小写——》大写   HTTP_ 开头   -  ——》 _

request.get_full_path()  完整的路径信息  不包含IP和端口 包含参数
request.is_ajax()    请求是否是ajax请求



response
from django.shortcuts import render, HttpResponse, redirect

HttpResponse('字符串')  #   返回字符串
render(request,'模板的文件名'{'k1':v1})   # 返回一个HTML页面
redirect('地址')  # 重定向  Location:‘地址’  301 302 
from django.http.response import JsonResponse
JsonResponse({'k1':'v1'})
JsonResponse(data,safe=False)


上传文件

urls.py

url(r'^upload/', views.Upload.as_view()),

视图:

class Upload(View):

    def get(self, request):
        return render(request, 'upload.html')

    def post(self, request):
        file = request.FILES.get('f1')

        with open(file.name, 'wb') as f:
            for i in file:
                f.write(i)

        return HttpResponse('ok')

upload.html

<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}

    <input type="file" name="f1">
    <button>上传</button>
</form>

f1 = request.FILES.get('f1')
f1.name # 上传文件的名字

for i in f1:
	f.write(i)


路由
from django.conf.urls import url

urlpatterns = [
     url(正则表达式, views视图,参数,别名),]


正则表达式
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^blog/$', views.blog),
    url(r'^blog/[0-9]{4}/\d{2}/$', views.blogs),
]

^  以什么开头 
$  以什么结尾
\d 数字
\w 数字字母下划线
{} 
[a-z0-9]
.  匹配换行符之外的标志
+  一个或多个
?  0个或1个
*   0个或多个
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项
APPEND_SLASH=True


分组
url(r'^blog/([0-9]{4})/\d{2}/$', views.blogs),

url地址上捕获的参数会按照 位置传参 方式传递给视图函数

def blogs(request,year):


命名分组
url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs),

url地址上捕获的参数会按照 关键字传参 方式传递给视图函数

def blogs(request,year,month):


路由分发

ROOT_URLCONF

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls')),
    url(r'^app02/', include('app02.urls')),

]

app01/urls.py

from django.conf.urls import url

from app01 import views

urlpatterns = [

    url(r'^blog/$', views.blog),  # /app01/blog/
    url(r'^blog/(?P<year>[0-9]{4})/(?P<month>\d{2})/$', views.blogs,),
]


url的命名和反向解析

静态路由

命名:

url(r'^blog/$', views.blog,name='blog'),  # /app01/blog/    ——》 blog

反向解析:

模板

{% url  'blog' %}    ——》 /app01/blog/

py:

from django.shortcuts import reverse

from django.urls import reverse

reverse('blog')  # ——》 /app01/blog/


分组
url(r'^blog/([0-9]{4})/(\d{2})/$', views.blogs,name='blogs'),

反向解析:

模板

{% url 'blogs' '2020' '02' %}  ——》 /app01/blog/2020/02/

py

reverse('blogs',args=('2018','08')  ——》 /app01/blog/2018/08/


命名分组
url(r'^blog/([0-9]{4})/(\d{2})/$', views.blogs,name='blogs'),

反向解析:

模板

{% url 'blogs' '2020' '02' %}  ——》 /app01/blog/2020/02/
{% url 'blogs'  month='02' year='2020' %}  ——》 /app01/blog/2020/02/

py

reverse('blogs',args=('2018','08')  ——》 /app01/blog/2018/08/
reverse('blogs',kwargs={'year': '2018', 'month': '12'})  ——》 /app01/blog/2018/12/



namespace

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),

]

反向解析

reverse('namespace:name',args=('2018','08')  ——》 /app01/blog/2018/08/
{% url namespace:name  %}


常用的字段
AutoField  自增字段 primary_key=True 变成主键
IntegerField    整形  10-2147483648 ~ 2147483647。
CharField    字符串   varchar  max_length 必填参数
DateField
DatetimeField   # auto_now_add=True 新增数据时自动保存当前的时间
   				# auto_now=True  新增和编辑数据时自动保存当前的时间
BooleanField  布尔类型
TextField     大文本
FloatField  浮点型
DecimalField  10进制小数   # 999.99
			# max_digits,小数总长度   5
            # decimal_places,小数位长度  2
    

自定义一个char类型

class MyCharField(models.Field):
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)
 
    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length

字段参数

null=True   该字段在数据库可以为空
blank=True  允许用户输入为空
db_column   数据库中字段的列名
default     默认值
db_index    建立索引
unique      唯一约束
verbose_name 显示的字段名
choices    用户选择的参数

表的参数

class Person(models.Model):
    pid = models.AutoField(primary_key=True)  # 主键
    name = models.CharField(verbose_name='姓名', unique=True, db_column='nick',
                            max_length=32, null=True, blank=True)  # varchar(32)
    age = models.IntegerField(default=18)
    phone = MyCharField(max_length=11, unique=True)
    gender = models.BooleanField(choices=((True, 'male'), (False, 'female')))
    birth = models.DateTimeField(auto_now=True)

    # auto_now_add=True 新增数据时自动保存当前的时间
    # auto_now=True  新增和编辑数据时自动保存当前的时间

    def __str__(self):
        return "{}-{}".format(self.name, self.age)

    class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "person"

        # admin中显示的表名称
        verbose_name = '个人信息'

        # verbose_name加s
        verbose_name_plural = '所有用户信息'
      
        # 联合索引
        index_together = [
            ("name", "age"),  # 应为两个存在的字段
        ]
        #
        # # 联合唯一索引
        unique_together = (("name", "age"),)  # 应为两个存在的字段


使用django的admin:
  1. 创建一个超级用户

    python manage.py createsuperuser

  2. 在app下的admin.pu中注册model

    from django.contrib import admin
    from app01 import models
    
    # Register your models here.
    admin.site.register(models.Person)
    
  3. 登录http://127.0.0.1:8000/admin/操作表



必知必会13条
https://www.cnblogs.com/alex3174/p/11608374.html

返回对象列表  
all     查询所有的数据
filter   查询所有满足条件的数据
exclude  查询所有不满足条件的数据
values     查询数据的字段和值   [ {}]
values_list   查询数据的值  [ () ]
order_by   排序  默认升序 -  多字段排序 age id 
reverse   对已经排好序的queryset翻转
distinct  去重

返回对象
get   获取有且唯一的对象
first 第一个
last 最后一个

返回布尔值
exists   

数字 
count  

# 限制结果集  不能用负的下标
# 切片 取前两条记录
# data = User.objects.order_by('username')[:2]
# 从下标为4记录到下标为9的记录
# data = User.objects.order_by('uid')[4:10]

单表的双下划线

ret = models.Person.objects.filter(pid__lt=6)  # 字段__条件 =    less than 小于
ret = models.Person.objects.filter(pid__gt=6)  # 字段__条件 =    greater than 大于
ret = models.Person.objects.filter(pid__lte=6)  # 字段__条件 =   less than equal 小于等于
ret = models.Person.objects.filter(pid__gte=6)  # 字段__条件 =   greater than equal 大于等于

ret = models.Person.objects.filter(pid__range=[1,6])    # 范围
ret = models.Person.objects.filter(pid__in=[1,5,6])     # 成员判断

ret = models.Person.objects.filter(name__contains='alex')   # like
ret = models.Person.objects.filter(name__icontains='alex')  # like ignore忽略   忽略大小写

ret = models.Person.objects.filter(name__startswith='aaa')  # 以什么开头
ret = models.Person.objects.filter(name__istartswith='aaa') # 以什么开头 忽略大小写

ret = models.Person.objects.filter(name__endswith='aaa')   # 以什么结尾
ret = models.Person.objects.filter(name__iendswith='aaa')  # 以什么结尾 忽略大小写

ret = models.Person.objects.filter(birth__year='2019')
ret = models.Person.objects.filter(birth__contains='-01-')

ret = models.Person.objects.filter(name__isnull=True)   # 是否为null

外键的操作

class Publisher(models.Model):
	name = models.CharField(max_length=32)


class Book(models.Model):
	name = models.CharField(max_length=32)
	pub = models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books')  # pub_id
    
    
 #  基于对象的查询
book_obj = Book.objects.get(pk=1)
# 正向查询
book_obj.pub      # 所关联的对象    
book_obj.pub_id   # 所关联的对象的id
# 反向查询
pub_obj = Publisher.objects.get(pk=1)

# 不指定related_name  使用 类名小写_set
pub_obj.book_set  # 关系管理对象
pub_obj.book_set.all()  # 所关联的所有的书籍对象

# 指定related_name='books'
pub_obj.books.all()

# 基于字段的查询
ret = Book.objects.filter(name='xxxx')
ret = Book.objects.filter(pub__name='出版社')

ret = Publisher.objects.filter(name='出版社')

# 不指定related_name
ret = Publisher.objects.filter(book__name='xxx')
# 指定related_name='books'
ret = Publisher.objects.filter(books__name='xxx')
# 指定related_query_name='xxx'
ret = Publisher.objects.filter(xxx__name='xxx')



################### 基于对象的查询 ####################

# 正向查询
book_obj = models.Book.objects.get(pk=1)
# print(book_obj.pub)  # 关联的出版社对象
# print(book_obj.pub_id)  # 关联的出版社的id

# 反向查询
pub_obj = models.Publisher.objects.get(pk=1)
# 没有指定related_name  类名小写_set
# print(pub_obj)
# print(pub_obj.book_set,type(pub_obj.book_set))   # 类名小写_set   关系管理对象
# print(pub_obj.book_set.all())

# 指定related_name='books'  没有 类名小写_set 的写法



################### 基于字段的查询 ####################
ret = models.Book.objects.filter(pub__name='新华出版社')

# 不指定related_name='books'    类名小写
# ret = models.Publisher.objects.filter(book__name='坐在床上学xx')

# 指定related_name='books'  不指定related_query_name
# ret = models.Publisher.objects.filter(books__name='坐在床上学xx')

# 指定related_query_name='book'
# ret = models.Publisher.objects.filter(book__name='坐在床上学xx')

多对多的操作

author_obj = models.Author.objects.get(pk=1)


# 关系管理对象的方法
# all  查询所有的对象

# set 设置多对多的关系  [id,id]  [对象,对象]
# author_obj.books.set([1,2])
# author_obj.books.set(models.Book.objects.filter(id__in=[3, 4]))

# add  添加多对多关系  id或者对象
# author_obj.books.add(1, 2)
# author_obj.books.add(*models.Book.objects.filter(id__in=[5, 6]))

# remove  删除多对多关系  id或者对象
# author_obj.books.remove(1, 2)
# author_obj.books.remove(*models.Book.objects.filter(id__in=[5, 6]))

# clear   清空多对多关系
# author_obj.books.clear()

# create  新建一个对象和当前的对象建立关系
# author_obj.books.create(name='天然懵逼了没',pub_id=1)
ret = book_obj.authors.create(name='天然')

###################  外键  ###################

# set  add  create   参数只能写对象 不能写id
# pub_obj.books.set(models.Book.objects.filter(id__in=[1,2,3,4] ))
pub_obj.books.add(*models.Book.objects.all())

# remove clear  外键字段参数 null=True 才有这两个方法
# pub_obj.books.clear()

class Book(models.Model):
	name = models.CharField(max_length=32)
	pub = models.ForeginKey('Publisher',on_delete=models.CASCADE,related_name='books')  # pub_id

class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToMantField('Book')

author_obj.books  # 管理管理对象
# all  查询所有关联的对象
author_obj.books.all()  

# set  设置关系   [1,2]    [对象,对象]
author_obj.books.set([1,2])  # []

# add  新增关系    id   对象
author_obj.books.add(1,2) 

# remove  删除关系    id   对象
author_obj.books.remove(1,2) 

# clear  清空关系
author_obj.books.clear() 

# create  新增一个所关联的对象,并且和当前的对象绑定关系
author_obj.books.create(name='xxx',pub_id='1')


# 一对多关系中 反向查询获取到的关系管理对象  
# 参数中只能使用对象,不能使用id 
# 当ForeginKey中有参数null=True时,才有remove、clear方法
    


聚合和分组
from django.db.models import Max, Min, Count, Sum, Avg

# 聚合 aggregate  终止子句
ret = models.Book.objects.filter(id__gt=2).aggregate(min=Min('price'),max=Max('price')) 

#  分组   group by
# annotate  注释   过程中使用了分组
# 1. 统计每一本书的作者个数
ret = models.Book.objects.annotate(Count('authors')).values()  # 添加额外的信息

# 2. 统计出每个出版社卖的最便宜的书的价格
# 方法一
ret = models.Publisher.objects.annotate(Min('book__price')).values()

# 方法二
########## 错误写法 ##########
# ret = models.Book.objects.annotate(Min('price')).values()  按照书分组
########## 正确写法 ##########
# 按照 pub_id  pub__name 分组
ret = models.Book.objects.values('pub','pub__name').annotate(min=Min('price'))

# 3. 统计不止一个作者的图书
ret = models.Book.objects.annotate(count=Count('authors')).filter(count__gt=1)

# 4. 根据一本图书作者数量的多少对查询集 QuerySet进行排序
ret = models.Book.objects.annotate(count=Count('authors')).order_by('-count')

# 5.查询各个作者出的书的总价格
ret = models.Author.objects.annotate(sum=Sum('books__price')).values()
ret = models.Book.objects.values('authors','authors__name').annotate(sum=Sum('price'))

F和Q

from django.db.models import F, Q

ret = models.Book.objects.filter(kucun__lt=50)

ret = models.Book.objects.filter(sale__gt=F('kucun'))  # where  'sale' > 'kucun'

# ret = models.Book.objects.filter(id__lte=3).update(sale=F('sale') * 2 + 13)

# Q()
# |   或
# &   与
# ~   非

ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=5))

ret = models.Book.objects.filter(Q(Q(id__lt=3) | Q(id__gt=5))&Q(name__startswith='天然'))

ret = models.Book.objects.filter(Q(Q(id__lt=3) | Q(id__gt=5))&~Q(name__startswith='天然'))

事务

from app01 import models
from django.db.models import F
from django.db import transaction

try:
    with transaction.atomic():
        # 一系列的操作
        models.Book.objects.all().update(kucun=F('kucun') - 10)
        models.Book.objects.all().update(sale=F('sale') + 10)
except Exception as e:
    print(e)


cookie

保存在浏览本地上的一组组键值对

Http协议是无状态协议,每次请求之间都是相互独立,没有办法保存状态。使用cookie保存状态。

特性:

  1. 由服务器让浏览器进行设置的
  2. cookie信息保存在浏览器本地的,可以浏览器有权不保存
  3. 浏览器再次访问时自动携带对应的cookie

过程

  1. 第一个请求,没有cookie,设置键值对 ,根据浏览器生成一个唯一标识,给一个字典设置键值对。

  2. 将字典转成字符串(json序列化),进行加密,将唯一标识和字符串保存在数据库中(django_sessoion)。

    3. 返回给浏览器唯一标识(sessionid)的cookie

    4. 下次请求携带sessionid,服务器根据session找到对应的数据(session_data),进行解密,进行反序列化,根据key获取对应的值。

django中操作cookie

# 设置cookie:
response.set_cookie(key,value)  # Set-Cookie: is_login=1
response.set_signed_cookie('key', value, salt='s28')  # 加密cookie
设置cookie的参数
# max_age  超时时间
# path  cookie生效的路径
# secure=True  https进行传输
# httponly=True   只能传输,无法被JavaScript获取

# 获取cookie 
request.COOKIES  {}    #  请求头 Cookie: is_login=1;
request.get_signed_cookie('key', salt='s28',defalut='')  # 加密cookie

# 删除cookie   设置cookie 值为空  超时时间为0
response.delete_cookie(key)

# 设置cookie  响应头 Set-cookie
ret = HttpResponse('xx')
ret.set_cookie(key,value,max_age=5,path='/',)   # ret['Set-cookie'] = 'key:value;'
ret.set_signed_cookie(key,value,salt='',max_age=5,path='/',)
# max_age 超时时间 
# path  生效的路径

# 获取cookie    cookie的请求头
request.COOKIES  {}   [key]  .get(key)
request.get_signed_cookie(key,salt='',default='')

# 删除cookie   Set-cookie   key:''   max_age=0
ret = HttpResponse('xx')
ret.delete_cookie(key)



session

​ 保存在服务器上的一组组键值对,依赖于cookie使用

为什么要使用session?

1.  cookie是保存在浏览器本地,不太安全
2.  浏览器会对cookie的大小和个数有一定限制的

django中操作session

# 设置session
request.session[key] = value
# 获取
request.session[key]   request.session.get(key)
# 删除 
del request.session[key]
request.session.pop(key)

# session_key
request.session.session_key


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


# 删除当前用户的所有的session数据
request.session.delete()   # 不删除cookie
request.session.flush()	   # 删除cookie

# 设置会话Session和Cookie的超时时间
request.session.set_expiry(value)


###############################
# 设置session
reqeust.session[key] = value

# 获取
reqeust.session[key]
reqeust.session.get(key)

# 删除
request.session.pop(key)   del reqeust.session[key] 
request.session.delete()   # 删除所有的键值对   不删除cookie
request.session.flush()    # 删除所有的键值对   也删除cookie

# 其他
默认的超时时间(2周)
request.session.set_expiry(value)     # 设置超时时间
request.session.clear_expired()    # 清除已经失效的session数据


from django.conf import global_settings

SESSION_COOKIE_NAME = 'sessionid' 
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
SESSION_SAVE_EVERY_REQUEST = True  # 每次请求都更新session数据
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # cookie在浏览器关闭时就失效
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 默认是数据库  文件  缓存  数据库+缓存  加密cookie

配置

from django.conf import global_settings
SESSION_COOKIE_NAME = 'session'
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2
SESSION_SAVE_EVERY_REQUEST = True  # 每次请求后更新超时时间
SESSION_EXPIRE_AT_BROWSER_CLOSE = True  # 关闭浏览器cookie就失效
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
# session 保存的位置
# 默认是数据库  缓存  缓存+数据库 文件 加密cookie
from  django.contrib.sessions.backends import db



中间件

Django中处理请求和相应的框架级别的钩子。本质上就是一个类,类定义5个方法,特定时执行这些方法。

# 定义
from django.utils.deprecation import MiddlewareMixin

class MD1(MiddlewareMixin):
	def process_request(self,request):
		pass
		
# settings中要注册
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'app01.middlewares.my_middleware.MD1',
    'app01.middlewares.my_middleware.MD2',
]

5个方法,4个特征

执行时间 执行顺序 参数 返回值



process_request(self, request)

执行时间: 视图函数之前

参数:

​ request 请求的对象 和 视图函数是同一个对象

执行顺序:

​ 按照注册的顺序 顺序执行

返回值:

​ None 正常流程

​ HttpResponse 不执行后续的中间件的

process_request、路由匹配、process_view、视图

都不执行,直接执行当前中间的process_response方法



process_response(self, request, response)

执行时间: 视图函数之后

参数:

​ request 请求的对象 和 视图函数是同一个对象

​ response 响应对象

执行顺序:

​ 按照注册的顺序 倒序执行

返回值:

​ HttpResponse 必须返回



process_view(self, request, view_func, view_args, view_kwargs)

执行时间: 路由匹配之后,视图函数之前

参数:

​ request 请求的对象 和 视图函数是同一个对象

​ view_func 视图函数

​ view_args 视图函数的位置参数

​ view_kwargs 视图函数的关键字参数

执行顺序:

​ 按照注册的顺序 顺序执行

返回值:

​ None 正常流程

​ HttpResponse 之后中间件的process_view、视图都不执行,执行最后一个中间件的process_response方法



process_exception(self, request, exception)

执行时间(触发条件): 视图中有异常才执行

参数:

​ request 请求的对象 和 视图函数是同一个对象

​ exception 异常的对象

执行顺序:

​ 按照注册的顺序 倒序执行

返回值:

​ None 当前的中间件没有处理异常,交给下一个中间件处理异常,如果都没有处理异常,django处理异常

​ HttpResponse 当前中间件处理了异常,后面的中间件的process_exception就不执行,执行最后一个中间件的process_response方法



process_template_response(self,request,response)

执行时间(触发条件): 视图中返回的对象是TemplateResponse对象

参数:

​ request 请求的对象 和 视图函数是同一个对象

​ response TemplateResponse的对象

执行顺序:

​ 按照注册的顺序 倒序执行

返回值:

​ HttpResponse TemplateResponse的对象

​ 过程处理模板的名字 参数

​ response.template_name

​ response.context_data



django请求的生命周期

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wq1vQ5DM-1653464368408)(C:/Users/hp/AppData/Local/Temp/360zip$Temp/360(0/assets/1168194-20180719084357413-1778333372.png)]



JSON

轻量级的文本数据交换格式

python

支持的数据类型:

​ 字符串 数字 布尔值 列表 字典 None

序列化

​ python的数据类型 ——》 json字符串

反序列话

​ json字符串 ——》 python的数据类型

ajax 是一个js的技术,异步发送请求的。

ajax特点:

1. 异步
2. 局部刷新
3. 传输的数据量小

简单使用

$.ajax({
    url:'/calc/',
    type:'get',
    data:{
        'x1':$('[name="i1"]').val(),
        'x2':$('[name="i2"]').val(),
    },
    success:function (data) {
        $('[name="i3"]').val(data)
    }
})

ajax传收数据:

$.ajax({
    url: '/test/',
    type: 'get',
    data: {
        name: 'alex',
        age: 84,
        hobby: JSON.stringify(['抽烟', '喝酒', '画大饼'])
    },
    success: function (data) {
        console.log(data)
        console.log(data.status)

    }
})

使用ajax上传文件

$('#b1').click(function () {

    var formData = new FormData();  // multipart/form-data
    formData.append('name', 'alex');
    formData.append('f1', $('#f1')[0].files[0]);
    {#formData.append('f1',document.getElementById('f1').files[0]);#}

    $.ajax({
        url: '/upload/',
        type: 'post',
        data: formData,
        processData: false, //  ajax不处理数据的编码
        contentType: false, //   不修改content-type的请求头
        success: function (data) {
            alert(data)
        }

    })

})
from django.views.decorators.csrf import csrf_exempt, csrf_protect,ensure_csrf_cookie
csrf_exempt   加在视图上  该视图不需要进行csrf校验
csrf_protect  加在视图上  该视图需要进行csrf校验
ensure_csrf_cookie  加在视图上  确保返回时设置csrftoken的cookie


csrf的校验
从cookie中获取csrftoken的值
从request.POST中获取csrfmiddlewaretoken的值或者从请求头中获取x-csrftoken的值
把这两个值做对比,对比成功就接受请求,反之拒绝

前提:必须有csrftoken的cookie

  1. 使用{% csrf_token %}

  2. 使用ensure_csrf_cookie的装饰器,加在视图上

    from django.views.decorators.csrf import ensure_csrf_cookie

让ajax可以通过django的csrf的校验:

  1. 给data添加csrfmiddlewaretoken的键值对
  2. 给headers添加x-csrftoken的键值对(导入文件的方式)

推荐的方式:导入文件 + 使用{% csrf_token %}

csrf的中间件的流程:

  1. process_request

    从cookie中获取了csrftoken的值,将该值放入request.META中

  2. process_view

    1. 判断视图是否使用了csrf_exempt的装饰器,使用了就不再进行csrf 的校验了

    2. 判断请求方式是否是get head potions trace:

      1. 如果是 直接接受该请求 不做csrf的校验

      2. 不是 这些请求 要进行csrf的校验:

        1. csrf_token = request.META.get(‘CSRF_COOKIE’) # cookie中csrftoken的值

        2. request_csrf_token = ‘’

        3. 请求方式是post,尝试从request.POST中获取csrfmiddlewaretoken的值

          request_csrf_token = request.POST.get('csrfmiddlewaretoken', '')
          
        4. 如果request_csrf_token = ‘’,再尝试从请求头中获取X-CSRFTOKEN的值

          request_csrf_token = request.META.get(settings.CSRF_HEADER_NAME, '')
          
        5. 比较csrf_token和request_csrf_token

          1. 比较成功 通过校验
          2. 比较不成功 拒绝请求


发请求的途径:
  1. 直接在地址栏输入地址 get
  2. form表单提交 method=‘post’ get/post
  3. a标签 get
  4. ajax

使用:

导入jquery.js

<script>
	var v1 = 'v1'
	$.ajax({
		url:'地址',
		type:'post'// 请求方式
        headers:{
        	x-csrftoken:'asdasdkljlqjklqewqweweqwe'
    },
		data:{
			k1: v1,
            k2:'v2',
        	// csrfmiddlewaretoken:'xasdaskklqjwkleqweqweqweqweqwe'
        
		},
		success:function(data) {  // data 是返回响应的响应体 
			//  后续的操作	
		
		}
	
	})

</script>

上传文件

html

<input type='file'  id='f1'>
<button id='b1' >上传</button>


<script>
	
	$('#b1').click(function(){
		
		var formData = new FormData()
		formData.append('csrfmiddlewaretoken','sasdqljqweqwljeljqjlqwjewe')
		formData.append('xxx',$('#f1')[0].files[0])
	
		$.ajax({
            url:'地址',
            type:'post', // 请求方式
            processData:false,   //   不需要处理编码 formdata
            contentType:false,	 //  不处理content-type 的请求头
            data:formData,
            success:function(data) {  // data 是返回响应的响应体 
                //  后续的操作	
		
		}
	
	})
	
	})

</script>

form组件定义

class RegForm(forms.Form):
    user = forms.CharField(label='用户名')
    pwd = forms.CharField(label='密码')

使用:

def reg2(request):
    form_obj = RegForm()  # 空的form
    if request.method == 'POST':
        # 对提交的数据做校验
        form_obj = RegForm(request.POST)  # 包含用户提交的数据的form
        if form_obj.is_valid():  # 对数据进行校验
            # 校验成功
            return HttpResponse('注册成功')

    return render(request, 'reg2.html', {'form_obj': form_obj})

模板:

{{ form_obj.as_p }}   一次性生成所有的input框

{{ form_obj.user }}    		该字段的input框
{{ form_obj.user.label }}   该字段提示的信息
{{ form_obj.user.id_for_label }}    该字段的input框的id
{{ form_obj.user.errors }}      该字段的所有错误 
{{ form_obj.user.errors.0 }}    该字段的第一个错误 

{{ form_obj.errors }}   所有字段的错误


常用字段

CharField   文本输入框
ChoiceField    单选  默认是select
MultipleChoiceField	  多选  默认是select

字段参数

initial       默认值
choices      用户选择的数据
error_messages   自定义错误信息
widget  	 插件    修改input框的类型
required	 是否必填
disabled	 是否禁用

完整使用

# 定义form
form django import forms

class RegForm(forms.Form):
    user = forms.CharField(
        label='用户名',
        min_length=11,
        inital='初始值',
        required=True,
        disabled=False,
        error_messages={
            'required':'xxxx',
            'min_length':'xxssw'
        }
        validators=[自定义的函数,内置的校验器]
        
    )
    password = forms.CharField(widget=forms.PasswordInput)

    
url(^'reg',reg)
    
  
def reg(request):
    form_obj = RegForm()
    if request.method == 'POST':
        form_obj = RegForm(request.POST)
        # 对提交的数据进行校验
        if form_obj.is_valid():
            # 校验成功做的操作
            form_obj.cleaned_data   # request.POST
    
    return  render(request,'reg.html',{'form_obj':form_obj})

校验

校验器:

  1. 定义函数

    def check_name(value):
        # 不符合校验规则
        if 'alex' in value:
            raise ValidationError('alex的名字不符合社会主义价值观')
        # 符合校验不做任何操作
        
    user = forms.CharField(
      		...
            validators=[check_name],)
            
            
    #########################################
    from django.core.exceptions import ValidationError
    def xxx(value):
        # 写校验规则
        # 校验成功 什么都不做 return
        # 校验失败 抛出异常 ValidationError('提示信息')
    
  2. 使用内置的校验器

    from django.core.validators import RegexValidator
    
    phone = forms.CharField(validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式不正确')])
    

钩子函数:

  1. 局部钩子

    # 一定要写在类中    
        def clean_user(self):
            # 不符合校验规则 抛出异常
            value = self.cleaned_data.get('user')
            if 'alex' in value:
                raise ValidationError('alex的名字不符合社会主义价值观')
            # 符合校验规则   返回该字段的值
            return value
        
        ###########################
        class RegForm(forms.Form):
        user = forms.CharField()    
        
        def clean_user(self):
        	# 校验
            # 校验成功  返回该字段的值
            # 校验失败  抛出异常 
    
  2. 全局钩子

        def clean(self):
            # 全局钩子
            pwd = self.cleaned_data.get('pwd')
            re_pwd = self.cleaned_data.get('re_pwd')
            if pwd != re_pwd:
                # 不符合校验规则 抛出异常
                # 将错误信息加入到某个字段中
                self.add_error('re_pwd','两次密码不一致!!!')
                raise ValidationError('两次密码不一致')
            # 符合校验规则   返回所有字段的值
            return self.cleaned_data
        
        
        ###############################
        class RegForm(forms.Form):
        user = forms.CharField()
        def clean(self):
        	# 校验
            # 校验成功  返回所有字段的值  self.cleaned_data
            # 校验失败  抛出异常
    

is_valid()的流程:

  1. 执行一个full_clean方法

    1. 定义一个 错误字典 和 cleaned_data = {} # 存在已经经过校验的数据的字典、

    2. 执行self._clean_fields()

      循环所有的字段

      对一个字段进行内置的校验、校验器的校验、局部钩子的校验

      校验不通过,错误字典中有该字段的错误信息

      所有校验通过,self.cleaned_data有该字段的值

    3. 执行全局钩子

定义的modelform

from django import forms


class RegForm(forms.ModelForm):
    # username = forms.CharField()class RegForm(forms.ModelForm):
    # username = forms.CharField(label='xxx')
    # password = forms.CharField()
    password = forms.CharField(error_messages={'required': '这是必选项'}, widget=forms.PasswordInput, label='密码',
                               min_length=6)
    re_pwd = forms.CharField(widget=forms.PasswordInput, label='确认密码', min_length=6)

    class Meta:
        model = models.User
        fields = '__all__'  # ['username','password']
        exclude = ['last_time']
        # labels = {
        #     'username': '用户名'
        # }
        widgets = {
            'password': forms.PasswordInput
        }
        error_messages = {
            'username': {
                'required': '必填项',
            },
            'password': {
                'required': '必填项',
            }
        }

    def clean_phone(self):
        import re
        phone = self.cleaned_data.get('phone')
        if re.match(r'^1[3-9]\d{9}$', phone):
            return phone
        raise ValidationError('手机号格式不正确')

    def clean(self):
        self._validate_unique = True  # 数据库校验唯一
        password = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_pwd')

        if password == re_pwd:
            return self.cleaned_data
        self.add_error('re_pwd', '两次密码不一致!!')
        raise ValidationError('两次密码不一致')



一个小例子(登录、注册)
# models.py
class User(models.Model):
    """
    员工信息表用户、密码、职位、公司名(子、总公司)、手机、最后一次登录时间。
    """
    username = models.CharField(max_length=32, verbose_name='用户名',unique=True)
    password = models.CharField(max_length=32, verbose_name='密码')
    position = models.CharField(max_length=32, verbose_name='职位')
    company = models.CharField(max_length=32, verbose_name='公司',
                               choices=(('0', '北京总公司'), ('1', '石家庄分公司'), ('2', '广州分公司')))
    phone = models.CharField(max_length=11, verbose_name='手机号')
    last_time = models.DateTimeField(null=True, blank=True, verbose_name='上次登陆时间')
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='注册时间')

form

from django import forms
from app01 import models

# form 
class RegForm(forms.Form):
	username = forms.Charfield(label='用户名')
    password = forms.Charfield(widget=forms.PasswordInput)

# modelform
class RegForm(forms.ModelForm):
    
    password = forms.CharField(widget=forms.PasswordInput, label='密码',min_length=6)
    re_pwd = forms.CharField(widget=forms.PasswordInput, label='确认密码', min_length=6)
    
    class Meta:
        model = models.User
        fields =  '__all__'  # ['username','password']
        exclude = ['last_time']
        
        labels = {
            'username':'用户名'
        }
        
        widgets = {
            'password':forms.PasswordInput
            
        }
        error_messages = {
            'username':{}
        }
    
    
    def clean_password(self):
        password = self.cleaned_data.get('password')
        # 通过校验   返回该字段的值
        # 不通过校验 抛出异常ValidationError
    
    def clean(self):
        self._validate_unique = True # 校验数据的唯一性
        # 通过校验   返回所有字段的值 self.cleaned_data
        # 不通过校验 抛出异常ValidationError
        password = self.cleaned_data.get('password')
        re_pwd = self.cleaned_data.get('re_pwd')
        if re_pwd == password:
            return self.cleaned_data
        self.add_error('re_pwd','两次密码不一致')
     	raise ValidationError('两次密码不一致')   # '__all__'
        
        
def reg(request,*args,**kwargs):
    form_obj = RegForm()
    if request.method == 'POST':
        form_obj = RegForm(request.POST)  # 包含用户提交的数据
        if form_obj.is_valid():   # 对数据进行校验
            form_obj.save()       # 新增上数据
            return redirect('login')
    return render(request,'reg.html',{'form_obj':form_obj})


# html


{{  form_obj.username }}     input{{  form_obj.username.label }}    label提示

{{  form_obj.username.id_for_label }}       input框的id   

{{  form_obj.username.errors.0 }} 



一个小项目

需求:

  1. 博客的首页

    • 展示所有的文章 1
  2. 文章详情页 3

    • 展示文章信息 3.1
    • 文章下有评论信息 3.2
    • 评论框,当前作者提交评论 3.3
  3. 后台的管理 2

    • 分类管理 2.1

      • 表格展示
      • 新增和编辑
    • 文章管理 2.2

      • 表格展示
      • 新增和编辑

展示数据:

给模板一个字querySet 对象列表,循环出对象obj。

  1. 普通字段

    obj.字段名 ——》 数据库中的数据

  2. 外键

    obj.外键 ——》 外键的对象 给类定义

    __str__

    的方法

    obj.外键.字段

  3. 带choices参数的

    obj.字段名 ——》 数据库中的数据 这个显示不是很好


    obj.get_字段名_display()

    显示定义好的结果

  4. 自定义方法

    def show_publish_status(self):
        color_dict = {True: 'green', False: '#c35353'}
    
        return mark_safe(
            '<span style="background: {};color: white;padding: 3px" >{}</span>'.format(color_dict[self.publish_status],self.get_publish_status_display()))
    

展示数据的方法:

数据对象 obj

  1. 普通字段

    obj.字段名 ——》 数据库该字段的值

  2. 带choices参数的

    obj.字段名 ——》 数据库该字段的值


    obj.get_字段名_display()

    要显示的结果

  3. 外键

    obj.外键 ——》 所关联的对象

    __str__

    方法

    obj.外键.字段

  4. 自定义方法

    from django.utils.safestring import mark_safe
    def show_publish_status(self):
        color_dict = {True: 'green', False: '#c35353'}
    
        return mark_safe(
            '<span style="background: {};color: white;padding: 3px" >{}</span>'.format(color_dict[self.publish_status],
                                                                                       self.get_publish_status_display()))
    

定义modelform

class ArticleForm(forms.ModelForm):
    class Meta:
        model = models.Article
        fields = "__all__"
        exclude = ['detail']

        # widgets = {
        #     'title': forms.TextInput(attrs={'class': 'form-control'})
        # }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # 自定义的操作
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

使用form

def article_add(request):
    form_obj = ArticleForm()
    if request.method == 'POST':
        form_obj = ArticleForm(request.POST)
        if form_obj.is_valid():
            #  获取文章详情的字符串
            detail = request.POST.get('detail')
            #  创建文章详情的对象
            detail_obj = models.ArticleDetail.objects.create(content=detail)
            form_obj.instance.detail_id = detail_obj.pk
            form_obj.save()  # form_obj.instance.save()
            return redirect('article_list')

    return render(request, 'article_add.html', {'form_obj': form_obj})


# 编辑文章
def article_edit(request, pk):
    obj = models.Article.objects.filter(pk=pk).first()
    form_obj = ArticleForm(instance=obj)
    if request.method == 'POST':
        form_obj = ArticleForm(request.POST, instance=obj)
        if form_obj.is_valid():
            form_obj.instance.detail.content = request.POST.get('detail')
            form_obj.instance.detail.save()   # 保存文章详情
            form_obj.save()   # 保存文章的信息
            return redirect('article_list')

    return render(request, 'article_edit.html', {'form_obj': form_obj, 'obj': obj})



#  HTML


{% for field in form_obj %}

    {{ field.id_for_label }}
    
    {{ field.label }}

    {{ field }}    input{{ field.errors.0 }}

{% endfor %}


让用户上传头像

model中添加ImageField
avatar = models.ImageField(upload_to='img/avatar',default='img/avatar/dafault.jpeg')

# ImageField  依赖 pillow模块  pip install pillow

media的配置

seetings.py

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR,'media')

urls.py

from django.views.static import serve
from django.conf import settings

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('app01.urls')),
    url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]

使用django-ckeditor

https://pypi.org/project/django-ckeditor/

  1. 下载

    pip install django-ckeditor

  2. 注册app

    INSTALLED_APPS = [
     	...
        'ckeditor',
        'ckeditor_uploader',
    ]
    
  3. urls.py

    urlpatterns = [
       ...
        url(r'^ckeditor/', include('ckeditor_uploader.urls')),
    ]
    
  4. models.py

    class ArticleDetail(models.Model):
        """
        文章详情
        """
        content = RichTextUploadingField(verbose_name='文章内容')
    
  5. modelform

    class ArticleDetailForm(forms.ModelForm):
        class Meta:
            model = models.ArticleDetail
            fields = "__all__"
            
            
            
    form_obj = ArticleDetailForm()
    
  6. html

    {

    { form_obj. content }}

    引入静态文件

    {% load static %}
    <script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
    <script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
    
  7. 上传文件需要认证,取消认证

    # 将staff_member_required装饰器取消掉即可
    urlpatterns = [
        url(r'^upload/', staff_member_required(views.upload), name='ckeditor_upload'),
        url(r'^browse/', never_cache(staff_member_required(views.browse)), name='ckeditor_browse'),
    ]
    



新增和编辑

路由

url(r'^category_add/$', views.category_change, name='category_add'),
url(r'^category_edit/(\d+)$', views.category_change, name='category_edit'),

视图

def category_change(request, pk=None):
    obj = models.Category.objects.filter(pk=pk).first()  # pk=None   obj=>None
    form_obj = CategoryForm(instance=obj)
    if request.method == 'POST':
        form_obj = CategoryForm(request.POST, instance=obj)
        if form_obj.is_valid():
            form_obj.save()
            return redirect('category_list')

    title = '编辑分类' if pk else '新增分类'

    return render(request, 'form.html', {'form_obj': form_obj, 'title': title})

模板

{% extends 'dashboard.html' %}


{% block main %}
    <h1>{{ title }}</h1>

    <form class="form-horizontal" method="post" action="" novalidate>
        {% csrf_token %}

        {% for field in form_obj %}
            <div class="form-group {% if field.errors %}has-error{% endif %}">

                <label for="{{ field.id_for_label }}"
                       class="col-sm-2 control-label">{{ field.label }}</label>
                <div class="col-sm-8">
                    {{ field }}
                    <span class="help-block"> {{ field.errors.0 }} </span>
                </div>
            </div>
        {% endfor %}

        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default">保存</button>
            </div>
        </div>
    </form>

{% endblock %}

Q

q = Q()
# Q(Q(title__contains=query)|Q(detail__content__contains=query))
q.connector = 'OR'
q.children.append(Q(title__contains=query))
q.children.append(Q(detail__content__contains=query))


Q(title__contains=query)  #   Q(('title__contains',query))
 


def get_query(request, field_list):
    # 传入一个列表['title','detail__content'] ,返回一Q对象
    query = request.GET.get('query', '')

    q = Q()
    # Q(Q(title__contains=query)|Q(detail__content__contains=query))
    q.connector = 'OR'
    for field in field_list:

        q.children.append(Q(('{}__contains'.format(field),query)))
    # q.children.append(Q(detail__content__contains=query))

    return q

from django.http.request import QueryDict

request.GET._mutable = True
request.GET['page'] = 1

request.GET.copy()  # 返回一个可编辑的深拷贝

request.GET.urledncode()  # page=1&aa=111

展示数据的4个方法

对单标的新增和编辑

两个url + 一个视图(modelform) + 一个模板

评论框样式

http://www.jq22.com/demo/jQueryWbPl201705260102/

文章详情 模板

<!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">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <meta name="description" content="">
    <meta name="author" content="">

    <title>Blog Template for Bootstrap</title>

    <!-- Bootstrap core CSS -->
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">

    {% load static %}
    <!-- Custom styles for this template -->
    <link href="{% static 'css/blog.css' %}" rel="stylesheet">
    <link href="{% static 'css/comment/comment.css' %}" rel="stylesheet">
    <link href="{% static 'css/comment/style.css' %}" rel="stylesheet">


</head>

<body>

{% include 'nav.html' %}

<div class="container">

    <div class="blog-header">
        <h1>{{ article_obj.title }}</h1>
        <p class="lead blog-description">{{ article_obj.detail.content|safe }}</p>


        <div class="commentAll">

            <div class="reviewArea clearfix">
                <div class="flex-text-wrap">
                    <pre class="pre"><span></span><br></pre>
                    <textarea class="content comment-input" placeholder="Please enter a comment…"
                    ></textarea></div>
                <a href="javascript:;" id='comment' class="plBtn">评论</a>
            </div>


            <div class="comment-show">

                {% for comment in article_obj.comment_set.all %}
                    <div class="comment-show-con clearfix">
                        <div class="comment-show-con-img pull-left">
                            <img src="{{ comment.author.avatar.url }}" alt="">
                        </div>
                        <div class="comment-show-con-list pull-left clearfix">
                            <div class="pl-text clearfix">
                                <a href="#" class="comment-size-name">{{ comment.author.username }} : </a>
                                <span class="my-pl-con">&nbsp;{{ comment.content }}</span>
                            </div>
                            <div class="date-dz">
                                <span class="date-dz-left pull-left comment-time">{{ comment.time }}</span>
                                <div class="date-dz-right pull-right comment-pl-block">
                                    <a href="javascript:;" class="removeBlock">删除</a>
                                </div>
                            </div>
                            <div class="hf-list-con"></div>
                        </div>
                    </div>
                {% endfor %}




            </div>

        </div>

    </div>


</div><!-- /.container -->

<footer class="blog-footer">
    <p>Blog template built for <a href="http://getbootstrap.com">Bootstrap</a> by <a
            href="https://twitter.com/mdo">@mdo</a>.</p>
    <p>
        <a href="#">Back to top</a>
    </p>
</footer>


<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>


<script>

    $('#comment').click(function () {
        var content = $('.comment-input').val();
        var article_id = {{ article_obj.pk }};
        var author_id = {{ request.user_obj.pk }};
        var author_name = '{{ request.user_obj.username }}';
        var author_avatar = '{{ request.user_obj.avatar.url }}';
        $.ajax({
            url:'/comment/',
            data:{
                content:content,
                article_id:article_id,
                author_id:author_id,
            },
            success: (res) => {
                if (res.status){
                    // 评论插入到数据库成功了
                    $('.comment-show').append(`<div class="comment-show-con clearfix">
                    <div class="comment-show-con-img pull-left">
                        <img src="${author_avatar}" alt="">
                    </div>
                    <div class="comment-show-con-list pull-left clearfix">
                        <div class="pl-text clearfix">
                            <a href="#" class="comment-size-name">${author_name} : </a>
                            <span class="my-pl-con">${content}</span>
                        </div>
                        <div class="date-dz">
                            <span class="date-dz-left pull-left comment-time">${res.time}</span>
                            <div class="date-dz-right pull-right comment-pl-block">
                                <a href="javascript:;" class="removeBlock">删除</a>
                            </div>
                        </div>
                        <div class="hf-list-con"></div>
                    </div>
                </div>`)

                    $('.comment-input').val('')
                    alert('评论成功')
                }
            }

        })
    })
</script>


</body>
</html>

评论路由

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

视图

from django.http.response import JsonResponse

from django.utils import timezone


def comment(request):
    obj = models.Comment.objects.create(**request.GET.dict())

    return JsonResponse({'status': True, 'time': timezone.localtime(obj.time).strftime('%Y-%m-%d %H:%M:%S')})

Django模板语言、过滤器整理

https://blog.csdn.net/weixin_30621959/article/details/97725864?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&utm_relevant_index=1

<a href="#">Back to top</a>
</p>

“`

评论路由

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

视图

from django.http.response import JsonResponse

from django.utils import timezone


def comment(request):
    obj = models.Comment.objects.create(**request.GET.dict())

    return JsonResponse({'status': True, 'time': timezone.localtime(obj.time).strftime('%Y-%m-%d %H:%M:%S')})

Django模板语言、过滤器整理

https://blog.csdn.net/weixin_30621959/article/details/97725864?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_antiscanv2&utm_relevant_index=1



版权声明:本文为weixin_43241490原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。