Django

Django手册

1.django中所有的命令

  1. 下载django

    pip install django==1.11.25 -i 源

  2. 创建django项目

    django-admin startproject 项目名

  3. 启动项目

    切换到项目目录下

    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

  4. 创建app

    python manage.py startapp app名称

  5. 数据库迁移的命令

    python manage.py makemigrations # 检测已经注册app下的models变更记录

    python manage.py migrate # 将变更记录同步到数据库中

2.django的配置

  1. 静态文件

    STATIC_URL = '/static/' # 静态文件的别名

    STATICFILES_DIRS = [ # 静态文件存放的路径

    ​ os.path.join(BASE_DIR ,'static')

    ]

  2. 数据库

    ENGINE mysql

    NAME 数据库名称

    HOST ip

    PORT 端口号

    USER 用户名

    PASSWORD 密码

  3. 中间件

    注释掉 一个 csrf (可以提交POST请求)

  4. app

    INSTALLED_APPS = [

    ​ 'app名称'

    ​ ‘app名称.apps.app名称Config’

    ]

  5. 模板

    TEMPLATES DIRS =[ os.path.join(BASE_DIR ,'templates')]

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

  1. 创建一个mysql数据库

  2. 配置数据库连接信息

    ENGINE mysql

    NAME 数据库名称

    HOST ip

    PORT 端口号

    USER 用户名

    PASSWORD 密码

  3. 告诉django使用pymysql替换Mysqldb

    在与settings同级目录下的init文件中写:

    import pymysql
    pymysql.install_as_MySQLdb()
    
  4. 在models中写类(继承models.Model)

    class Publisher(models.MOdel):
        pid = models.AutoField(primry_key=True)
        name = models.CharField(max_length=32,unique=True)
    
  5. 执行数据库迁移的命令

    python manage.py makemigrations # 检测已经注册app下的models变更记录

    python manage.py migrate # 将变更记录同步到数据库中

4.get和post的区别

发送get的方式:

  1. 地址栏中输入地址 回车
  2. a 标签
  3. from表单

?k1=v1&k2=v2 request.GET.get(key) # url携带的参数

发送post请求:

​ form表单 method ='post'

form表单

1. action=''   向当前地址进行提交  method ='post'
  1. 所有的input需要有name属性 value
  2. 需要有button或者一个type=‘submit’的input框

request.POST.get(key)

5.ORM

对象关系映射

对应关系

​ 类 ——》 表

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

​ 属性 ——》 字段

ORM操作

查询
from app01 import  models
models.Publisher.objects.all()   #  查询所有的数据    QuerySet 【对象】    对象列表
models.Publisher.objects.get(name='xxx')  #  查询有且唯一的对象  没有或者是多个就报错
models.Publisher.objects.filter(name='xxx')   # 查询所有符合条件的对象   QuerySet 【对象】    对象列表

for  i in models.Publisher.objects.all() :
	print(i)    #  Publisher object   __str__()
    print(i.name)
新增
models.Publisher.objects.create(name='xxxx',addr='xxxx')

obj = models.Publisher(name='xxxx',addr='xxxx')
obj.save()
删除
models.Publisher.objects.filter(pid=pid).delete()
models.Publisher.objects.get(pip=pid).delete()
编辑
pub_obj.name = 'xxxx'
pub_obj.addr = 'xxxx'
pub_obj.save()

6.模板语法

render(request,‘模板的文件名’,{ key:value })

{{  key }}  ——》 value的值

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

{% endfor  %}

今日内容

书籍的管理

外键 描述一对多的关系

class Book(models.Model):
    title = models.CharField(max_length=32, unique=True)
    pub = models.ForeignKey('Publisher',on_delete=models.CASCADE)
  
on_delete在2.0版本中是必填的
on_delete=models.CASCADE(级联删除)
		  models.SET()
    	  models.SET_DEFAULT   default=值
		  models.SET_NULL      null=True
          models.DO_NOTHING

展示

    all_books = models.Book.objects.all()  # 对象列表
    for book in all_books:
        print(book)
        print(book.title,type(book.title))
        print(book.pub)     # 外键关联的对象
        print(book.pub_id)  # 关联对象的id  直接从数据库查询

新增

models.Book.objects.create(title=book_name,pub=models.Publisher.objects.get(pk=pub_id))
models.Book.objects.create(title=book_name,pub_id=pub_id)

模板语法

{% if  条件  %}
	满足条件显示的值
{% endif  %}


{% if  条件  %}
	满足条件显示的值
{% else %}
	不满足条件显示的值
{% endif  %}


{% if  条件  %}
	满足条件显示的值
{% elif  条件1  %}
	满足条件显示的值
{% else %}
	不满足条件显示的值
{% endif  %}

Django多对一操作

# 这里列举出版社与书的需求
# 需求:一个出版社能出版多本图书,没有一本图书被多个出版社出版(这里的数据库表结构是1对多)→1个出版社对应多本书。
1.创建一个Django项目命名为library,然后将app命名为app01。

2.library→settings.py配置静态路径
STATIC_URL = '/static/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]
# 这个路径如果弄错了可能会导致网页的无法显示样式等等。小括号里的static可以随便起名字,规范用static。

2.1→注释MIDDLEWARE 防止校验出问题,目前是本地服务本地,暂不需要。 # 'django.middleware.csrf.CsrfViewMiddleware',

2.2→配置数据库DATABASES包括更改数据库引擎为mysql,
设置库的名字,数据库用户名,数据库密码,数据据库端口,数据库ip地址。
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'library',
        'USER': 'root',
        'PASSWORD': '123456',
        'PORT': 3306,
        'HOST': '127.0.0.1',
    }
}

2.3→这里补一点东西,是之前链接数据库,配置的东西就是基本的用户名,密码,端口,。。。记不清了,遇到百度。

2.4→数据库还有一个导入pymysql,可以放在settings里面,不过不规范,需要放在library→__init__.py里面
import pymysql
pymysql.install_as_MySQLdb() 

2.5→在项目/写static路径,并在stati文件夹里写上css,js,img,以及plugins。这里的plugins里面放入的是bootstrap-3.3.7-dist,这里面包装的有css,js样式。

3.1→
找到app名称/models.py在里面写入ORM数据库与面向对象结合的mysql语言。
# 其中这里要弄清面向对象里面类,对象,属性与数据库表,字段,数据值之间对应的关系。目前不知道是否与库有关系!!!类→表,对象→数据值,属性→字段。
然后在里面使用orm创建数据库的表,

from django.db import models

class Book(models.Models):
	title = models.CharField(max_length=32, unique=True)
	pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
然后打开pycharm左下角的Terminal,在命令台上输入以下命令导入mysql数据库。我安装了两个python这里用的是python3。
这里的manage.py是管理整个项目里面的东西的。
python3 manage.py makemigrations
python3 manage.py migrate


3.2→在pycharm中查看表结构。
点击有边框的Database,找到自己写的数据库,然后将在excel中写的表格copy到数据库中。也可以自己手写。
细节的自己知道懒得写。
id  title		pub
1	jin瓶梅		 1
2	回村的诱惑	4
3	jin瓶梅续集	4
4	爱绿的淫魔	1



4.1→找到urls文件,写入自己数据库要在网页中展示的网页的网段。例如/
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^book_list',views.book_list),
   ]
ctrl+点击view进入views中写数据库面向对象结合的ORM,也不太清楚,就这里写也可以写在urls里面不过不规范。

4.2→进入views里面写函数。
def book_list(request):
	# 1.首先获取所有的数据。
	all_books = models.Book.objects.all()#对象列表
#个人理解:models是模型,Book是自己创的表,objects是封装好的管理东西,必须和manage.book.object,单独book.objects拿出来会出错。,all()指的是所有的东西。
#其中Book相当于面向对象里面的类,objects相当于是面向对象里面的属性,这里的models.Book.objects.all()可以理解为从Django/app01里面的models模型使用类似面向对象里面的组合的关系,将Book点出来,然后再点出Book里面的对象,再点出所有的对象数值数据。
	# 2.将对象(数值)遍历出来。
	for book in all_books:
		print(book)
# book表(类)包括好多字段例如id,name,pirce....这里可以使用面向对象的组合关系点出类里的属性,下面就是如此。
		print(book.title,type(book.title))
		print(book.pub.name,type(book.pub.name))
# 如果现在访问urls地址book_list后端就会给我打印这个东西
	# 2.返回书籍展示页面。
	render
	return render()
	
# print(book.title,type(book.title)),print(book.pub.name,type(book.pub.name)),print(book.pub,type(book.pub)),print(book.pub.pid)
# 通过book表(类)点出本表的name,也可以点出本表的外键,通过外键在点出外键对应表的的字段或者是主键。








Django常用字段

  • 常用字段
AutoField
自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。
一个model不能有两个AutoField字段。

IntegerField
一个整数类型。数值的范围是 -2147483648 ~ 2147483647。

CharField
字符类型,必须提供max_length参数。max_length表示字符的长度。

<DateFiel>
日期类型,日期格式为YYYY-MM-DD,相当于Python中的datetime.date的实例。
参数:
auto_now:每次修改时修改为当前日期时间。
auto_now_add:新创建对象时自动添加当前日期时间。
auto_now和auto_now_add和default参数是互斥的,不能同时设置。
    
DatetimeField
日期时间字段,格式为YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime的实例。
字段类型,详情可点击查询官网。
    
  • 自定义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

<!-- -->
这段代码需要放在models的文件下,然后在自己的数据库中定义自己的字段例如
phone = MyCharField(max_length=11)
在生成表的时候就会出现选择自定义字段数值。
  • 创建超级用户
1,找到Terminal
2,python manage.py createsuperuser
3,密码需要输入两次。
4,找到app01/admin.py/输入
from app01 import models
admin.site.register(models.Person)相当于注册models。
5,如果想要在admin的网页中看到自己所创建的表,需要在models.py里面写上
def __str__(self):
	return self.name
6,打开django找到表里面有save的选项,(前提是表里字段数值有空的情况下),会提醒你表单的空格不能为空。
这里的解决办法是:如果想让你的数据库字段为空的情况下,需要在你创建表的时候字段后面输入null=True,blank=True
例如:phone = MyCharField(max_length=11,null=True,blank=True)
7,最后补充,在创建django的时候写入app01的下面会有
Enable Django admin 
点击选中即可
如果未选中的话,
urls.py下面的会注释如下:
# from django.contrib import admin
urlpatterns = [
# url(r'^admin/',admin.site.urls),
]
另外:
settings里面少admin可以自己加
'django.contrib.admin'

  • 字段参数详情
null  数据库中字段可以为空
db_column  数据库中字段的列名
<!--
class Person(models.Model):
    pid = models.AutoField(primary_key=True)

    name = models.CharField(max_length=32, db_column='username')

    age = models.IntegerField()  # integerfield 他是10位的不能存手机号
    birth = models.DateTimeField(auto_now=True)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    phone = MyCharField(max_length=11, null=True, blank=True)-->

(别人定义好数据库,自己不想用的话就需要自己改列名)
数据库和django数据库中显示的是username,但是在前端用的还是name和后面的没有一点关系。

  • 中文传参
default  默认值
db_index=True   索引
unique  唯一约束
verbose_name  显示字段中文名称,可以在models定义字段的前面,不需要写verbose_name,因为按照位置传参是第一个。
help_text ='不能为空'  admin/表字段的提示信息。
gender = models.BooleanField(choices=((True,'男'),(False,'女')))
在数据库中要选择1,然后下面还要选择1.
之后可以在admin中查询并选中,确认后页面会显示出选择的数值。

  • Model Meta参数
class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "table_name"
 
        # admin中显示的表名称
        verbose_name = '个人信息'
 
        # verbose_name加s
        verbose_name_plural = '所有用户信息'
 
        # 联合索引  
        index_together = [
            ("pub_date", "deadline"),   # 应为两个存在的字段
        ]
 
        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)   # 应为两个存在的字段
<!--
Models Meta
这段代码是要放在自己写的类函数下面的
可以在admin页面中分表的个人信息,和表的所用用户信息。
联合索引可以在字段中加索引值,为了是加快查询时间。
联合唯一索引,在索引的基础上加了唯约束。
也就是说加上联合唯一索引的东西会有唯一的约束了。
-->

具体查询必知必会13条

第一种:查询,设置url地址设置试图函数
第二种:查询更改,可以用到PythonConsole在pytho自制命令台进行查询。弊端,每次都需要重启,需要.save()去保存。
第三种:orm操作,是以文件的方式去执行。具体操作如下:
之前跑数据库的时候用的是manage.py runserver,现在也要用到manage.py所以需要导入
一:导入os里面的东西,因为manage.py里面就是导入了os既:
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")
二:然后导入django的安装
import django
django.setup()
三:导入django模板models
from app01 import models
ret = models.Person.objects.all()
然后下面就可以写入自己要查询的表的数据,并且以文件的方式跑起来。
一个网页两个项目
用于数据迁移,同步数据
<!--练习 -->
from app01 import models
ret = models.Person.objects.all()
ret1 = models.Person.objects.get(name='alex')
print(ret)
print(ret1)
它的这里调用了models里面的对象,然后执行了里面的__str__属性
def __str__(self):
	return self.name
返回的是一个字段的的属性字段。

Django的中间件(生命周期简单理解)

  • 前戏(有了装饰器为什么还要用中间件)
	学习cookie和session的时候我们做登陆校验的时候,做成装饰其了,每个函数需要登陆校验的时候,需要登陆之后才能访问的。如果后期试图函数很多,加装饰器就变得不方便了,去掉装饰器也不方便了。
	整个项目里面有很多东西功能都很统一,这个情况下用中间件,而不用装饰器。
  • Django里面有什么东西,Django请求的生命周期是什么样
Django请求宏观上的生命周期:
浏览器向Django发送请求(django内部给他做了些操作),django内部链接数据库,并把数据返回给浏览器。

Django请求微观上的生命周期:(细化)
[WSGI]
浏览器发送请求给Django的请求是谁接收的。→WSGI(本身就是socket,帮我们收发消息,可以给我们配置上不同服务器的,是服务器程序和应用程序之间的一种协议。)
功能:django从浏览器接收到请求并分给WSGI,然后由WSGI将其封装成request对象,发送到路由。
接收并发送给浏览器后,我们可以用WSGI封装成的request对象,通过request点到其中的内容或属性。

[路由]
功能:路由匹配→根据你写的url函数地址,找到相应的视图函数。



[视图]
(视图函数有两种写法,一种是FBV,一种是CBV,没太大区别)
功能:执行具体的函数或者方法,得到具体的结果。执行的过程中有其他的操作,比如从数据库中拿东西(通过model)orm操作。



[model]
功能:通过FBV或CBV从数据库中拿多西。然后数据库返回结果也走到视图。
 
@(线路1)[视图]→[model](不过路由了)→[WSGI](返回给浏览器返回的还是http的响应,按照http协议将其封装成hhtp响应(socket对象)给[比如响应头,响应体。。。])浏览器在解析,返回给客户看。


[temple]

@(线路2)[视图]→[template](视图去读取到模板,视图函数里python文件中给了一些变量,用模板语法与python文件变量交互)→[WSGI]也是封装成hhtp响应[响应头,响应体](socket对象),浏览器将其解析,然后返回给用户看。

  • Django里面有什么东西,Django请求的生命周期是什么样简图,装饰器的位置,简图

1573030070447

  • Django中间件的位置,以及中间件状态在没有的情况下的走法。
  • 中间件无状态,后面的路由,视图,model,template,db,都不走了。节省了很多效率。进出的时候都会通过中间件。return不写和return为none是一个意思。

1573030372525

什么是中间件

官方的说法:中间件是一个用来处理Django的请求和响应的框架级别的钩子。它是一个轻量、低级别的插件系统,用于在全局范围内改变Django的输入和输出。每个中间件组件都负责做一些特定的功能。个人理解(就好比一个绳子上挂了一个钩子,这里的钩子指的就是中间件。)

但是由于其影响的是全局,所以需要谨慎使用,使用不当会影响性能。
说的直白一点中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在处理请求的特定的时间去执行这些方法。

我们一直都在使用中间件,只是没有注意到而已,打开Django项目的Settings.py文件,看到下图的MIDDLEWARE配置项。

自定义中间件

中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
这里的process可以理解为"处理"的意思
1.process_request(self,request) 
# 处理请求
2.process_view(self, request, view_func, view_args, view_kwargs)
# 处理视图
3.process_template_response(self,request,response)
# 处理模板
4.process_exception(self, request, exception)
# 处理异常
5.process_response(self, request, response)
# 处理响应

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

process_request(处理请求)

新建一个文件夹,将要写的中间件.py放在其下。该文件可以在app01下任意都行。

利用中间件为登陆跳转做校验

1573050068717

反向解析(路由这块)

URLconf和正则表达式

  • URLconf
ROOT_URLCONF = 'day60.urls'
这里是根路由(如果把day60文件里的urls拖到app01,这里的跟目录会发生变化,变化的前题是是否勾中(Search for references))

正常urls.py里面的内容,路由都是以^括号开头
url(r'^blog/[0-9]{4}/\d{2}/', views.blog)
这里的后面跟的是正则的路由
路由的urls.py里面的urlpatterns是一个列表
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^blog/', views.blogs),
url(r'^blog/[0-9]{4}/\d{2}/', views.blog),
]
这样的话在前端页面输入blog或者blog/2019/11结果都是blog这里的blog页面被前面的blogs所截胡,
如果在第二个blog/的后面都加上$,这样就保证了以什么开头,以什么结尾,能够保证路由地址被匹配到。

  • 简单要记住的正则表达式
正则表达式:
r' ^开头 $结尾 [0-9a-zA-Z]{4} \d \w(数字字母下划线) ?(表示0个或1个)  *0个或无数个  +至少一个

网络路由的地址可以按照年月日去分(某年某月某日去分。按年份查找。)

补充:
我们Django里面的blog前面没有加/,打开网页的时候就有了,这个不是浏览器帮我们加的,而是
APPEND_SLASH = True
# 是否开启URL访问地址后面不为/跳转至带有/的路径的配置项。
Django程序里面给你加的

分组命名分组

  • 分组
(就是从url上捕获参数,将参数按照位置传参给视图函数。有啥用?)
(这样用?)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^(bl)og/', views.blogs),
url(r'^blog/[0-9]{4}/\d{2}/', 'views.blog/2019/10/'),
]

由r'^blog/([0-9]{4})/(\d{2})/',去匹配后面的'views.blog/2019/10/',用分组的目的是为了提取路由里面的数据。

分组之后它会按照位置参数传给后面的东西,没分组一次就会多一个数据,这样的话视图函数里面就应该写
def blog(request,year,month):
或者里面写成*args也行,因为这里有三个位置参数。

  • 命名分组
在分组的基础上命名(分组与命名分组没有太大的关系。)
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^(bl)og/', views.blogs),
url(r'^blog/(?P<year>[0-9]{4})/\(?P<month>d{2})/', 'views.blog'),
]
# 这个传参也是按照关键字传参,如果函数里面没有year和month的话就会报错
def blog(request,year,month):
    print(year,type(year))
    print(month,type(month))
    return HttpResponse('blog') 
# 当然这里的位置参数也可以换成**args统一接收

视图函数中指定默认值

# urls.py中
from django.conf.urls import url
from . import views
urlpattern = [
url(r'^blog/$', views.page),
url(r'^blog/page(?P<num>[0-9]+)/$',views.page),
]
# views.py中,可以为num指定默认值
def page(request,num="1")
    pass
# 这里相当于运用了默认值。

路由分发(include)

app01或app02不是一个人写的,而且所实现的功能还也有所区别。
这个时候就要用到路由分发。
之前urls.py是写在命名的项目下的,现在将urls.py拷贝一份,放到app01的文件下中,然后将app01的usrl.py中的admin包括导入admin的模块删掉(like this,这里目前只有library出版社的增删改)
from django.conf.urls import url, include

from app01 import views

urlpatterns = [

    url(r'^publisher_list/', views.publisher_list),
    url(r'^publisher_add/', views.publisher_add),
    url(r'^publisher_del/(\d+)/', views.publisher_del),
    url(r'^publisher_edit/', views.publisher_edit),
]

在library中这样写:
"""
from django.conf.urls import url,include
from django.contrib import admin
from app01 import views

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

  • 传递额外参数给视图函数(了解即可)
找到urls:
ctrl + url (看其内置的写法,可以在其内置里面有**kwargs,和*args)
内置首行函数的写法:
def url(regex, view, kwargs=None, name=None):
也就意味着我们可以在其网络路由传参,路由再走到视图,。

url的命名与反向解析

URL的命名

 # 这一步叫做url的命名   与前面的url有了一一对应的关系了
    url(r'^publisher_list/', views.publisher_list, name='publisher_list'),

URL的反向解析

用在两个地方:
1,模板:
上面的是url的命名,命名后直接在模板里这样用就行了,
不用写死的地址了。
{% url 'publisher_list' %}
作用:解析生成完整的URL路径'/app01/publisher_list'


2,py文件
from django.shortcuts import render, HttpResponse, redirect, reverse
# 从py文件中导入reverse(反转的意思)

Ajax

Django回顾

  • 模板

html + 特殊的语法

{{}} 变量 {% %}

.key   .属性 .方法  .索引 
过滤器
内置过滤器
safe , default(当前变量没给或者为空), filesizeformat, date:"Y-m-d H:i:s" (日期时间格式化), add, length, slice(切片,对应切片的内容),  join(做拼接的),
可以自己定义内置过滤器

  • 标签
for forloop
for empty (表示在做循环的时候,如果循环不出来,拿empty)
if (不支持算数运算, 连续判断)
母版和继承
母版:
1.写上一个HTML页面,包含多个页面的公共部分
3。定义多个block块,让子页面进行重写
继承:
1.{% extends '母版的路径' %}
2.重写block块
组件:
把一小段html代码放到nav.html组件
页面里写上{% include 'nav.html' %}
静态文件:
{% load static %}
{% static '相对路径' %}
{% get_statice_prefix %}# 拿到配置中的STATICE_URL='/static/'

自定义的方法:
filter, simple_tag, inclusion_tag
定义:
	1.在已经注册的app下创建一个tempatetags的python包;
	2.在python包中创建py文件(自己定义my_tags)
	3.写代码
from django import template
register = template.Library()
	4.写函数+装饰器
定义:
@register.filter
def add_arg(value,arg):
	return 'xxxx'

@register.simple_tag
def joins(*args,**kwargs):
	return 'xxxx'

@register.inclusion_tag('li.html')
def show_li(*args,**kwargs):
	return {}
li.html
{% for i in li %}
	{{ i }}
{% endfor %}

使用:
模板
{% load 'my_tags' %}
{{ a|add_tag:'xxx' }}  # 替换我函数的返回值了

{% joins 'v1' 'v2' k1='v3' k2='v4' %} # 返回的是一个字符串,也可以返回html代码段,不过需要转义

{% show_li 3  %}




  • ORM

对象关系映射

对应关系

类→表

对象→数据行

属性→字段

必知必会13条

返回对象列表QuerySet

all

filter

exculde (取反的,获取不满足的所有)

values

values [()] 字典边缘组了

order_by () 默认是升序

reverse() 给已经排好序的整个给颠倒过来

distinct 去重,

返回对象:

get

first

list (获取对象里面的第一个或最后一个,得不到为none)

返回布尔值的:

extsts

返回数字:

count (count效率要比len高一点)

单表的双下划线

__gt
__gte
__lt
__lte
__in=[]
__range=[1,6]
__startswith 以什么什么开头  __isstartswith 以什么开头
__endswith 
__contains 包含  like__icontains
__year
__isnull=True


  • 外键的操作
图书馆里系统
class Book(models.Model):
name = models.CharField(max_length=32)
pub = models.Forginkey(to='publisher',on_delete=models.CASCADE,relate_name='books')


  • 基于对象的
正向查
book_obj.pub→关联出版社对象
book_obj.pub_id→关联的出版社对象id

反向查
不指定relate_name
pub_obj.book_set →关系管理对象
pub_obj.book_set.all()→关联的所有的书籍对象 表名_set

反向查
指定relate_name
pub_obj.book_set →关系管理对象
pub_obj.book_set.all()→关联的所有的书籍对象 related_name


  • 基于字段
models.Book.objects.filter(pub__name='xxx')
models.Publisher.objects.filter(book__name)
# 不指定relate_name时候这样写,指定了就写指定的



  • 多对多操作
class Book(models.Model):
	name = models.CharField(max_length=32)
class Author(models.Model):
	name = models.CharField(max_lenght=32)
	book = models.ManyToManyField('Book','related_name'='authors')

外键的关系对象里面只能放 对象,多对多里面还可以放id

  • 聚合和分组
from  django.db.models import  Max,Min,Count,Avg,Sum
Hobby.objects.all().aggreate(max=Max('filed'))

  • 分组
Author.objects.annotate(count=Count('books'))
# 分组的结果还是对象列表
另外一种写法
Book.objects.values('Author__name')annotate(Min('price'))


  • F和Q
Book.bojects.update(price=F('price')+2)
# update更新只更新update所更新的字段
Q(Q(price__gt=10)|Q())
|或
&与
~非



  • 事务
一些列的操作要么都成功,要么都失败

from django import transaction
try:
	with transaction.atomic():
		# 一系列操作
		pass
except Exception as e:
	print(e)

第十章:Django框架

web框架的本质

所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。

简单的web框架编写

简单的web框架(返回HTMl页面)

import socket
server = socket.socket()
server.bind(('127.0.0.1', 8896))
server.listen(5)
while 1:
    # HTTP请求是一次请求一次响应,在返回数据之后,关闭链接就可以了
    conn, addr = server.accept()
    from_client_data = conn.recv(1024)
    # 获取浏览器访问的路径
    path = from_client_data.decode('utf8').split()[1]
    # 按照HTTP协议规定的方式返回消息
    conn.send(b'HTTP/1.1 200 ok\r\n\r\n')
    # '/'代表的是根路径,就是IP地址后面没有写访问路径
    if path == '/':
        # 打开html文件,以rb的形式读取出来
        with open('test.html', 'rb')as f:
            # 读取的html文件
            data = f.read()
    # 如果访问的是CSS文件路径,将CSS文件读取出来
    elif path == '/style.css':
        with open('style.css', 'rb')as f:
            data = f.read()
    # 如果访问的是JS文件,将js的文件读取出来
    elif path == '/test.js':
        with open('test.js', 'rb')as f:
            data = f.read()
	#将获取的文件发送给客户端
    conn.send(data)
    conn.close()
'''
1:通过浏览器访问上面socket代码中设置的IP地址和端口就能获取到HTML页面的内容
2:我们的css,js代码单独写在一个文件中的,当我们吧HTMl页面发送给浏览器的时候,浏览器对HTML页面进行渲染,渲染的时候读取HTMl中有导入的css文件和JS文件,就会再次向server端发送请求
'''

多线程动态返回HTMl页面

import socket, time
from threading import Thread

# 客户端通过根路径访问时,服务端返回HTML页面
def home(conn):
    with open('test.html', 'r', encoding='utf-8')as f:
        new = str(time.time())
        s = f.read()
        #将获取的动态时间替换掉HTML页面中的时间,达到动态返回页面的效果
        s = s.replace('$$xx$$', new)
        conn.send(s.encode('utf-8'))
        conn.close()

# 当客户端通过CSS路径访问的时候,服务端返回CSS文件
def style(conn):
    with open('style.css', 'rb')as f:
        conn.send(f.read())
        conn.close()

# 当客户端通过JS路径访问的时候,服务端返回JS文件
def index(conn):
    with open('test.js', 'rb')as f:
        conn.send(f.read())
        conn.close()

# 当客户端通过请求索要图片的时候,服务端返回图片
def png(conn):
    with open('1.png','rb')as f:
        conn.send(f.read())
        conn.close()

# 当客户端发送请求索要,ico文件的时候,返回ioc文件
def ico(conn):
    with open('wechat.ico','rb')as f:
        print('333')
        conn.send(f.read())
        conn.close()

def ind(conn):
    with open('index.html','rb')as f:
        print('333')
        conn.send(f.read())
        conn.close()


#定义一个路径和文件的对应列表,节省代码,
urlList=[(b'/',home),(b'/style.css',style),(b'/test.js',index),(b'/wechat.ico',ico),(b'/index',ind)]

while 1:
    server = socket.socket()
    server.bind(('127.0.0.1',8896))
    server.listen(5)
    conn,addr=server.accept()
    #获取请求路径
    path=conn.recv(1024).split()[1]
    print(path)
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    func=None
    for url in urlList:
        if path==url[0]:
            p=Thread(target=url[1],args=(conn,))
            p.start()


起飞版的
https://pan.baidu.com/s/1Ns5QHFpZGusGHuHzrCto3A

wsgiref模块

from wsgiref.simple_server import make_server
# wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便)
#函数名字随便起
def application(environ, start_response):
    '''
    :param environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息
    :param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数
    :return:
    '''
    start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')])
    print(environ)
    print(environ['PATH_INFO']) #获取的是请求路径
    #输入地址127.0.0.1:8000,这个打印的是'/',输入的是127.0.0.1:8000/index,打印结果是'/index'
    return [b'<h1>Hello, web!</h1>']

#和socketserver那个模块很像
httpd = make_server('127.0.0.1', 8080, application)

print('Serving HTTP on port 8080...')
# 开始监听HTTP请求:
httpd.serve_forever()

jinja2字符串替换

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>个人介绍</h1>
<ul>
    //jinja2的替换格式,对列表可以进行循环取值
    {% for i in hobby %}
    <li>{{ i }}</li>
    {% endfor %}
</ul>
</body>
</html>
#*************************
from jinja2 import Template
def home(conn):
    # {% for i in hobby %}
    # <li>{{ i }}</li>
    # {% endfor %}
    with open('08jinja2test.html', 'r',encoding='utf-8') as f:
        data = f.read()
    t = Template(data)
    ret = t.render({'hobby':['金钱','女人','金鑫']})//对HTMl进行替换
    conn.send(ret.encode('utf-8'))
    conn.close()

jinja2和wsgire框架编写

import socket, time
from wsgiref.simple_server import make_server
from jinja2 import Template

# 客户端通过根路径访问时,服务端返回HTML页面
def home(conn):
    with open('test.html', 'r', encoding='utf-8')as f:
        new = str(time.time())
        s = f.read()
        # 将获取的动态时间替换掉HTML页面中的时间,达到动态返回页面的效果
        t = Template(s)
        ret = t.render({'hobby':['张达','猪繁育','力争']})
        return ret.encode('utf-8')
# 当客户端通过CSS路径访问的时候,服务端返回CSS文件
def style(conn):
    with open('style.css', 'rb')as f:
        return f.read()
# 当客户端通过JS路径访问的时候,服务端返回CSS文件
def index(conn):
    with open('test.js', 'rb')as f:
        return f.read()
# 当客户端通过请求索要图片的时候,服务端返回图片
def png(conn):
    with open('1.jpg', 'rb')as f:
        return f.read()
# 当客户端发送请求索要,ico文件的时候,返回ioc文件
def ico(conn):
    with open('wechat.ico', 'rb')as f:
        return f.read()
def ind(conn):
    with open('index.html', 'rb')as f:
        return f.read()
# 定义一个路径和文件的对应列表,节省代码,
urlList = [('/', home), ('/style.css', style), ('/test.js', index), ('/wechat.ico', ico), ('/index', ind),
           ('/1.jpg', png)]

def app(envion,start_sponse):
    #返回HTTP请求行
    start_sponse('200 ok',[])
    #获取请求路径
    path=envion['PATH_INFO']
    ret=None
    for i in urlList:
        if path==i[0]:
            ret=i[1](1)
            break
    if ret:
        return [ret]
    else:
        return [b'404']
httpd=make_server('127.0.0.1',8896,app)
httpd.serve_forever()

'''**************************************************'''
#前端代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>test</title>
    <link rel="stylesheet" href="style.css">
    <link rel="icon" href="http://www.jd.com/favicon.ico">
    <script src="test.js"></script>
    <style>
        img{
            width: 500px;
            height: 200px;
        }
    </style>
</head>
<body>
    #循环读取hobby的数据
    {% for i in hobby %}
    <h1 id="d1">欢迎猪繁育在{{i}}来到繁育基地</h1>
    {% endfor %}
    #申明结束
    <a href="/index">你大太阳</a>
    <img src="1.jpg" alt="">
</body>
</html>

HTTP

HTTP协议概述

#参考博客:https://www.cnblogs.com/clschao/articles/9230431.html

1:HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。
2:尽管TCP/IP协议是互联网上最流行的应用,HTTP协议中,并没有规定必须使用它或它支持的层。事实上,HTTP可以在任何互联网协议上,或其他网络上实现。HTTP假定其下层协议提供可靠的传输。因此,任何能够提供这种保证的协议都可以被其使用。因此也就是其在TCP/IP协议族使用TCP作为其传输层。
3:通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的TCP连接。HTTP服务器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如"HTTP/1.1 200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。

HTTP工作原理

HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。
#1. 客户端连接到Web服务器
	一个HTTP客户端,通常是浏览器,与Web服务器的HTTP端口(默认为80)建立一个TCP套接字连接。例如,http://www.luffycity.com。
#2. 发送HTTP请求
	通过TCP套接字,客户端向Web服务器发送一个文本的请求报文,一个请求报文由请求行、请求头部、空行和请求数据4部分组成。
#3. 服务器接受请求并返回HTTP响应
	Web服务器解析请求,定位请求资源。服务器将资源复本写到TCP套接字,由客户端读取。一个响应由状态行、响应头部、空行和响应数据4部分组成
#4. 释放连接TCP连接
	若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求;
#5. 客户端浏览器解析HTML内容
	客户端浏览器首先解析状态行,查看表明请求是否成功的状态代码。然后解析每一个响应头,响应头告知以下为若干字节的HTML文档和文档的字符集。客户端浏览器读取响应数据HTML,根据HTML的语法对其进行格式化,并在浏览器窗口中显示。
'''示例在浏览器地址栏键入URL,按下回车之后会经历以下流程''':
	1:浏览器向 DNS 服务器请求解析该 URL 中的域名所对应的 IP 地址;
	2:解析出 IP 地址后,根据该 IP 地址和默认端口 80,和服务器建立TCP连接;
	3:浏览器发出读取文件(URL中域名后面部分对应的文件)的HTTP请求,该请求报文作为 TCP 三次握手的第三个报文的数据发送给服务器;
	4:服务器对浏览器请求作出响应,并把对应的 html 文本发送给浏览器;
	5:释放 TCP连接;
	6:浏览器将该 html 文本并显示内容;
#*************************************************************
#http协议是基于TCP/IP协议之上的应用层协议
1:#基于  请求-响应 的模式
	HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并 返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有 接收到请求之前不会发送响应
2:#无状态保存
    HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议 自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个 级别,协议对于发送过的请求或响应都不做持久化处理。
    HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能, 于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管 理状态了。
3:#无连接(短链接)
	无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间,并且可以提高并发性能,不能和每个用户建立长久的连接,请求一次相应一次,服务端和客户端就中断了。    但是现在的http协议1.1版本不是直接就断开了,而是等几秒钟,这几秒钟是等什么呢,等着用户有后续的操作,如果用户在这几秒钟之内有新的请求,那么还是通过之前的连接通道来收发消息,如果过了这几秒钟用户没有发送新的请求,那么就会断开连接,这样可以提高效率,减少短时间内建立连接的次数,因为建立连接也是耗时的,默认的好像是3秒中现在,但是这个时间是可以通过咱们后端的代码来调整的,自己网站根据自己网站用户的行为来分析统计出一个最优的等待时间。


HTTP请求方式

1:#GET请求
    向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问
2:#HEAD请求
    与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据)
3:#POST请求
    向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有。
4:#PUT
    向指定资源位置上传其最新内容。
5:#DELETE
    请求服务器删除Request-URI所标识的资源。
6:#TRACE
    回显服务器收到的请求,主要用于测试或诊断。
7:#OPTIONS
    这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作。
8:#CONNECT
    HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器)。
9.#path
局部更新


GET请求和POST请求

1:GET提交的数据会放在URL之后,也就是请求行里面,以?分割URL和传输数据,参数之间以&相连,如EditBook?name=test1&id=123456. POST方法是把提交的数据放在HTTP包的请求体中
2:GET提交的数据大小有限制(因为浏览器对URL的长度有限制),而POST方法提交的数据没有限制
3:GET与POST请求在服务端获取请求数据方式不同,就是我们自己在服务端取请求数据的时候的方式不同了
#GET请求和POST请求的区别
    GET在浏览器回退时是无害的,而POST会再次提交请求。
    GET产生的URL地址可以被Bookmark,而POST不可以。
    GET请求会被浏览器主动cache,而POST不会,除非手动设置。
    GET请求只能进行url编码,而POST支持多种编码方式。
    GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
    GET请求在URL中传送的参数是有长度限制的,而POST没有。
    对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
    GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
    GET参数通过URL传递,POST放在Request body中。
#对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
#而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
1. GET与POST都有自己的语义,不能随便混用。
2. 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的TCP在验证数据包完整性上,有非常大的优点。
3. 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
-----------------------------------------------------------------------------------



HTTP请求的状态码

所有HTTP响应的第一行都是状态行,依次是当前HTTP版本号,3位数字组成的状态代码,以及描述状态的短语,彼此由空格分隔。
状态代码的第一个数字代表当前响应的类型:
    1xx消息——请求已被服务器接收,继续处理
    2xx成功——请求已成功被服务器接收、理解、并接受
    3xx重定向——需要后续操作才能完成这一请求(301永久重定向,302临时重定向)
    4xx请求错误——请求含有词法错误或者无法被执行
    5xx服务器错误——服务器在处理某个正确请求时发生错误
#常见的HTTP请求状态码
	https://www.runoob.com/http/http-status-codes.html
常见的状态码
200 -请求成功
202 -已接受请求,尚未处理
204 -请求成功,且不需返回内容
301 - 资源(网页等)被永久转移到其他url
400 - 请求的语义或是参数有错
403 - 服务器拒绝请求
404 - 请求资源(网页)不存在

500 - 内部服务器错误
502 - 网关错误,一般是服务器压力过大导致连接超时
503 - 由于超载或系统维护,服务器暂时的无法处理客户端的请求。


URL

    超文本传输协议(HTTP)的统一资源定位符将从因特网获取信息的五个基本元素包括在一个简单的地址中:
    1:传送协议。
    2:层级URL标记符号(为[//],固定不变)
    3:访问资源需要的凭证信息(可省略)
    4:服务器。(通常为域名,有时为IP地址)
    5:端口号。(以数字方式表示,若为HTTP的默认值“:80”可省略)
    6:路径。(以“/”字符区别路径中的每一个目录名称)
    7:查询。(GET模式的窗体参数,以“?”字符为起点,每个参数以“&”隔开,再以“=”分开参数名称与数据,通常以UTF8的URL编码,避开字符冲突的问题)
    8:片段。以“#”字符为起点
#示例:
	以http://www.luffycity.com:80/news/index.html?id=250&page=1 为例, 其中
    http,是协议;
    www.luffycity.com,是服务器;
    80,是服务器上的默认网络端口号,默认不显示;
    /news/index.html,是路径(URI:直接定位到对应的资源);
    ?id=250&page=1,是查询。


HTTP请求消息格式

请求行\请求头部\空行\响应数据
请求行: GET  路径  HTTP/1.1


HTTP响应消息格式

响应行\响应头部\空行\响应数据
响应行: HTTP/1.1 状态码  状态描述


HTTP request对象

from django.shortcuts import render
def login(request):
    #request参数就是HTTPrequest对象
   print(request)#<WSGIRequest: GET '/login/'>
    
    1.#返回请求的路径,纯路径
    print(request.path)#/login/
    
    2.#返回请求的路径,纯路径
    print(request.path_info)#/login/
    
    3.#返回的是请求的全路径包括参数,不包含ip地址和端口,没有参数就返回纯路径
    print(request.get_full_path())#/login/?a=1&b=1
    
    4.#查看当前的请求方式类型
    print(request.method)#GET 一般常见的是GET和POST
    
    5.#返回的是一个QueryDict对象,是HTTP通过GET请求从客户端发送的请求数据
    #可以使用get方法获取对象的值:name=request.GET.get('username')
    print(request.GET)URL上面携带的参数#<QueryDict: {}>
    
   6. # 返回的是一个QueryDict对象,是HTTP通过POST请求从客户端发送的请求数据
    # 可以使用get方法获取对象的值:name=request.POST.get('username')
    print(request.POST)post请求提交的数据默认有一个参数: enctype="application/x-www-form-urlencoded"这个表示对URL的编码
#<QueryDict: {}>
    注意:键值对的值是多个的时候,比如checkbox类型的input标签,select标签,需要用
    request.POST.getlist("hobby")
    7.# 上传的文件
    print(request.FILES)	注意:有一个参数:enctype="multipart/form-data"request.FILES	
    
    8.#返回HTTP请求中的请求体,是byte类型,POST请求的数据就是在body中提取的,GET请求获取不到数据
    print(request.body)
    
    9.#返回的是一个python字典,包含所有的HTTP请求头信息
    print(request.META)
    return render(request,'login.html')
	
    10.print(request.COOKIES)  # cookies
	
    11.print(request.session ) # session

9.#META的常见值(了解):
    CONTENT_LENGTH —— 请求的正文的长度(是一个字符串)。
    CONTENT_TYPE —— 请求的正文的MIME 类型。
    HTTP_ACCEPT —— 响应可接收的Content-Type。
    HTTP_ACCEPT_ENCODING —— 响应可接收的编码。
    HTTP_ACCEPT_LANGUAGE —— 响应可接收的语言。
    HTTP_HOST —— 客服端发送的HTTP Host 头部。
    HTTP_REFERER —— Referring 页面。
    HTTP_USER_AGENT —— 客户端的user-agent 字符串。
    QUERY_STRING —— 单个字符串形式的查询字符串(未解析过的形式)。
    REMOTE_ADDR —— 客户端的IP 地址。
    REMOTE_HOST —— 客户端的主机名。
    REMOTE_USER —— 服务器认证后的用户。
    REQUEST_METHOD —— 一个字符串,例如"GET" 或"POST"。
    SERVER_NAME —— 服务器的主机名。
    SERVER_PORT —— 服务器的端口(是一个字符串)。
    #示例:
	请求中的任何 HTTP 首部转换为 META 的键时,都会将所有字母大写并将连接符替换为下划线最后加上 HTTP_  前缀。
    所以,一个叫做 X-Bender 的头部将转换成 META 中的 HTTP_X_BENDER 键。


HTTP Response对象

def home(request):
    #向前端返回一个字符串
    1. return HttpResponse('返回一个字符串')---Content-Type: text/html:charset=utf-8
    #向前段返回一个HTML页面
    2. return render(request,'模板的文件名')
	return render(request,'home.html',{namee:name})  ---可以给HTML文件中传参数,HTML中写{{namee}}
    #临时重定向,将请求指向另一个页面,括号里面写路径
    3. return redirect('/login/'---地址)--------------302/login/   响应头 Location: 地址
	重定向
     ret=HttpResponse('',)
     ret['Location']='http://www.baidu.com'
     ret.status_code=301
     return ret
	返回页面
     data={'name':'tiancai','age':18}
	4.JsonResponse({})  #返回json数据  返回非字典类型safe=False Content-Type: application/json


什么是HTTP协议(面试总结)**

1:HTTP协议是一个超文本传输协议
2:HTTP协议规定一次请求和一次响应之后就断开链接,表示了HTTP是一个无状态的,短链接链接方式
3:HTTP传输格式上,
    在请求时,会有请求头和请求体,请求头和请求体之间用\r\n\r\n进行分割,请求头之间用\r\n分割.get请求是没有请求体的,get的参数都放在了请求头中
    get请求的请求示例:
        send("GET /index/?a=123  http1.1\r\nhost:www.baidu.com\r\nuseragent:Chrome\r\n\r\n")
    post请求的请求示例:
        send("POST /index/  http1.1\r\nhost:www.baidu.com\r\nuseragent:Chrome\r\n\r\n username=alex&pwd=123")
    在请求响应时:会有响应头和响应体,响应头和响应体之间用\r\n\r\n进行分割,响应头之间用\r\n分割
    Content-Encoding: gzip\r\nCache-Control:private\r\n\r\n网页看到的HTML的内容
  
扩展:常见的请求头:
	- user-agent:用什么浏览器访问的网站。
	- content-type: 请求体的数据格式是什么?(服务端按照格式要求进行解析)   
---------------
请求和响应的步骤
1.在浏览器的地址栏种输入URL地址,回车,发个一个GET请求;
2.Django接受到请求,根据URL的路径找到对应的函数,执行函数拿到结果
3.Django将结果封装成http响应的报文返回浏览器
4.浏览器接受响应,断开连接,解析数据


MVC和MTV

MVC框架

Web服务器开发领域里著名的MVC模式,所谓MVC就是把Web应用分为模型(M),控制器(C)和视图(V)三层,他们之间以一种插件式的、松耦合的方式连接在一起,模型负责业务对象与数据库的映射(ORM),视图负责与用户的交互(页面),控制器接受用户的输入调用模型和视图完成用户的请求,其示意图如下所示:
M -- models 数据库相关  模型 数据库交互
V -- views  视图相关  展示给用户看的 HTML
C -- controller url控制器(url分发器,路由分发) 业务逻辑 传递指令


MTV框架

Django的MTV模式本质上和MVC是一样的,也是为了各组件间保持松耦合关系,只是定义上有些许不同,Django的MTV分别是值:
M 代表模型(Model): 负责业务对象和数据库的关系映射(ORM)。
T 代表模板 (Template):负责如何把页面展示给用户(html)。 模板
V 代表视图(View):   负责业务逻辑,并在适当时候调用Model和Template。
除了以上三层之外,还需要一个URL分发器,它的作用是将一个个URL的页面请求分发给不同的View处理,View再调用相应的Model和Template,MTV的响应模式如下所示:


Django下载安装

#下载
	pip3 install django==1.11.9 
	pip3 install django==1.11.9 -i http://xxxxxx  指定源
#创建项目
	django-admin startproject mysite   创建了一个名为"mysite"的Django 项目
#启动项目
	python manage.py runserver  默认是127.0.0.1:8000
	python manage.py runserver 127.0.0.1  默认端口号是8000
	python manage.py runserver 0.0.0.0:80 修改IP和端口
#创建应用
	python manage.py startapp APP项目名称  #通过执行manage.py文件来创建应用,执行这句话一定要注意,你应该在这个manage.py的文件所在目录下执行这句话,因为其他目录里面没有这个文件
    指令 创建的APP需要配置
    项目的setting配置文件中,installapps的列表,添加一个APP名称作为配置
    pycham中创建完APP之后如果想创建新的APP,需要执行创建APP的命令,并添加配置


Django项目目录结构

#manage.py ----- 
	Django项目里面的工具,通过它可以调用django shell和数据库,启动关闭项目与项目交互等,不管你将框架分了几个文件,必然有一个启动文件,其实他们本身就是一个文件。
#settings.py ---- 
	包含了项目的默认设置,包括数据库信息,调试标志以及其他一些工作的变量。
#urls.py ----- 
	负责把URL模式映射到应用程序。
#wsgi.py ---- 
	runserver命令就使用wsgiref模块做简单的web server,后面会看到renserver命令,所有与socket相关的内容都在这个文件里面了,目前不需要关注它。
'''创建的的应用的目录结构'''
#models.py 
	之前我们写的那个名为model的文件就是创建表用的,这个文件就是存放与该app(应用)相关的表结构的
#views.py
	存放与该app相关的视图函数的


Django的配置

1.静态文件:
    STATIC_URL'/别名/'
    STATICFILES_DIRS=[os.path.join(BASE_DIR,'文件名字')]
2.数据库:
    DATABASES={default
       		 ENGINE: mysql    
              NAME:数据库的名称
              USER:用户名
              PASSWORD:密码
              PORT:3306端口号
              HOST:127.0.0.1   IP地址}
    
 3.APP
INSTALLEN_APPS=[
    app名称
    或者app名称.apps.APP名称Config
]
4.模版
TEMPLATES DIRS=[
    os.path.join(BASE_DIR,'templates')
]
5.中间件
.注释掉一个csrf(可以提交post请求)


Djiango路由分发

# Django路由使用时在路径末尾加一个$符,如果是多个路径拼接就不用,
	路由系统中记得加入终止符 $
    url(r'^home/$', views.home),
# 无名分组参数
	url(r'^articles/(\d+)/(\d+)/', views.articles), #articles/2019/9/
	def articles(request,year,month):  # 位置参数 2019  9
         print(year,type(year)) #2019 <class 'str'>  #匹配出来的所有数据都是字符串
         print(month)
         return HttpResponse(year+'年'+ month +'月' +'所有文章')
# 有名分组参数
    url(r'^articles/(?P<xx>\d+)/(?P<oo>\d+)/', views.articles), #articles/2019/9/
    #xx=2019  oo=9  关键字传参
    def articles(request,oo,xx):  # 关键字传参 2019  9
        print(xx,type(xx)) #2019 <class 'str'>  #匹配出来的所有数据都是字符串
        print(oo)
        return HttpResponse(xx+'年'+ oo +'月' +'所有文章')
---------------
有名分组,分组数据以关键字传参的方式给了视图函数,不在乎参数位置了,并且试图函数形参的名称要和有名分组的变量名称一样


路由正则表达式

 r''使用反斜杠(\)转义特殊字符,如果你不想让反斜杠发生转义,可以在字符串前面添加一个r,表示原始字符串
^ --开头 ,
$--- 结尾 
[0-9a-zA-Z]{4}  --表示从什么到什么可以,几个位数
\d --数字
\w ---数字字母下划线
.
? ---0个或1个
*---0个或无数个 
+ ---至少一个
[] 范围 [^] 非范围
{} 次数区间
| 是或者
* 是匹配0或多
+ 是匹配1或多
? 是匹配0或1
*?  是限制*
.*  是一个任意的元素重复出现0次或多次
m(?:)t  合并到一起写
m(?P<名字>\w+)t.group('名字')
#\.转义 == 转移成普通的.
# 匹配所有的数字(包含小数包含负号)
print(re.findall("-?\d+\.\d+|-?\d+",s))
er的网址:公司让匹配的时候就用这个网站  (匹配的时候不要加空格)
http://tool.chinaz.com/regex/
匹配手机号:
/^1[3456789]\d{9}$/


URL的命名和反向解析

静态路由

'URL的命名':
url(r'^publisher_list/', views.publisher_list, name='publisher_list'),
#URL反向解析:
#模板:
{% url 'publisher_list' %}    ——》  解析生成完整的URL路径  '/app01/publisher_list/'
#py文件:
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.urls import reverse

reverse('publisher_list')   ——》  '/app01/publisher_list/'


分组

'URL的命名'
url(r'^publisher_del/(\d+)/', views.publisher_del,name='publisher_del'),
#URL反向解析
#模板
{% url 'publisher_del' 1  %}    ——》  解析生成完整的URL路径  '/app01/publisher_del/1/'
#py文件
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.urls import reverse

reverse('pub_del',args=(1,))   ——》  '/app01/publisher_del/1/'



命名分组

'URL的命名'
 url(r'^publisher_del/(\d+)/', views.publisher_del,name='publisher_del'),
#URL反向解析
#模板
{% url 'publisher_del' 1  %}    ——》  解析生成完整的URL路径  '/app01/publisher_del/1/'
{% url 'publisher_del' pk=1  %}    ——》  解析生成完整的URL路径  '/app01/publisher_del/1/'
#py文件
from django.shortcuts import render, redirect, HttpResponse, reverse
from django.urls import reverse

reverse('pub_del',args=(1,))   ——》  '/app01/publisher_del/1/'
reverse('pub_del',kwargs={'pk':1})   ——》  '/app01/publisher_del/1/'



namespace

URL的命名:
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='app01')),  # home   name=home
    url(r'^app02/', include('app02.urls', namespace='app02')),  # home   name=home

]
相对应的模版中:
反向解析生成URL
{% url 'namespace:name' % }
reverse( 'namespace:name' )


POST请求数据传输

#方式一:
def login(request):
    #获取请求的方式是GET还是POST
    path=request.method
    #当请求方式是GET的时候返回login页面
    if path=='GET':
        return render(request,'login.html')
    else:
        #为POST的时候,判断用户名和密码,返回首页或者返回登陆页面
        name=request.POST.get('username')
        password=request.POST.get('password')
        if name=='lijie'and password=='123456':
            return render(request,'home.html')
        else:
            return render(request,'login.html')
#方式二:
def login(request):
    #获取请求的方式是GET还是POST
    path=request.method
    #当请求方式是GET的时候返回login页面
    if path=='GET':
        return render(request,'login.html')
    else:
        #为POST的时候,判断用户名和密码,返回首页或者返回登陆页面
        #request.POST.dict()可以将获取的数据变成一个字典的形式,通过键值的形式取值
        data=request.POST.dict()
        if data['name']=='lijie'and data['password']=='123456':
            return render(request,'home.html')
        else:
            return render(request,'login.html')


FBV视图函数和CBV视图类

FBV视图函数

FBV(function base views) 就是在视图里使用函数处理请求,面向过程式的编程,上面的代码都是这个


视图函数装饰器

from django.shortcuts import render,HttpResponse

# Create your views here.
#
# 视图(函数.类)
装饰器
def wrapper(func):
    def inner(*args,**kwargs):
        print('111')
        ret=func(*args,**kwargs)
        print(222)
        return ret
    return inner

@wrapper
def home(request):  #---request请求相关信息对象

    print(request.path)
    # return HttpResponse('田彩')
    return render(request,'home.html')

@wrapper
def login(request):
    print('当前请求方法:',request.method)
    if request.method=='GET':
        print(request.path)
        return render(request,'login.html')

    else:
        print(request.POST)
        name=request.POST.get('username')
        pwd=request.POST.get('pwd')
        print(name,pwd)
        if name=='田彩' and pwd=='123':
            return HttpResponse('登录成功')
        else:
            return HttpResponse('登录失败')


CBV视图类

#urls.py路由文件中写法,其中views.login.as_view()中的login值得是处理这个路径相关请求的视图类
url(r'^login/', views.Login.as_view()),
----------------

from django.views import View


#请求login路径时执行的类
class login(View):
    #重构父类View类的dispatch方法可以在处理请求之前和请求完成之后,添加一些处理的逻辑,
    #父类View的dispatch方法查看请求的类型,然后通过反射来调用本类中的对应函数,来处理请求
    #如果没有找到请求方式对应的函数,就会执行父类中dispatch的第三个参数,也就是报错
    def dispatch(self, request, *args, **kwargs):
        print(f'{request.method}请求来了')
        ret = super().dispatch(request, *args, **kwargs)
        print('逻辑处理完成')
        return ret
	#如果是处理GET请求就直接定义get方法,父类View中的dispatch方法通过反射的方式来获取get函数来处理GET请求
    def get(self, request):
        print('111')
        return render(request, 'login.html')
	#如果是处理POST请求就直接定义get方法,父类View中的dispatch方法通过反射的方式来获取POST函数来处理GET请求
    def post(self, request):
        print('222')
        return HttpResponse('登陆成功')
    
#请求home路径时执行的类
class home(View):
    def get(self, request):
        return render(request, 'home.html')

    def post(self, request):
        return HttpResponse('登陆成功')

    
#视图类的源码流程:
views.Addclass.as_view()
1.程序加载时,执行Addclass的类方法as_view
	在本类中未找到,由于该类继承了View,因此去父类找找看,发现存在
2.调用父类as_view的类方法(@classonlymethod)-->cls为Addclass
该方法返回重点实现任务:
    1.定义了view方法
    2.并返回view
    请求到来时,执行下面操作
    3.结果变成执行view
    4.view的执行结果就是views.Addclass.as_view()的执行结果

3.调用View的view方法
    1.self = cls(**initkwargs)因此self为Addclass类,self为实例化类的对象
    2.该函数返回self.dispatch(request, *args, **kwargs)
    3.需要先执行dispatch,并把执行结果返回给view函数

4.执行View的dispatch方法
    1.获取request.method请求方式,判断请求方式是否被允许,允许的情况下:
       1. 通过反射获取当前对象中的请求方式所对应的方法:
        字符串的方式getattr(Addclass,'get')赋值给handler------handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
    	2.若不存在,不允许的情况下则返回报错信息给handler--------handler = self.http_method_not_allowed
    2.执行handler-->相当于执行get(request,*args,**kwargs)
    3.把结果return给view



视图类加装饰器

from django.views import View
#如果要给视图类添加装饰器就要使用method_decorator这个方法
from functools import wraps
from django.utils.decorators import method_decorator
装饰器
def wrapper(func):
    @wraps(func)
    def inner(*args,**kwargs):
        print('111')
        ret=func(*args,**kwargs)
        print(222)
        return ret
    return inner
# 方式一:类装饰器,不常用
@method_decorator( wrapper, name='get')  # 给类中的某个方法添加装饰器,只能指向一个方法,
@method_decorator( wrapper, name='post')  # 如果要加多个,就再加一个装饰器,指向这个方法,
class login(View):

# 方式二:给dispatch添加装饰器,在请求处理之前和处理之后添加一些逻辑.
    重写一个dispatch添加装饰器.
   1. @method_decorator( wrapper)
    def dispatch(self, request, *args, **kwargs):
        print(f'{request.method}请求来了')
        ret = super().dispatch(request, *args, **kwargs)
        print('逻辑处理完成')
        return ret
    -----------------------
    在类的上面加装饰器,name='dispatch',等于在执行dispatch的时候给所有的函数方法上面都加上了这个装饰器(记住这个就可以了)
    2.@method_decorator(wrapper, name='dispatch')
	  class PublisherAdd(View):

# 方式三:给执行的请求方式添加一个装饰器,在处理这个请求的时候,添加一些逻辑
    @method_decorator( wrapper)
    def get(self, request):
        print('111')
        return render(request, 'login.html')

    def post(self, request):
        print('222')
        return HttpResponse('登陆成功')


模版渲染

语法

#{{变量名}}  HTML页面中使用
	用后台返回的变量来替换HTML中的字段
    #获取后台返回的name变量,把他放到标签中
    <h1>{{ name }}</h1>
#{% 处理逻辑相关的  %}
	在前段用逻辑处理后态返回的数据,比如for循环,if判断,with取别名等python代码逻辑
    <ul>
        #获取后带返回的列表lst,循环列表,生成多个li标签
        {% for i in lst %}
        <li>{{ i }}</li>
        #声明for循环逻辑的结束
        {% endfor %}
    </ul>
    
    
'''后台返回数据的方式'''
#在请求返回的逻辑中,定义变量,用render的方式返回HTML页面的时候,把要返回的数据放在第三个参数,返回给前段,
def home(request):
    time=datetime.datetime.now()
    name='张达'
    dic={'name':'张达','age':'88','sex':[11,22,33]}
    lst=[11,22,33]
    lst1=[]
    class person():
        n = '张达666'
        def p(self):
            return '张达777'
    #返回数据的方式一: 使用locals()方法,获取当前函数中的所有局部变量,返回给前端
    return render(request,'home.html',locals())
	#返回数据的方式er:使用字典的方式返回数据,字典键相当于是给数据去一个别名,值就是数据
    return render(request,'home.html',{'name':name,'time':time,'dic':dic})


万能的点

    #通过索引获取列表的值
    <h1>{{ lst.1 }}</h1>
    #通过实例化对象来调用方法,获取返回值和类中的局部变量
    <h1>{{ obj.p }}</h1>
    #获取字典的所有键,值,键值对,获取的是一个
    <h1>{{ dic.items }}</h1>
    <h1>{{ dic.keys }}</h1>
    <h1>{{ dic.values }}</h1>


过滤器

'''都是在前端页面中使用:|'''
切记:过滤器中的':'左右没有空格
#default
	如果一个变量是false或者为空,使用给定的默认值。 否则,使用变量的值。
    {{ value|default:"nothing"}}#给value这个值设置一个默认值,如果value没有传值或者值为空的话就显示nothing
    '''还有一种方法:在TEMPLATES的OPTIONS可以添加一个选项:'string_if_invalid':'变量不存在''''
    
#length
	返回值的长度,作用于字符串和列表
    {{ value|length }}#返回value的长度,如 value=['a', 'b', 'c', 'd']的话,就显示4.
#filesizeformat
	将值格式化为一个 “人类可读的” 文件尺寸 (例如 '13 KB', '4.1 MB', '102 bytes', 等等)。例如:
    {{ value|filesizeformat }}#如果 value 是 123456789,输出将会是 117.7 MB。
#slice
	切片,如果 value="hello world",还有其他可切片的数据类型
    {{value|slice:"2:-1"}}
#date   ***
	如果前面是时间的话讲课格式化时间,如果 value=datetime.datetime.now()
    {{ value|date:"Y-m-d H:i:s"}}
     '''还有一种方法:把settings的USE_L10N修改为False,然后再可以添加一个选项:DATETIME_FORMAT='Y-m-d  H:i:s'''
        
#safe   ***
	识别标签
	后端发送一个字符串形式的TML标签,前端可以通过safe方法,把这个字符串变成前端标签
    value = "<a href='#'>点我</a>"   和   value="<script>alert('123')</script>"
    {{ value|safe}}#会把value变成一个标签,放到页面上
#truncatechars
	如果字符串字符多于指定的字符数量,那么会被截断。截断的字符串将以可翻译的省略号序列(“...”)结尾
    {{ value|truncatechars:9}} #注意:最后那三个省略号也是9个字符里面的,也就是这个9截断出来的是6个字符+3个省略号,配合前端的点击事件就可以展开这三个点
#truncatewords
	在一定数量的字后截断字符串,是截多少个单词,以空格来计算字符串
    例如:‘hello girl hi baby yue ma’,
    {{ value|truncatewords:3}}  #得到的结果是 'hello girl h1...'
#cut
	移除value中所有的与给出的变量相同的字符串
    {{ value|cut:' ' }}#如果value为'i love you',那么将输出'iloveyou',移除空格
#join
	使用字符串连接列表,{{ list|join:', ' }},就像Python的str.join(list)
    <h1>{{ l1|join:'+' }}</h1>
#timesince(了解)
	计算时间差
    {{ blog_date|timesince:comment_date }}#comment_date减去blog_date的时间的时间差
#timeuntil(了解)
	和timesince一样,不过时间可以指定
	{{ conference_date|timeuntil:from_date }}#conference_date减去from_date的时间差
    
#add       加法和拼接
{{ 2|add:'2'  }}   数字加法
{{ 'a'|add:'b'  }}   字符串的拼接
{{ [1,2]|add:[3,4]  }}   列表的拼接


标签逻辑操作

for循环

{% for person in person_list %}
    <p>{{ person.name }}</p>  <!--凡是变量都要用两个大括号括起来-->
{% endfor %}

#遍历字典
{% for key,val in dic.items %}
    <p>{{ key }}:{{ val }}</p>
{% endfor %}

#循环序号可以通过{{forloop}}显示,必须在循环内部用
forloop.counter            当前循环的索引值(从1开始),forloop是循环器,通过点来使用功能
forloop.counter0           当前循环的索引值(从0开始)
forloop.revcounter         当前循环的倒序索引值(到1结束)
forloop.revcounter0        当前循环的倒序索引值(到0结束)
forloop.first              当前循环是不是第一次循环(布尔值)
forloop.last               当前循环是不是最后一次循环(布尔值)
forloop.parentloop         本层循环的外层循环的对象,再通过上面的几个属性来显示(获取)外层循环的计数等
#示例:
    {% for k,v in d1.items %}
    <li>{{ forloop.counter }}-- {{ forloop.parentloop.counter }} === {{ k }} -- {{ v }}</li>
    {% endfor %}

#*******************************************
#for 标签带有一个可选的{% empty %} 从句,以便在给出的数据是空的或者没有被找到时,就执行empty从句。  
    {% for person in person_list %}
        <p>{{ person.name }}</p>
    {% empty %}
        <p>sorry,no person here</p>------为空的时候 展示
    {% endfor %}


if判断

#{% if %}会对一个变量求值,如果它的值是“True”(存在、不为空、且不是boolean类型的false值),满足那个条件就执行那个条件
    {% if num > 100 or num < 0 %}
        <p>无效</p>  <!--不满足条件,不会生成这个标签-->
    {% elif num > 80 and num < 100 %}
        <p>优秀</p>
    {% else %}  <!--也是在if标签结构里面的-->
        <p>凑活吧</p>
    {% endif %}
#if语句支持 and 、or、==、>、<、!=、<=、>=、in、not in、is、is not判断,注意条件两边都有空格。
-------------------
if要注意的内容:
    1.不支持算术运算(过滤器)
    2.不支持连续判断(10>2>0)


with

#使用一个简单地名字缓存一个复杂的变量,多用于给一个复杂的变量起别名
#方式一:
	{% with total=business.employees.count %}
    {{ total }} <!--只能在with语句体内用-->
	{% endwith %}
#方式er:
	{% with business.employees.count as total %}
    {{ total }}
	{% endwith %}


csrf 跨站请求伪造:csrf_token

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


模版继承(母版继承)

注意点:

​ 1. {% extends 'base.html' %} 母版的名字有引号的 不带引号会当做变量

{% extends 'base.html' %} 上不要写内容,想显示的内容要写在block块中
多定义点block块,有css js

模版继承可以创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
<!--设置一个共同的HTML模版(一个HTML文件,base.html)-->
<!--其他的html文档来继承这个公共的母版-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>home</title>
    <!--设置block方法,block中包含的内容,在其他html页面继承后可以进行修改,-->
    <!--block可以设置多个,可以在页面的任意位置,包括css,js内容-->
    {% block style %}
    <style>
        h1{
            background-color: red;
        }
    </style>
    <!--申明block方法的结束-->
    {% endblock %}
</head>
<body>
    <h1>你好世界</h1>
    {% block connect %}
    <h1>修改的内容</h1>
    {% endblock %}
</body>
</html>
***************************************************
<!--继承页面的写法-->
<!--申明继承于那个页面,必须放在开头的位置-->
{% extends 'base.html' %}
<!--使用block来申明要修改的内容,block后面的变量必须和模板中的一一对应,指定要修改的部分-->
<!--页面中写和母版中名字相同的block块,从而来显示自定义的内容-->
{% block style %}
<style>
    h1{
        background-color: blue;
    }
</style>
{% endblock %}

{% block connect %}
	<!--block.super可以显示继承的母版中的connect中的内容-->
    {{ block.super }}
    <h1>修改后的内容</h1>
{% endblock %}


组件

 一小段HTML代码段 —-> HTML文件中

​ {% include 'nav.html' %}
//可以将常用的页面内容如导航条,页尾信息等组件保存在单独的文件中,然后在需要使用的地方,文件的任意位置按如下语法导入即可。
//设置一个组件,就是新建一个html文件,然后在这个文件中封装组件的内容
//组件的意思类似于python中的函数的,在其他地方进行引用
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .c1{
            background-color: red;
        }
    </style>
</head>
<body>
    <div class="c1">
        <span>个人中心</span>
        <span>首页</span>
        <span>注册</span>
        <span>登陆</span>
    </div>
</body>
</html>
#**********************************************************
//在其他的html中使用这个组件,
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    //使用include方法来引用组件,后面写的是导入那个组件,导入后放到当前引入的位置
    //导入组件会导入所有的内容,包括css样式,js代码等,都是放在body标签中,不会影响当签页面的效果
    {% include 'index.html' %}
    <h1>新项目首页</h1>
</body>
</html>
#**********************************************************
//模版继承和组价的区别:
	模版是一个公共模版,是一个底板,其他页面只是修改他的内容,用的还是他的结构,
    组件类似一个插件,封装了一些功能,别的页面只是引入使用这些功能,组件中的页面效果,不会影响当前页面中的设置的效果
//www.jq22.com中有一些封装好的插件可以直接使用


自定义标签和过滤器

自定义过滤器

'''定义一个无参数过滤器'''
1:#:在当前的应用中创建一个templatetags的一个包(必须叫这个名字),然后在文件夹中创建一个python文件,名字随便
2:#在新建的python中引入django模块的template类,然后实例化一个register对象,对象的名字必须是register
    from django import template
    register = template.Library()#register对象名字不可更改
3:#在新建的python中定义过滤器:
    #使用register.filter装饰器装饰函数,就变成一个过滤器,使函数生效
    @register.filter
	def addhello(arg1):
    '''
    :param arg1:arg参数是使用过滤器时管道符前面变量的值
    :return:返回过滤器处理数据的结果
    '''
    	return arg1+'你好'
4:#在html文件中使用过滤器
    '''首先引入写着过滤器的这个python文件,会自动在templatetags的文件夹下找这个文件'''
    {% load 文件名 %}#例如:{% load filter %}文件名不用加引号
    '''使用过滤器'''
    {{ name | addhello }}#addhello是自己定义的一个过滤器(也就是那个函数名),然后返回一个新的值填充

'''*******************************************************************'''
'''定义一个有参过滤器'''
#定义有参过滤器就是在函数中增加一个参数,自定义过滤器函数中最多只能有两个参数
@register.filter
def addhello(arg1,arg2):
    '''
    :param arg1: 使用过滤器变量的值
    :param arg2: 传入的参数,就是管道符后面冒号后面的参数,过滤器最多只能有这两个参数
    :return: 返回处理数据后的值
    '''
    try:
        arg1=int(arg1)
        arg2=int(arg2)
   	    return arg1+arg2
    except Exception:
        return '111' 
#使用示例:
	{{ name | addhello:'你好' }}


模板自定义方法

1.filter 
2.simple_tag 
3.inclusion_tag
--------------------
自定义过滤器:

1.在已注册的app下创建templatetags(必须叫这个名字)的python包;
2.在包中创建py文件 (文件名任意指定 my_tags.py)
3.在py文件中写固定的代码:

from  django  import template
register = template.Library()   #  register的名字也不能错
4.写函数 + 加装饰器
- # filter
@register.filter
def str_upper(value,arg)  ---最多两个参数
	return 'xxxxx'
------
- #simple_tag
@register.simple_tag
def str_join(*args,**kwargs):---可以有很多参数
	return 'xxxxx'
------
- #inclusion_tag
@register.inclusion_tag('li.html')
def show_li(num,*args,**kwargs):  ---可以有很多参数
    return {'num':range(1,num+1)}

# li.html
<ul>
	{% for i in num %}
    	<li>{{ i }}</li>
    {% endfor %}
</ul>
5.使用
{% load my_tags  %}#先引入那个函数的文件

- {{ 'xxx'|str_upper:'参数'  }}   

- {% str_join 1 2 3 k1=4 k2=5 %}

- {% show_li 3 %}
' 
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul>
'


自定义标签

#和自定义过滤器一样,在template文件夹中的python中来定义标签,引入方式也相同
@register.simple_tag#register.simple_tag装饰器申明函数是一个自定义标签
def newtag(arg1,arg2):
    '''
    自定义标签没有参数限制,可以是多个参数,但是使用的时候要一一对应
    :param arg1:传入的参数一
    :param arg2:传入的参数二
    :return:返回处理后的数据
    '''
    return arg1+arg2
#引用示例:
	{% newtag name '世界' %}注意格式:标签使用是{% %}的形式
    #第一个参数是要使用的自定义标签的函数名,后面是要传入的参数,name是一个变量传的参数,函数有几个参数,就传入几个,一一对应


返回HTML片段的标签

##和自定义过滤器一样,在template文件夹中的python中来定义标签,引入方式也相同
#register.inclusion_tag装饰器来申明是一个返回html片段的标签,括号中申明使用的组件
@register.inclusion_tag('index.html')
def res(n1):
    '''
    参数可以随意设置,使用时要一一对应
    标签返回的值必须是字典格式的,键是前端申明的变量名,值就是变量对应的数据
    '''
    return {'li':[11,22,33]}
引用示例:
    {% res %}注意格式:标签使用是{% %}的形式
        
#返回HTML片段标签的执行流程
	'''概念'''
    1:在使用组件的时候,如果组件中有for循环这样的逻辑便签,在加载组件的时候,就会查找这个数据
    2:如果后台没有返回这个数据,就无法获取变量的值,就不会产生数据,这个时候就可以使用返回HTML片段标签,给组件返回一个值
   	'''流程'''
	1:在页面加载到返回HTML片段标签的时候,就先去标签中生成一个值,
    2:值不会直接返回给HTMl页面,而是吧值返回给标签关联的组件,去执行逻辑,渲染组件标签
    3:组件标签渲染完成之后,html页面会加载组件,渲染组件产生的标签


内置的过滤器

内置的过滤器
https://docs.djangoproject.com/en/1.11/ref/templates/builtins/#built-in-filter-reference

static¶
To link to static files that are saved in STATIC_ROOT Django ships with a static template tag. If the django.contrib.staticfiles app is installed, the tag will serve files using url() method of the storage specified by STATICFILES_STORAGE. For example:

{% load static %}
<img src="{% static "images/hi.jpg" %}" alt="Hi!" />

It is also able to consume standard context variables, e.g. assuming a user_stylesheet variable is passed to the template:

{% load static %}
<link rel="stylesheet" href="{% static user_stylesheet %}" type="text/css" media="screen" />
If you’d like to retrieve a static URL without displaying it, you can use a slightly different call:

{% load static %}
{% static "images/hi.jpg" as myphoto %}
<img src="{{ myphoto }}"></img>


静态文件配置

 {% load static %}

​ {% static '相对路径' %}

​ {% get_static_prefix %} 相当于是STATIC_URL里面的路径,  获取静态文件的别名
*-----------------------
1:#在django项目下创建一个static静态文件文件夹,在static文件夹下创建对应的css,img,js文件夹,用来存放css样式文件,图片和js代码,用于HTML引入
2:#在django项目的settings配置文件中,配置静态文件相关配置(setting文件的最下面)
    STATIC_URL = '/static/' #静态文件的文件路径别名,别名可以改,一般不改
    STATICFILES_DIRS = [  #静态文件的文件路径,就是创建的static的静态文件夹路径,变量必须是这个名字,django会指定这个变量名来查找文件路径
        os.path.join(BASE_DIR,'static')
    ]
    
3:HTML页面引入
{% load filter %} 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    #引入css文件
    <link rel="stylesheet" href="/static/css/index.css">
</head>
<body>
    {% res %}
    <h1>新项目首页</h1>
    <h1>{{ name | addhello:'你好个屁' }}</h1>
    <h1>{%  newtag '你好' '世界' %}</h1>
    #引入图片
    <img src="/static/img/1.jpg" alt="">
</body>
#引入js文件
<script src="/static/js/index.js"></script>
</html>
--------------
http://www.jq22.com      前端网址


URL别名和反向解析

#url写法
	url(r'^home2/', views.home,name='home'),#name给路径其别名
    url(r'^index3/', views.index,name='index'),

#当页面的路径发生变化的时候,后台代码中所有的相关的路径都要发生改变,使用反向解析,使用别名的方式,就不会发生这种情况,修改路径只要在url的路由分发修改就可以了,后台中使用反向解析就可识别路径
from django.shortcuts import render,HttpResponse,redirect
from django.urls import reverse

def home(request):
    if request.method=='GET':
        return render(request,'home.html',{'name':'张达'})
    else:
        print('111')
        if request.POST.get('username')=='lijie':
            #reverse可以反向解析出别名代表的路径地址,
            return redirect(reverse('index'))

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

#**************************************************************
#前段示例:
	<a href="{% url 'index' %}"></a>
    把路径变成{% url '路径别名' %},就可以反向解析路径


URL命名空间

路由分发 include

#在一个项目中可能有多个应用,如果使用项目的urls文件的话,不利于维护,可以使用include路由分发的方式,分发给不同的应用
1 在每个app下创建urls.py文件,写上自己app中每个页面的路径
2 在项目目录下的urls.py文件中做一下路径分发:
    from django.conf.urls import url,include
    from django.contrib import admin
    urlpatterns = [
        #当进来的路径是以app01/开头的,就会吧路径分发到app01的应用中,比如访问路径是app01/home/,以app01开头,就会home/这个路径分发给app01下面的urls文件去
        url(r'^app01/', include('app01.urls')),
        url(r'^app02/', include('app02.urls')),
    ]


命名空间namespace

#使用include分发不同路由的时候,如果在每个APP中有重复出现的路径别名,name在进行反向解析的时候,会在各个应用中查找这个路径别名,会吧最后一个查找到的路径进行解析,就会出现不是请求的app应用中那个别名的问题,使用namespace命名空间的方式,可以执行这个别名属于那个命名空间:就不会出现这个问题
示例代码:
    from django.conf.urls import url,include
	from django.contrib import admin
urlpatterns = [
    #使用namespace指定命名空间的名称,
    url(r'^app01/', include('app01.urls',namespace='app01')),
    url(r'^app02/', include('app02.urls',namespace='app02')),
      
#在每个app应用下,使用URL反向解析的时候指定别名的命名空间:
    后台:reverse('命名空间名称:别名') -- reverse('app01:home') #指定只在app01中查找这个别名,进行解析
    前段:hmtl:{% url '命名空间名称:别名' %}  -- {% url 'app01:home' %}


命名空间模式

举个例子:

#project中的urls.py
from django.conf.urls import url, include
 
urlpatterns = [
    url(r'^app01/', include('app01.urls', namespace='app01')),
    url(r'^app02/', include('app02.urls', namespace='app02')),
]
#app01中的urls.py
from django.conf.urls import url
from app01 import views
 
app_name = 'app01'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]
#app02中的urls.py
from django.conf.urls import url
from app02 import views
 
app_name = 'app02'
urlpatterns = [
    url(r'^(?P<pk>\d+)/$', views.detail, name='detail')
]
#现在,我的两个app中 url名称重复了,我反转URL的时候就可以通过命名空间的名称得到我当前的URL。
#语法:
'命名空间名称:URL名称'

#模板中使用:
{% url 'app01:detail' pk=12 pp=99 %}

#views中的函数中使用
v = reverse('app01:detail', kwargs={'pk':11})
#这样即使app中URL的命名相同,我也可以反转得到正确的URL了。 


ORM

orm介绍

ORM对象关系映射
​ 类 ——》 表
​ 对象 ——》 数据行(记录)
​ 属性 ——》 字段
#ORM是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)
#类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎
orm语句 -- sql -- 调用pymysql客户端发送sql -- mysql服务端接收到指令并执行


ORM操作

from app01.models import User
User.objects.all()  #  查询所有的数据   QuerySet 对象列表  【  对象 】
User.objects.get(username='alexs',password='alexdsb')  
# 对象  get  只能获取数据库唯一存在的数据  有且唯一 (不存在或者多条数据就报错)

User.objects.filter(password='dsb')  # 获取满足条件的所有对象  对象列表


登录示例

form表单提交数据

1.action="" 向当前的地址进行提交 method="post" 请求方式 
2. input需要有name属性 value值 
3. button按钮或者input 类型是submit

在settings中注释一行代码,注释后可以提交post请求:
MIDDLEWARE
'django.middleware.csrf.CsrfViewMiddleware' 


request.method  获取请求方式 GET POST
request.POST    POST请求的数据  {} 
request.POST.get()   获取post请求中的值
redirect(‘要重定向的地址’)   重定向 


1.创建一个mysql数据库
2.#在django项目的配置文件中,修改链接数据库的配置,django默认链接自带数据库sqllite3,改成链接mysql数据库
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',#引擎  数据库类型 申明链接mysql
            'NAME':'orm01',		#数据库名称 要连接的数据库,连接前需要创建好  
            'USER':'root',		# 连接数据库的用户名
            'PASSWORD':'123',# 连接数据库的密码
            'HOST':'127.0.0.1',  #IP地址
            'PORT':3306,	#端口号}
      'app01': { #可以为每个app都配置自己的数据,并且数据库还可以指定别的,也就是不一定就是mysql,也可以指定sqlite等其他的数据库
            'ENGINE': 'django.db.backends.mysql',
            'NAME':'bms',           # 要连接的数据库,连接前需要创建好
            'USER':'root',        # 连接数据库的用户名
            'PASSWORD':'',        # 连接数据库的密码
            'HOST':'127.0.0.1',       # 连接主机,默认本级
            'PORT':3306            #  端口 默认3306 }}
3.:#告诉django使用pymysql模块连接数据库 ,在项目的文件夹中的__init__.py文件中,申明使用pymysql模块链接mysql服务端,django默认使用mysqldb链接数据库服务端,mysqldb并不支持python3.4以上版本,用pymysql替换mysqldb;
    import pymysql
    pymysql.install_as_MySQLdb()
4.:#在应用的models文件中新建一个类:
    from django.db import models
    #类名就是新建的表的表名,orm要求每一个表中都必须有一个主键,没有设置就自动生成一个自增主键,叫ID字段
    class UserInfo(models.Model):
        #添加ID字段,AutoField为自增属性字段,参数是设置为主键, verbose_name可以当作注释,可写可不写
        id=models.AutoField(verbose_name='id',primary_key=True)
        #添加一个name字段CharField代表varchar类型,参数max_length执行长度
        name=models.CharField(verbose_name='用户名',max_length=10)
        #Date类型,只有年月日,传入数据的时候必须是"2018-12-12"这种格式
        bday=models.DateField()
        #布尔值类型,数据库中bool值存储的是tinyInt类型,存的是0或者1数字
        checked=models.BooleanField()
5.:#执行数据库同步指令,
	#生成记录,每次修改了models里面的内容或者添加了新的app,新的app里面写了models里面的内容,都要执行这两条
    #执行makemigrations命令会在当前应用中的migrations模块中生成一个记录,
	python manage.py makemigrations
    #执行上面这个语句的记录来创建表,生成的表名字前面会自带应用的名字,
    #例如:你的book表在mysql里面叫做app01_book表
	python manage.py migrate
6:#往表中添加数据:
    #导入应用中的midels模块
    from app01 import models
    new_obj=models.UserInfo(
    	id=1,name='张达',bday='2019-09-10',chenked=1,)
    #save方法把上面的语句翻译成sql语句,然后调用pymysql,发送给mysql服务端,
    new_obj.save()
'''
id=models.AutoField(verbose_name='id',primary_key=True)
varbose_name给字段加一个注释,不写默认不会报错,目前可以当做注释使用
以后会在model from和from中使用
'''


更多字段和参数

'''
<1> CharField      *******
        字符串字段, 用于较短的字符串.
        CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数.
 
<2> IntegerField   *******
       #用于保存一个整数.
 
<3> DecimalField    *******
        一个浮点数. 必须 提供两个参数:
        参数    描述
        max_digits    总位数(不包括小数点和符号)
        decimal_places    小数位数
                举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段:
                models.DecimalField(..., max_digits=5, decimal_places=2)
                要保存最大值一百万(小数点后保存10位)的话,你要这样定义:
                models.DecimalField(..., max_digits=17, decimal_places=10) #max_digits大于等于17就能存储百万以上的数了
                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参数.
'''
#更多参数
(1)null
 
如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False.
 
(1)blank
 
如果为True,该字段允许不填。默认为False。
要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。
如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。
 
(2)default
 
字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用,如果你的字段没有设置可以为空,那么将来如果我们后添加一个字段,这个字段就要给一个default值
 
(3)primary_key
 
如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,
Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为,
否则没必要设置任何一个字段的primary_key=True。
 
(4)unique
 
如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的
 
(5)choices
由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,<br>而且这个选择框的选项就是choices 中的选项。
(6)db_index
  如果db_index=True 则代表着为此字段设置数据库索引。

DatetimeField、DateField、TimeField这个三个时间字段,都可以设置如下属性。

(7)auto_now_add
    配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库。

(8)auto_now
    配置上auto_now=True,每次更新数据记录的时候会更新该字段,标识这条记录最后一次的修改时间。


增加数据

'''方式一'''
#导入应用中的midels模块
    from app01 import models
    #实例化对象,会把对象的参数,当作数据插入到数据库中
    new_obj=models.UserInfo(
    	id=1,name='张达',bday='2019-09-10',chenked=1,)
    #save方法把上面的语句翻译成sql语句,然后调用pymysql,发送给mysql服务端,
    new_obj.save()
'''方式二'''
	#new_obj是当前创建新的数据的model对象
    imnport datetime
    #传入的时间可以是获取的当前时间
    data_time=datetime.datetime.now()
	new_obj=models.UserInfo.objects.create(
        id=1,
        name='张达',
        bday=data_time,
        checked=0
    )
    print(new_obj)#打印对象:UserInfo object
    print(new_obj.name)


批量增加数据

#导入应用中的midels模块
    from app01 import models
    #定义一个空的列表用来盛放所有的数据
    obj_list=[]
    #实例化对象,会把对象的参数,当作数据插入到数据库中
    for i in range(20):
        new_obj=models.UserInfo(
            id=i,
            name='张达'+i,
            bday=f'2019-09-{i+1}',
            chenked=1,)
        obj_list.append(new_obj)
    #bulk_create方法可以批量添加数据
    models.UserInfo.objects.bulk_create(obj_list)
-*----------------------
应用:从文件中读出来,批量在数据库中添加数据
    python 操作 exsil表格


添加字段

from django.db import models
1:#在models文件中添加一个字段
    class UserInfo(models.Model):
    id=models.AutoField(primary_key=True)
    name=models.CharField(max_length=10)
    bday=models.DateField()
    checked=models.BooleanField()
    #新增一个字段,orm添加字段,该字段不能为空,所以要么给默认值,要么设置它允许为空 null=True
    now=models.DateTimeField(null=True)
2:#当models中的数据发生改变执行python mange.py makemigrations和python manage.py migrate指令
    python mange.py makemigrations  执行指令会产生一个新的记录,然后在执行migrate指令,发送数据到服务端
'''DateTimeField时间问题'''
	#DateTimeField设置参数auto_now_add=True,在添加数据的时候自动帮你添加当前时间
    #添加的事件存在时区问题,使用的时区是格林尼治(UTC)时区,
	now=models.DateTimeField(auto_now_add=True,null=True)
    #auto_now属性更新数据时,自动添加更新当前时间,创建新数据时,也会自动添加时间
    now=models.DateTimeField(auto_now=True,null=True)
    #解决方法:
    	settings配置文件中将USE_TZ的值改为False:告诉mysql存储时间是按照当地时间来存储
        使用pycharm的数据库客户端的时候,时区也是utc时间,要注意修改


删除数据

1:#filter()简单查询
    返回的是queryset类型的数据,里面是一个个的model对象,类似于列表,因为查到的可能是多个数据
    #queryset对象删除数据,如果查询到的是多条数据,就会删除这些查到的所有数据
    models.UserInfo.objects.filter(id=7).delete()  
    #model对象调用删除,使用索引具体到某一条数据,删除只会删除这一条数据
    models.UserInfo.objects.filter(id=7)[0].delete()  
    #删除所有的数据
    models.UserInfo.objects.all().delete()


修改数据

#方式一:update
	#update方法只能用queryset对象可以调用,model对象不能使用update方法
	models.UserInfo.objects.filter(id=2).update(
         name='篮子文',
         checked = 0,)
#方式二:
	#使用model对象修改,直接设置值的方式直接修改
	ret = models.UserInfo.objects.filter(id=2)[0]
    ret.name='lijie'
    ret.checked=1
    ret.save()#save提交数据
    
#修改数据时时间auto_now参数:
	# 更新记录时,自动更新时间,创建新纪录时也会帮你自动添加创建时的时间,但是在更新数据时只有使用save方法的方式2的形式更新才能自动更新时间,update方式不会生效,
    now2 = models.DateTimeField(auto_now=True,null=True)


打印一个对象,显示一个能够看懂的值,str

from django.db import models

# Create your models here.

class Book(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2,)
    pub_date = models.DateTimeField() #必须存这种格式"2012-12-12"
    publish = models.CharField(max_length=32)
    def __str__(self): #后添加这个str方法,也不需要重新执行同步数据库的指令
        return self.title #当我们打印这个类的对象的时候,显示title值


查询API

#all() 查询所有
	查询数据库中的所有结果,结果是queryset类型
    models.UserInfo.objects.all() 返回的数据库中所有的数据
#filter() 条件查询
	查询条件不能匹配到数据时,不会报错,返回一个空的queryset,<QuerySet []>,如果没有写查询条件会获取所有数据,获取的是queryset对象,queryset类型的数据还能够继续调用fitler方法
    ret = models.Book.objects.filter(title='jin瓶梅7',publish='24期出版社')可以指定多个条件查询,相当于原生sql的and语句,
#get() model对象查询
	get获取的是一个model对象,有且只能有一个,查询不到,或者查询到多个都会报错
    1. 查不到数据会报错 :Book matching query does not exist
    2. 超过一个就报错 :returned more than one Book -- it returned 13!
#exclude() 排除
	指定排除与条件匹配的数据,返回值是queryset类型 
	1.object能够调用,models.Book.objects.exclude(id=6)
    2.queryset类型数据能够调用, models.Book.objects.all().exclude(id=6)
#order_by()排序
	queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
	指定条件排序,可以指定多个排序字段,在条件中加上减号就是倒序排列
    models.Book.objects.all().order_by('-price','id')先根据price倒序排列,然后数据相同的再根据id排序
#reverse() 反转
	queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型,数据哟啊先排序再反转
    models.Book.objects.all().order_by('id').reverse()
#count() 计数
	queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量
    models.Book.objects.all().count() 查询一共有多少条数据
#first()    
	返回第一条数据,结果是model对象类型
    Book.objects.all()[0] = Book.objects.all().first()这两种写法的意思相同
#last()
	返回最后一条数据,结果是model对象类型
    Book.objects.all()[-1] = Book.objects.all().last()这两种写法的意思相同
#exists()
	判断返回结果集是不是有数据
 	queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
    models.Book.objects.filter(id=9999).exists() #有结果就是True,没有结果就是False
#values()
	返回的queryset对象,里面的数据是字典的形式,每一条数据是一个字典,字段为键,数据为值,
    ret = models.Book.objects.filter(id=9).values('title','price') #条件查询返回一个字典
    ret = models.Book.objects.values() #返回所有的数据
#values_list
	返回的queryset,里面是数组类型数据,它返回的是一个元组序列,把一条数据封装成一个元祖
    ret = models.Book.objects.all().values_list('title','price')#返回的是一个元祖
    ret = models.Book.objects.values_list()#返回所有数据
#distinct() 去重
	values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
    models.Book.objects.all().values('name').distinct()#吧name值相同的去除掉
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 <1> all():  查询所有结果,结果是queryset类型
  
<2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象,结果也是queryset类型 Book.objects.filter(title='linux',price=100) #里面的多个条件用逗号分开,并且这几个条件必须都成立,是and的关系,or关系的我们后面再学,直接在这里写是搞不定or的
  
<3> get(**kwargs):          返回与所给筛选条件相匹配的对象,不是queryset类型,是行记录对象,返回结果有且只有一个,
                            如果符合筛选条件的对象超过一个或者没有都会抛出错误。捕获异常try。  Book.objects.get(id=1)
  
<4> exclude(**kwargs):      排除的意思,它包含了与所给筛选条件不匹配的对象,没有不等于的操作昂,用这个exclude,返回值是queryset类型 Book.objects.exclude(id=6),返回id不等于6的所有的对象,或者在queryset基础上调用,Book.objects.all().exclude(id=6)
                 
<5> order_by(*field):       queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值还是queryset类型
                  models.Book.objects.all().order_by('price','id') #直接写price,默认是按照price升序排列,按照字段降序排列,就写个负号就行了order_by('-price'),order_by('price','id')是多条件排序,按照price进行升序,price相同的数据,按照id进行升序
        
<6> reverse():              queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型
  
<7> count():                queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量。
  
<8> first():                queryset类型的数据来调用,返回第一条记录 Book.objects.all()[0] = Book.objects.all().first(),得到的都是model对象,不是queryset
  
<9> last():                queryset类型的数据来调用,返回最后一条记录
  
<10> exists():              queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False
                   空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits
                 例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS `a` FROM `app01_book` LIMIT 1,就是通过limit 1,取一条来看看是不是有数据

<11> values(*field):        用的比较多,queryset类型的数据来调用,返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列
                            model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续链式调用queryset类型的其他的查找方法,其他方法也是一样的。
<12> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
 
<13> distinct():            values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录
"""
返回对象列表  QuerySet
all()  
filter() 
exclude()
values()    [ {},{} ]
values_list()    [ (),() ]
order_by()
reverse() 
distinct()

返回对象
get
first
last

返回数字
count

返回布尔值
exists() 
"" 


双下划线模糊查询

Book.objects.filter(price__in=[100,200,300]) #price值等于这三个里面的任意一个的对象
Book.objects.filter(price__gt=100)  #大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__lt=100)	#小于,小于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__range=[100,200])  #sql的between and,大于等于100,小于等于200
Book.objects.filter(title__contains="python")  #title值中包含python的
Book.objects.filter(title__icontains="python") #不区分大小写
Book.objects.filter(title__startswith="py") #以什么开头,区分大小写
Book.objects.filter(title__istartswith="py") #以什么开头,#istartswith  不区分大小写
Book.objects.filter(title__endswith="py") #以什么结尾, 区分大小写
Book.objects.filter(title__iendswith="py") #以什么结尾,iendswith  不区分大小写
Book.objects.filter(pub_date__year=2012) #查询时间是2012年的所有数据
#某年某月某日:
ret = models.Book.objects.filter(publish_date__year='2018') #查询时间是2018年的所有数据
ret = models.Book.objects.filter(publish_date__year__gt='2018')#2018写数字也可以
ret = models.Book.objects.filter(publish_date__year='2019',publish_date__month='8',
                                 publish_date__day='1')#2019-08-01的所有数据
#找字段数据为空的双下滑线
models.Book.objects.filter(publish_date__isnull=True) #publish_date这个字段值为空的那些数据    # __isnull=False  不为null


多表表结构

一对一在少的表中创建
一对多在多的表中创建
多对多新创建一个表


from django.db import models
# Create your models here.
class Author(models.Model):
    """
    作者表
    """
    name=models.CharField( max_length=32)
    age=models.IntegerField()
    # authorDetail=models.OneToOneField(to="AuthorDetail",to_field="nid",on_delete=models.CASCADE)
    #与AuthorDetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里面都可以
    au=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)
	#就是foreignkey+unique,只不过不需要我们自己来写参数了,并且orm会自动帮你给这个字段名字拼上一个_id,数据库中字段名称为au_id
class AuthorDetail(models.Model):
    """
    作者详细信息表
    """
    birthday=models.DateField()
    telephone=models.CharField(max_length=11)
    addr=models.CharField(max_length=64)
class Publish(models.Model):
    """
    出版社表
    """
    name=models.CharField( max_length=32)
    city=models.CharField( max_length=32)

class Book(models.Model):
    """
    书籍表
    """
    title = models.CharField( max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2)
    publishs=models.ForeignKey(to="Publish",on_delete=models.CASCADE,)
    ## 通过ORM自带的ManyToManyField自动创建第三张表,与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看book表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个book类里面有authors这个字段属性
    authors=models.ManyToManyField('Author',)
 
'''********************************************************************'''
#models.OneToOneField (to="Publish",to_field="nid",on_delete=models.CASCADE)
	建立一对一的关系时候使用,to指向被关联的表,to_field指向你关联的字段,不写这个,默认会自动关联主键字段,on_delete级联删除
#models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
	建立一对多的关系的时候使用
#models.ManyToManyField(to='Author',)    
	建立多对多关系的时候使用,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表,并且注意一点,你查看表的时候,你看不到这个字段,因为这个字段就是创建第三张表的意思,不是创建字段的意思,所以只能说这个类里面有这个字段属性,产生的第三张表中有一个默认的id字段,然后是两张被关联表中的id字段,第三张表的表名是以两个被关联数据库的名字来命名,中间加上一个下划线,类名在前,属性在后
'''所有的外键字段都会被自动加上_id字段.所有在建外键的时候,不要写_id,orm会自动帮你追加'''
'''db_constrtaint=False'''消除外键的强制消除关系


多对多关系表的创建方式:

1.django自己创建
class Book(models.Model):
    title = models.CharField(max_length=32, unique=True)
    pub = models.ForeignKey('Publisher', on_delete=models.CASCADE)
 

class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book')  # 不会生成字段  生成第三张表
 ---------------------------------------------------
2.自己手动创建     (是因为Django自动创建的时候只有两个字段,自己创建可以写多个字段)
class Book(models.Model):
    title = models.CharField(max_length=32, unique=True)

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

class AuthorBook(models.Model):
    book = models.ForeignKey('Book',on_delete=models.CASCADE)
    author = models.ForeignKey('Author',on_delete=models.CASCADE)
    date = models.CharField(max_length=32)
--------------------------------------------------
3.半自动创建(自己手动创建表 + ManyToManyField 可以利用查询方法 不能用set)
class Book(models.Model):
    title = models.CharField(max_length=32, unique=True)

class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book',through='AuthorBook')  # 只用django提供的查询方法  不用创建表 用用户创建的表AuthorBook

class AuthorBook(models.Model):
    book = models.ForeignKey('Book',on_delete=models.CASCADE)
    author = models.ForeignKey('Author',on_delete=models.CASCADE)
    date = models.CharField(max_length=32)
----------------------------------
class Book(models.Model):
    title = models.CharField(max_length=32, unique=True)

class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField('Book',through='AuthorBook',through_fields=['author','book'])  # 只用django提供的查询方法  不用创建表 用用户创建的表AuthorBook

class AuthorBook(models.Model):
    book = models.ForeignKey('Book',on_delete=models.CASCADE,null=True)
    author = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='x1',null=True)
    tuiianren = models.ForeignKey('Author',on_delete=models.CASCADE,related_name='x2',null=True)
    date = models.CharField(max_length=32)


创建表时的元信息

元信息
    ORM对应的类里面包含另一个Meta类,而Meta类封装了一些数据库的信息。主要字段如下:
class Author2Book(models.Model):
    author = models.ForeignKey(to="Author")
    book = models.ForeignKey(to="Book")
    class Meta:
        unique_together = ("author", "book")

#db_table
    ORM在数据库中的表名默认是 app_类名,可以通过db_table可以重写表名。db_table = 'book_model'

#index_together
    联合索引。

#unique_together
    创建联合唯一索引。

#ordering
    指定默认按什么字段排序。
    ordering = ['pub_date',]
    ordering = ['-id','pub_date']
    只有设置了该属性,我们查询到的结果才可以被reverse(),否则是能对排序了的结果进行反转(order_by()方法排序过的数据)


admin后台管理系统

使用admin的步骤:
1.创建一个超级用户
python manage.py createsuperuser
输入用户名 和 秘密

2.在app下的admin.py中注册model
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.Person)

3.地址栏输入/admin/
   
#admin是django提供的管理数据的一个平台,可以查看项目的所有数据,包括查看数据,编辑数据,等
1:# 在manage.py Task中创建超级用户 :createsuperuser,输入创建账号,密码,创建用户
2:# 在应用的admin文件中添加表,在admin中就可以查看到表了,可以表进行操作
    admin.site.register(models.Author)
    admin.site.register(models.AuthorDetail)
    admin.site.register(models.Publish)
    admin.site.register(models.Book)


模板语法

{{ 变量  }}

for循环
{% for i in 变量  %}
	循环体 {{ i }}
{% endfor  %}


多表的增删改查

多表增加

# 一对一
	#查询要被关联的属相对象
    au_obj = models.AuthorDetail.objects.get(id=4)
    models.Author.objects.create(
        name='海狗',
        age=59,
        #方式一:直接通过数据库的字段名au_id增加,一对一时,数据必须是没有关联过的,否则会报错
        au_id=4,
        #方式二:通过属性models类的属性添加,首先要查出要关联的对象,然后通过属性关联,一对一数据必须未关联
        au=au_obj,)
#一对多
    pub_obj = models.Publish.objects.get(id=3)
    models.Book.objects.create(
        title='xx2',
        price=13,
        publishDate='2011-11-12',
        #方式一:直接通过数据库的字段名au_id增加,如果关键字为数据库字段名称,那么值为关联数据的值
        publishs_id=3,
        #方式二:通过属性models类的属性添加, #类属性作为关键字时,值为model对象 
        publishs=pub_obj,)
    
   models.Book.objects.create(title=book_name,pub=models.Publisher.objects.get(pk=pub_id))
models.Book.objects.create(title=book_name,pub_id=pub_id)
 #多对多
	多对多添加数据是在中间表中添加关联,只能通过属性操作
    #查找要关联的作者
    ziwen = models.Author.objects.get(id=3)
    haigou = models.Author.objects.get(id=5)
    new_obj = models.Book.objects.create(
            title='海狗产后护理第二部',
            price=0.5,
            publishDate='2019-09-29',
            publishs_id=2,
    )
	#方式一:
    new_obj.authors.add(3, 5)
    #方式二:
    new_obj.authors.add(*[3, 5])  # 用的最多,
    #方式三:
    new_obj.authors.add(ziwen, haigou)
----------------------
author_obj = models.Author.objects.create(name=author_name)  # 插入作者信息
author_obj.books.set(book_id) # 设置作者和书籍的多对多的关系


多表删除

#一对一
	#删除被关联表的数据,级联删除,会自动删除关联表中的数据,如果没有设置级联删除,就不会删除关联表中的数据
    models.AuthorDetail.objects.filter(id=3).delete()
	#通过外键表删除,只会删除自己的数据,被关联表中的数据不会删除
    models.Author.objects.filter(id=3).delete()
#一对多(和一对一的一样)
    models.Publish.objects.filter(id=3).delete()
    models.Book.objects.filter(id=4).delete()
#多对多
	#多表删除只能通过删除中间表的关联信息,来消除关联,
    #获取要消除关联的数据,(书籍)
    book_obj = models.Book.objects.get(id=2)
    #删除于书籍book_obj相关的作者id为1的作者
    book_obj.authors.remove(1)  #删除
    #清除所有于书籍book_obj相关的关联数据
    book_obj.authors.clear()  # 清除
    #重新设置于书籍book_obj相关的关联作者信息
    book_obj.authors.set(['1','5'])  # 先清除再添加,相当于修改


多表修改

#orm表中的关联是关联id字段,所以修改字段数据,不会影响关联关系,就是单表修改数据
    ret = models.Publish.objects.get(id=2)
    models.Book.objects.filter(id=5).update(
        #通过属性来修改字段值
        title='华丽丽',
        publishs=ret,
        #通过数据库字段来修改值
        publishs_id=1,
    )
#多对多修改,是修改中间表的数据,来设置关联关系,通过set设置
	#重新设置于书籍book_obj相关的关联作者信息
	book_obj.authors.set(['1','5'])  # 先清除再添加,相当于修改


多表查询

#一对一查询:
# 关系属性写在表1,关联到表2,那么通过表1的数据去找表2的数据,叫做正向查询,返过来就是反向查询	
#正向查询:
	obj = models.Author.objects.filter(name='王洋').first()
    #正向查询,通过属性查找,会自动查找被关联表中的数据字段,对象.属性
	ph = obj.au.telephone
    # 反向查询会自动查找关联表中的数据字段,对象.属性对象.小写的表名
    obj = models.AuthorDetail.objects.filter(telephone=120).first()
    ret = obj.author.name
    print(ret)
#一对多:
	#正向查询:
    obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
    ret = obj.publishs.name
    #反向查询:
    #一堆多的情况下,反向查询是从关联表的数据查找被关联表中的数据,可能是多条数据,
    obj = models.Publish.objects.filter(name='24期出版社').first()
    #表名后面加__set就是可能查找到多条数据时使用,通过all的方式获取所有查询到的结果
	ret = obj.book_set.all() #查询到的是QuerySet对象
    #通过foe循环,查看每个结果的字段值
	for i in ret:
    	print(i.title)
#多对多:
    #正向查询:建表时的关系属性写在那个表中,通过这个表去查询,就是正向查询
	obj = models.Book.objects.filter(title='海狗的怂逼人生').first()
	ret = obj.authors.all()
    #反向查询:
    查询一个作者写了多少书
    obj = models.Author.objects.filter(name='海狗').first()
    ret = obj.book_set.all()
    for i in ret:
        print(i.publishs.name)
        print(i.title)


基于双下划线的跨表查询(连表查询 join)

#一对一查询
    from app01 import models
    # Create your views here.
    def home(request):
        '''正向查询'''
        #通过外键表中的外键属性查询被关联表中的值,就叫正向查询
        #第一种:已知本表(Author表)中的条件,通过外键属性查询被关联表(AuthorDetail表)中的数据,
        #values获取的是字典形式的数据,values_list获取的是元祖形式的数据
        ret1 = models.Author.objects.filter(name='李杰').values('au__telephone')
        print(ret1).....<QuerySet [{'au__telephone': '13222222222'}]>
        ret1 = models.Author.objects.filter(name='李杰').values_list('au__telephone')
        print(ret1).....<QuerySet [('13222222222',)]>
        #第二种:已知被关联表(AuthorDetail表)中的条件,通过外键属性查询有外键表(Author)中的数据
        ret2 = models.Author.objects.filter(au__telephone='13111111111')
        print(ret2)
        '''反向查询'''
        #通过被关联表中的字段值查询外键表中的数据,就叫反向查询
        #第一种:已知本表(AuthorDetail表)中的条件,通过小写关联表类名(Author表)的方式指定查询的数据
        ret3 = models.AuthorDetail.objects.filter(telephone='13111111111').values('author__name')
        print(ret3)
        #第二种:已知被关联表中的条件,通过小写关联表类名的方式,先跨表,再指定查询的数据
        ret4 = models.AuthorDetail.objects.filter(author__name='李杰').values('telephone')
        print(ret4)
        return HttpResponse('你好')
#一对多查询
	def home(request):
    	#查询python是那个出版社出版的
        '''正向查询'''
        ret1 = models.Book.objects.filter(title='python').values('publishs__name')
        print(ret1)
        '''反向查询'''
        ret2 = models.Publish.objects.filter(book__title='python').values('name')
        print(ret2)
        #查询山西出版社出版过哪几本书
        '''反向查询'''
        ret3 = models.Publish.objects.filter(name='山西出版社').values('book__title')
        print(ret3)
        '''正向查询'''
        ret4 = models.Book.objects.filter(publishs__name='山西出版社').values('title')
        print(ret4)
        return HttpResponse('你好')
#多对多查询
	def home(request):
        #查询python是那些作者写的
        '''正向查询'''
        ret = models.Book.objects.filter(title='python').values('authors__name')
        print(ret)
        '''反向查询'''
        ret1 = models.Author.objects.filter(book__title='python').values('name')
        print(ret1)
        return HttpResponse('你好')
    
----------------------------
related_name
查询一下24出版社出版了哪些书
ret = models.Publish.objects.filter(name='24期出版社').values('xxx_title')   #xxx代替反响查询的小写表名


聚合查询

#aggregate(*args, **kwargs)
# 计算所有图书的平均价格
>>> from django.db.models import Avg,Max,Min,Sum
>>> Book.objects.all().aggregate(Avg('price')) #或者给它起名字:aggretate(a=Avg('price'))
    {'price__avg': 34.35}#python字典格式,也就是说,聚合查询是orm语句的结束.是一个普通的字典
  aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。

>>> Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
  如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:

>>> from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))  #count('id'),count(1)也可以统计个数,Book.objects.all().aggregete和Book.objects.aggregate(),都可以
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}


分组查询

#示例代码:
	emp.objects.values("dep").annotate(c=Count("id") 
    #注意:annotate里面必须写个聚合函数,不然没有意义,并且必须有个别名=,别名随便写,但是必须有,用哪个字段分组,values里面就写哪个字段,annotate其实就是对分组结果的统计,统计你需要什么。
'''
  select dep,count('id') as c from emp grouby dep;  #原生sql语句中的as c,不是必须有的
'''
# 每个出版社出版的书的平均价格
	# 用的是publish表的id字段进行分组
    ret = models.Book.objects.values('publishs__id').annotate(a=Avg('price'))
    # 用的book表的publishs_id字段进行分组
    ret = models.Book.objects.values('publishs_id').annotate(a=Avg('price'))
    print(ret)
    #用的是publish表的id字段进行分组(反向形式)
    ret = models.Publish.objects.annotate(a=Avg('book__price')).values('a')
    print(ret) #<QuerySet [{'a': None}, {'a': 71.166667}, {'a': 6.0}]>                               


F查询

F查询指得是自己本表当中的一些字段的比较,Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作
from django.db.models import Avg, Sum, Max, Min, Count,F
#查询一下评论数大于点赞数的书
    ret = models.Book.objects.filter(comment__gt=F('good'))
    print(ret)
#将所有书的价格上调100块
models.Book.objects.all().update(
    price=F('price')+100)


Q查询

django.db.models import Q
Q(title__startswith='Py')
  #Q 对象可以使用&(与) 、|(或)、~(非) 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
'''   
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))
  等同于下面的SQL WHERE 子句:
WHERE name ="yuan" OR name ="egon"
'''
  你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:

bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")

bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用
  查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如:
bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017),
                              title__icontains="python"  #也是and的关系,但是Q必须写在前面
                             )


反向查询时,如果定义了related_name ,则用related_name替换 表名,例如:
publish = ForeignKey(Blog, related_name='bookList')#只能加在外键属性上
#反向查询 不再按表名:book,而是related_name:bookList
queryResult=Publish.objects.filter(name="人民出版社").values_list("bookList__title","bookList__price") 


ORM执行原生sql语句(了解)

Django 提供两种方法使用原始SQL进行查询:一种是使用raw()方法,进行原始SQL查询并返回模型实例;另一种是完全避开模型层,直接执行自定义的SQL语句。
#第一种:
	#raw()管理器方法用于原始的SQL查询,并返回模型的实例:注意:raw()语法查询必须包含主键。
    这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。 这个RawQuerySet 实例可以像一般的QuerySet那样,通过迭代来提供对象实例。
    for p in Person.objects.raw('SELECT * FROM myapp_person'):
		 print(p)
	#raw()查询可以查询其他表的数据。 
    ret = models.Student.objects.raw('select id, tname as hehe from app02_teacher')
    for i in ret:
        print(i.id, i.hehe)
    #raw()方法自动将查询字段映射到模型字段。还可以通过translations参数指定一个把查询的字段名和ORM对象实例的字段名互相对应的字典
    d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher', translations=d)
    for i in ret:
        print(i.id, i.sname, i.haha)
	#原生SQL还可以使用参数,注意不要自己使用字符串格式化拼接SQL语句,防止SQL注入
    d = {'tname': 'haha'}
    ret = models.Student.objects.raw('select * from app02_teacher where id > %s', translations=d, params=[1,])
    for i in ret:
        print(i.id, i.sname, i.haha)
#第二种:
	我们可以直接从django提供的接口中获取数据库连接,然后像使用pymysql模块一样操作数据库。
    from django.db import connection, connections
	cursor = connection.cursor()  # cursor = connections['default'].cursor()
	cursor.execute("""SELECT * from auth_user where id = %s""", [1])
	ret = cursor.fetchone()


cookie和session

装饰器回顾

#标准版装饰器
from functools import wraps
def wrapper(f):
    #保留函数的元数据(函数名/注释)这样对inner里面的数据就不会产生影响
    @wraps(f)
    def inner(*args,**kwargs):
        return f(*args,**kwargs)
    return inner

@wrapper
def func():
    print('你好世界')

#执行函数会先进行赋值
#func=wrapper(func)此时func变量指向的就是inner函数,函数名和函数的注释都会发生变化
#打印func.__name__和func.__doc__打印的都是inner的函数名和注释
#在装饰器里引入functools模块,给inner加上functools.wraps装饰器,就会在赋值的时候保留func函数的函数名和注释
func()


Django的请求生命周期

1:在浏览器发送请求的时候,会先通过wsgi接收,(wsgi本质就是一个socket服务端)
    wsgi是一个协议,叫做web服务网关接口,wsgi分两种wsgiref和uwsgi,wsgief是一个用于我们本地测试时使用,uwsgi是项目部署时使用的  
2:请求由wsgi接收后,交由Django的路由系统,进行路由匹配,匹配到相应的视图函数
3:到视图函数的时候,可能通过ORM去数据库查询数据,返回给数据库,也可能是通过模版(HTML页面)去查询对应的模版文件,然后在视图函数里面进行渲染,然后生成一个字符串返回给用户
4:通过wsgi send给浏览器端,(wsgi会帮你加上响应头这些)


1572405568874

jquery操作cookie: https://www.cnblogs.com/clschao/articles/10480029.html
        
        
cookie是由服务器发送,然后保存在用户浏览器端的一个键值对,在向服务器发送请求的时候会自动携带cookie信息
#示例代码
def home(request):
    result=render(request,'home.html')
    #设置一个cookie
    result.set_cookie('k1','lijie')
    return result

def login(request):
    #获取cookie,进行判断
    ret=request.COOKIES.get('k1')
    if ret=='lijie':
        return  render(request,'index.html')


cookie操作

#设置cookie
	result=render(request,'home.html')
	result.set_cookie('k1','lijie')
#读取cookie
	ret=request.COOKIES.get('k1')
#删除cookie
	delete_cookie('k1')
    #删除cookie起始是设置了cookie的过期时间,使cookie失效
#设置cookie的参数:
set_cookie(self, key, value='', max_age=None, expires=None, path='/', domain=None, secure=False, httponly=False)
    #key,代表的是键,
    #value是cookie值,
    #max_age是设置cookie的时间,单位是秒,默认是两周时间
    #expires是设置cookie时间,指的是一个具体的日期
    #path是设置cookie的使用路径
    	'/'默认值,适用于当前网站的所有URL路径,都能读取到cookie值
        ' '空值,只能在当前页面访问此数据
        '/index/'指定路径使用,比如设置了index路径只能在index路径开头的路径下使用
    #domain默认指此域名下的所有路径都可以使用的cookie值
    #httponly:设置此cookie值只用于数据传输,不可以修改,默认可以修改


js操作cookie

document.cookie = 'k1=lijie;path=/';前端页面修改cookie值
$.cookie('k1','lijie',{path:'/'});使用jQuery修改cookie值
注意:path设置的路径不同,导致结果也不同


cookie登陆注册代码示例

from django.shortcuts import render,redirect
from app01 import models
def login(request):
    """
    用户登录
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'login.html')
    # 获取用户提交的用户名和密码
    user = request.POST.get('user')
    pwd = request.POST.get('pwd')

    # 去数据库检查用户名密码是否正确
    # user_object = models.UserInfo.objects.filter(username=user, password=pwd).exists()
    user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
    if user_object:
        # 用户登录成功
        result = redirect('/index/')
        result.set_cookie('xxxxxxxx',user)
        return result
    # 用户名或密码输入错误
    return render(request,'login.html',{'error':'用户名或密码错误'})
def index(request):
    """
    博客后台首页
    :param request:
    :return:
    """
    user = request.COOKIES.get('xxxxxxxx')
    if not user:
        return redirect('/login/')
    return render(request,'index.html',{'user':user})


session

依赖cookie实现,是一种存储数据的方式,依赖于cookie,
实现本质:
    用户向服务端发送请求,服务端做两件事:生成随机字符
    串;为此用户开辟一个独立的空间来存放当前用户独有的值.
    在空间中如何想要设置值:
    request.session['x1'] = 123
    request.session['x2'] = 456
    在空间中取值:
    request.session['x2'](不推荐)
    request.session.get('x2')
    视图函数中的业务操作处理完毕,给用户响应,在响应时会
    将随机字符串存储到用户浏览器的cookie中.
'''session中的数据是根据用户相互隔离'''


session操作

request.session.get('x2')#获取session值
request.session['x'] = 123#设置session值
del request.session['x']#删除session值
request.session.keys()#获取所有的session的键
request.session.values()#获取所有的session的值
request.session.items()#获取session所有的键值
request.session.set_expiry(value)#修改session的过期时间
	* 如果value是个整数,session会在些秒数后失效。
	* 如果value是个datatime或timedelta,session就会在这个时间后失效。
	* 如果value是0,用户关闭浏览器session就会失效。
	* 如果value是None,session会依赖全局session失效策略(配置文件中的失效时间)。
request.session.session_key#获取sessionid,就是session生成的随机字符串


Django和session的相关配置

SESSION_COOKIE_NAME = "sessionid" #Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_DOMAIN = None #api.baidu.com / 就是这个域名下的所有路径都可使用此session
SESSION_COOKIE_PATH = "/" # Session的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,默认修改之后才保存


session默认存储的位置

#在Djagno中默认存储在数据库中
#存储其他地方的设置信息
'存储于文件中':
	SESSION_ENGINE ='django.contrib.sessions.backends.file'
	SESSION_FILE_PATH = '/sssss/'
'存储于缓存(内存中)'
	SESSION_ENGINE ='django.contrib.sessions.backends.cache'
	SESSION_CACHE_ALIAS = 'default'
CACHES = {
	'default': {
		'BACKEND':'django.core.cache.backends.locmem.LocMemCache',
		'LOCATION': 'unique-snowflake',
			}
	}
'存储于缓存数据库redis中'
	SESSION_ENGINE ='django.contrib.sessions.backends.cache'
	SESSION_CACHE_ALIAS = 'default'
	CACHES = {
		"default": {
            "BACKEND":"django_redis.cache.RedisCache",
            "LOCATION":"redis://127.0.0.1:6379",
			"OPTIONS": {
                "CLIENT_CLASS":"django_redis.client.DefaultClient",
                "CONNECTION_POOL_KWARGS":{"max_connections": 100},
                "PASSWORD": "密码",
}
}
}


session登陆注册示例代码

from django.shortcuts import render,redirect
from app01 import models
def login(request):
    """
    用户登录
    :param request:
    :return:
    """
    if request.method == 'GET':
        return render(request, 'login.html')
    # 获取用户提交的用户名和密码
    user = request.POST.get('user')
    pwd = request.POST.get('pwd')
    # 去数据库检查用户名密码是否正确
    # user_object = models.UserInfo.objects.filter(username=user,password=pwd).first()
    # user_object = models.UserInfo.objects.filter(username=user, password=pwd).exists()
    user_object = models.UserInfo.objects.filter(username=user, password=pwd).first()
    if user_object:
        # 用户登录成功
        request.session['user_name'] = user_object.username
        request.session['user_id'] = user_object.pk
        return redirect('/index/')
    # 用户名或密码输入错误
    return render(request,'login.html',{'error':'用户名或密码错误'})
def auth(func):
    def inner(request,*args,**kwargs):
        name = request.session.get('user_name')
        if not name:
            return redirect('/login/')
        return func(request,*args,**kwargs)
    return inner
@auth
def index(request):
    """
    博客后台首页
    :param request:
    :return:
    """
    return render(request,'index.html')


Ajax

简介

1:AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML,现在更多使用json数据)
2:AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
3:AJAX除了异步的特点外,还有一个就是:浏览器页面局部刷新;(这一特点给用户的感受是在不知不觉中完成请求和响应过程
a.同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
b.异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求


示例

'''前端代码'''
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    用户名:<input type="text" id="user">
    密码: <input type="password" id="password">
    <button id="sub">提交</button>
    <span id="span"></span>
</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    $('#sub').click(function () {
        var user = $('#user').val()
        var pwd = $('#password').val()
        $.ajax({
            	#请求提交的路径
                url: '{% url 'login' %}',
            	#请求方式
                type: 'post',
            	#发送的数据,键值形式的,可以不给键加引号
                data: {user: user, pwd: pwd},
            	#发送请求,并把结果返回给res
                success: function (res) {
                    if (res === '0') {
                        $('#span').text('用户名或密码错误');
                    } else {
                        location.href = '/home/';
                    }
                }
            }
        )
    })
</script>
</html>
#***********************************
'''后台代码'''
def login(request):
    if request.method=='GET':
        return render(request,'login.html')
    else:
        ret = request.POST.dict()
        print(ret)
        if ret['user']=='lijie':
            return HttpResponse('1')
        else:
            return HttpResponse('0')

def home(request):
    return render(request,'home.html')


csrftoken跨站请求伪造

简介

详述CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。攻击者通过HTTP请求江数据传送到服务器,从而盗取回话的cookie。盗取回话cookie之后,攻击者不仅可以获取用户的信息,还可以修改该cookie关联的账户信息。


1570709672562

from表单通过csrf认证

<form action="" method="post">
	# form表单里面加上这个标签,模板渲染之后就是一个input标签,
	#渲染的input标签:  type=hidden  name=csrfmiddlewaretoken  value='asdfasdfasdf'
    {% csrf_token %}  
    用户名: <input type="text" name="username">
    密码: <input type="password" name="password">
    <input type="submit">
</form>


csrf验证流程

请求页面时,csrf会返回一个token,就是渲染的input标签,在页面的cookie中也有一个token值,页面通过post请求发送数据时,会把两个token值都提交到后台解析,如果解析的结果正确,就认证成功,两个token解析的结果一致,就说明请求来自本网站的页面


ajax通过csrf认证

#方式一:
	<script>
    $('#sub').click(function () {
        var username = $('#username').val()
        var password = $('#password').val()
        #获取input的token值
        var csrf = $("[name = 'csrfmiddlewaretoken']").val()
        $.ajax({
                url:{% url 'login' %},
                type: 'post',
            	#将token值写入data发送到后台,cookie中的token请求时会自动携带
                data: {username: username, password: password,csrfmiddlewaretoken:csrf},
                success: function (res) {
                    if (res === '1') {
                        location.href='{% url "home" %}';
                    }else {
                        $('#span').text('用户名或密码错误');
                        }
                    }
                }
            )
        })
    </script>
#方式二:
    <script>
        $('#sub').click(function () {
            var username = $('#username').val()
            var password = $('#password').val()
            #模版加载渲染时自动获取到token
            var csrf = '{{ csrf_token }}'
            $.ajax({
                    url:{% url 'login' %},
                    type: 'post',
                    data: {username: username, password: password,csrfmiddlewaretoken:csrf},
                    success: function (res) {
                        if (res === '1') {
                            location.href='{% url "home" %}';
                        }else {
                            $('#span').text('用户名或密码错误');
                        }
                    }
                }
            )
        })
    </script>
#方式三:
	<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    #必须导入操作cookie的包
    <script src="https://cdn.bootcss.com/js-cookie/2.2.1/js.cookie.min.js"></script>
    <script>
        $('#sub').click(function () {
            var username = $('#username').val()
            var password = $('#password').val()
            $.ajax({
                    #放在head头中,获取token值,
                    #在ajax里面还有一个参数是headers,自定制请求头,可以将csrf_token加在这里,我们发contenttype类型数据的时候,csrf_token就可以这样加
                    headers:{"X-CSRFToken":$.cookie('csrftoken')},
                    url:{% url 'login' %},
                    type: 'post',
                    data: {username: username, password: password},
                    success: function (res) {
                        if (res === '1') {
                            location.href = '{% url "home" %}';
                        } else {
                            $('#span').text('用户名或密码错误');
                        }
                    }
                }
            )
        })
    </script>


from表单上传文件

'''前段代码'''
<form action="" method="post" enctype="multipart/form-data">#form_data的格式一般是把大数据一段一段隔开的
      用户名 <input type="text" name="user">
      头像 <input type="file" name="avatar">#如果不用form_data格式来发,那么默认的是urlencoded的格式,这个标签的数据会组成avatar:文件名字来进行发送,就无法获取发送的文件
    <input type="submit">
</form>
'''后台代码'''
views.py
#一个django的默认配置文件,可以查看好多的配置源码
from django.conf import settings
def upload(request):
    if request.method == 'GET':
        print(settings.BASE_DIR) #/static/
        return render(request,'upload.html')
    else:
        print(request.POST)
        print(request.FILES)
        uname = request.POST.get('username')
        pwd = request.POST.get('password')
		#获取文件句柄,一个文件对像
        file_obj = request.FILES.get('file')
        print(file_obj.name) #获取的是上传的文件的文件名
        with open(file_obj.name,'wb') as f:
            #file_obj.chunks()每次接受65536个字节,文件大时,就多次接受,推荐
            for chunk in file_obj.chunks():
                f.write(chunk)
        return HttpResponse('ok')


Ajax上传文件

 $('#sub').click(function () { 
     	#js只能通过fromdata对象的像是上传文件,先声明一个fromdata对象
        var formdata = new FormData();    
        var uname = $('#username').val();
        var pwd = $('#password').val();
     	#获取上传的文件对象,
        var file_obj = $('[type=file]')[0].files[0]; // js获取文件对象
        formdata.append('username',uname);
        formdata.append('password',pwd);
        formdata.append('file',file_obj);
        $.ajax({
            url:'{% url "upload" %}',
            type:'post',
            data:formdata, 
            #ajax传输数据是会对数据进行处理,下面两个参数就是告诉ajax不要处理数据,必须写
            processData:false,  // 必须写----------
            contentType:false,  // 必须写----------
            headers:{
                "X-CSRFToken":$.cookie('csrftoken'),
            },
            success:function (res) {
                console.log(res);
                if (res === '1'){
                    // $('.error').text('登录成功');
                    location.href = '/home/'; // http://127.0.0.1:8000/home/
                }else{
                    $('.error').text('用户名密码错误!');
                }
            }
        })
    })


1570713394523

json数据交互

后台代码

from django.http import JsonResponse
        username = request.POST.get('username')
        pwd = request.POST.get('password')
        ret_data = {'status':None,'msg':None}
        print('>>>>>',request.POST)
        #<QueryDict: {'{"username":"123","password":"123"}': ['']}>
        if username == 'chao' and pwd == '123':
            ret_data['status'] = 1000  # 状态码
            ret_data['msg'] = '登录成功'
        else:
            ret_data['status'] = 1001  # 状态码
            ret_data['msg'] = '登录失败'
		#方式一:
        #将数据转成json格式传回前端,是一个json格式的字符串,Ajax使用JSON.parse(res)对数据进行解析,转换
        ret_data_json = json.dumps(ret_data,ensure_ascii=False)
        return HttpResponse(ret_data_json)
    	#方式二:
        #content_type='application/json'告诉ajex请求返回的就是json数据,ajax会自动解析
        return HttpResponse(ret_data_json,content_type='application/json')
        #方式三:
        #返回的就是jaon的数据,告诉ajex请求返回的就是json数据,ajax会自动解析
        #JsonResponse默认只能使用字典格式的数据,加上safe=False,就可以使用非字典类型的数据传送
        return JsonResponse(ret_data,safe=False)


前端代码

#对应上面的方式一: 
$.ajax({
            url:'{% url "jsontest" %}',
            type:'post',
            // data:{username:uname,password:pwd,csrfmiddlewaretoken:csrf},
            //data:JSON.stringify({username:uname,password:pwd}),
            data:{username:uname,password:pwd},
            headers:{
                // contentType:'application/json',发送的数据就变成了json数据,django无法解析
                "X-CSRFToken":$.cookie('csrftoken'),
            },
            success:function (res) {
                var res = JSON.parse(res);  //-- json.loads()
                console.log(res,typeof res);  //直接就是反序列化之后的了
                //JSON.stringify()  --相当于 json.dumps
                if (res.status === 1000){
                    // $('.error').text('登录成功');
                    location.href = '/home/'; // http://127.0.0.1:8000/home/
                }else{
                    $('.error').text(res.msg);
                }
            }
        })


SweetAlert插件

<script>
    $(".btn-danger").on("click", function () {
  swal({
    title: "你确定要删除吗?",
    text: "删除可就找不回来了哦!",
    type: "warning",
    showCancelButton: true,
    confirmButtonClass: "btn-danger",
    confirmButtonText: "删除",
    cancelButtonText: "取消",
    closeOnConfirm: false
    },
    function () {
      var deleteId = $(this).parent().parent().attr("data_id");
      $.ajax({
        url: "/delete_book/",
        type: "post",
        data: {"id": deleteId},
        success: function (data) {
          if (data.status === 1) {
            swal("删除成功!", "你可以准备跑路了!", "success");
          } else {
            swal("删除失败", "你可以再尝试一下!", "error")
          }
        }
      })
    });
})
</script>
#实例:
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script src="{% static '/bootstrap-3.3.7-dist/js/bootstrap.min.js' %}"></script>
<script src="{% static '/sw/sweetalert.min.js' %}"></script>
<script>
    $("[type=submit]").on("click", function () {
        var ths = $(this);
        swal({
                title: "你确定要删除吗?",
                text: "删除可就找不回来了哦!",
                type: "warning",
                showCancelButton: true,
                confirmButtonClass: "btn-danger",
                confirmButtonText: "删除",
                cancelButtonText: "取消",
                closeOnConfirm: false
            },
            function () {
                var book_id = ths.attr('book_id');
                $.ajax({
                    url: "{% url 'delbooks' %}",
                    type: "get",
                    data: {"book_id": book_id, 'csrfmiddlewaretoken': "{{ csrf_token }}"},
                    success: function (data) {
                        if (data.status === '1') {
                            swal("删除成功!", "你可以准备跑路了!", "success");
                            ths.parent().parent().remove();
                        } else {
                            swal("删除失败", "你可以再尝试一下!", "error")
                        }
                    }
                })
            });
    })
</script>
</html>


1570781155871

ORM操作数据库事物和锁

ORM添加行级锁

#orm添加行级锁:
	mysql:  select * from book where id=1 for update;
    #由于mysql在查询时自动加的是共享锁,所以我们可以手动加上互斥锁。create、update、delete操作时,mysql自动加行级互斥锁
	#select_for_update()就相当于原生sql的 for update 添加行级锁
	models.Book.objects.select_for_update().filter(id=1)


ORM开启事物的三种方式

#第一种:全局开启事物的方式
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'mxshop',
            'HOST': '127.0.0.1',
            'PORT': '3306',
            'USER': 'root',
            'PASSWORD': '123',
            'OPTIONS': {
                "init_command": "SET default_storage_engine='INNODB'",
           #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置开启严格sql模式
            }
            #基于HTTP请求开启事物,请求的视图函数中,所有的ORM语句绑定成一个事物
            "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程
            "AUTOCOMMIT":False, #全局取消自动提交,慎用
        },
      'other':{
        'ENGINE': 'django.db.backends.mysql', 
      } #还可以配置其他数据库
#第二种:局部使用事物
    #导入transaction模块
	from django.db import transaction
    #给视图函数添加transaction.atomic装饰器吧这个视图函数中的ORM操作绑定成一个事物
   	#事物开启后,代码逻辑运行成功后自动提交数据,如果执行失败,自动roolback回滚
    @transaction.atomic
    def home(request):
        #查询每个作者的姓名以及出版的书的最高价格
        ret = models.Author.objects.annotate(a=Max('book__price')).values('a','name')
        print(ret) 
        return HttpResponse('你好')
#第三种:使用上下文管理期来开启事物
	from django.db import transaction
	def home(request):
        #使用with语句,with缩进体的所有的ORM语句会绑定成一个事物,不影响with体外的orm语句
        #事物开启后,代码逻辑运行成功后自动提交数据,如果执行失败,自动roolback回滚
        with transaction.atomic():
        	ret = models.Author.objects.annotate(a=Max('book__price')).values('a','name')
        print(ret)
        return HttpResponse('你好')


中间件

简介

中间件,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。
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.middlewares.Middleware'#自己定义的中间件
]
#MIDDLEWARE配置文件就是所有的中间件,我们可以把我们自己定义的一些中间件,加到这个配置文件中,
#中间件是帮助我们在视图函数执行之前和执行之后都可以做一些额外的操作,它本质上就是一个自定义类,类中定义了几个方法,Django框架会在请求的特定的时间去执行这些方法。我们自己定义的中间件要放到自带的中间件的下面


中间件的定义形式

#定义中间件要导入的包
from django.utils.deprecation import MiddlewareMixin
#定义中间件的类,类必须继承MiddlewareMixin类
class MD1(MiddlewareMixin):
	#在类中定义中间件方法
    def process_request(self, request):
        print("MD1里面的 process_request")


中间件可以定义的5种方法

中间件可以定义五个方法,分别是:(主要的是process_request和process_response)
#在请求过来,路由分发之前,先执行process_request(self,request),可以用来校验session,判断登录状态
#process_request(self,request)中间件方法执行完成后,会继续往下执行(不能设置return值).如果中间件执行未通过,可以设置return值,那么django会认为你的请求处理完成,然后执行process_response把你的返回值返回给前端.就不会走路由分发,可以用来处理登陆逻辑,参数中的request参数是wsgi接受到请求之后,按照http协议解析,把你的数据封装到request中
process_request(self,request)
#process_view在路由之后,视图之前执行,view_func参数就是要执行的视图函数
process_view(self, request, view_func, view_args, view_kwargs)
process_template_response(self,request,response)了解
#process_exception是视图函数执行出错之后执行的方法
process_exception(self, request, exception)
#process_response方法是请求处理完成之后,调用response对象,把返回值,返回给wsgi,按照http协议封装然后返回前端
process_response(self, request, response)


添加白名单

from django.shortcuts import render,redirect

# Create your views here.
from django.utils.deprecation import MiddlewareMixin
class process(MiddlewareMixin):
    #设置白名单
    white_list=['/login/',]
    #添加process_request获取session判断登陆状态,登陆就继续执行,没有就直接返回login页面
    def process_request(self,request):
        path=request.path
        if path not in self.white_list:
            status=request.session.get('is_login')
            if not status:
                return redirect('/login/')


多个中间件的执行顺序

1570796446021

Django的请求生命周期

1570800602253

from组件

简介

1:我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
2:与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
#3:总结一下,其实form组件的主要功能如下:
    生成页面可用的HTML标签
    对用户提交的数据进行校验
    保留上次输入内容
 
__all__ = (
    'Media', 'MediaDefiningClass', 'Widget', 'TextInput', 'NumberInput',
    'EmailInput', 'URLInput', 'PasswordInput', 'HiddenInput',
    'MultipleHiddenInput', 'FileInput', 'ClearableFileInput', 'Textarea',
    'DateInput', 'DateTimeInput', 'TimeInput', 'CheckboxInput', 'Select',
    'NullBooleanSelect', 'SelectMultiple', 'RadioSelect',
    'CheckboxSelectMultiple', 'MultiWidget', 'SplitDateTimeWidget',
    'SplitHiddenDateTimeWidget', 'SelectDateWidget',
)


form组件简单使用

from django.shortcuts import render
# Create your views here.
from django import forms

#form组件就是在后端写一个form组件,然后传到实例化一个对象,传到前端,获取对象属性,产生input标签
class UserInfo(forms.Form):
    #文本形式的输入框,CharField默认的输入框的类型就是type=text的输入框,label制定的是输入框的标签提示
    #form字段的名称定义的是什么,那么前端生成的input标签的name属性就是什么,比如username,前端属性就是username
    #initial设置默认值,required=True默认有一个校验,就是不能为空,还可以设置最大几位,最小几位
    username = forms.CharField(label='用户名:',widget=forms.TextInput)
    
    #输入框的一般都选择CharField,通过widgets组件设置input框的type属性,PasswordInput生成的就是密码输入框
    password = forms.CharField(label='密码:',widget=forms.widgets.PasswordInput)
    
    #选择框使用的是ChoiceField的类型,生成的是一个select下拉选择框,widget组件选择,RadioSelect生成的就是inpu框的单选框
    #choices设置的就是选择框的值,只是男,那应的value值就是1,一般用元祖套元祖的形式
    sex = forms.ChoiceField(choices=((1,'男'),(2,'女')),widget=forms.RadioSelect)
    
    #SelectMultiple是这是多选下拉狂,CheckboxSelectMultiple设置input框的多选形式
    hobby=forms.MultipleChoiceField(choices=((1,'喝酒'),(2,'抽烟'),(3,'烫头')),widget=forms.CheckboxSelectMultiple)
    
    #设置单选框,常见的就是页面登陆时的 记住我
    remember_me=forms.ChoiceField(label='记住我',widget=forms.CheckboxInput)
    
    #生成data input框
    data = forms.DateField(label='出生日期',widget=forms.DateInput(attrs={'type':'data'}))

def index(request):
    #实例化对象,然后吧对象传到前端
    user_obj=UserInfo()
    return render(request, 'index.html',{'user_obj':user_obj})

#前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
#novalidate是告诉浏览器,不用进行校验
<form action="" method="post" novalidate>
	#通过获取属性的方式来设置input框
    <div>
        {{ user_obj.username.label }} {{ user_obj.username }}
    </div>
    <div>
        {{ user_obj.password.label }} {{ user_obj.password }}
    </div>
    <input type="submit">
</form>
</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</html>


简单校验

from django.shortcuts import render
# Create your views here.
from django import forms

class UserInfo(forms.Form):
    username = forms.CharField(label='用户名:',
                               widget=forms.TextInput,
                               initial='lijie',
                               min_length=6,
                               required=True,
                               #error_messages设置报错详细,正对不同的报错类型,设置不同的报错信息
                               error_messages={'required':'不能为空','min_length':'用户名不能小于6位'}
                               )
    # 输入框的一般都选择CharField,通过widgets组件设置input框的type属性,PasswordInput生成的就是密码输入框
    password = forms.CharField(label='密码:', widget=forms.widgets.PasswordInput(render_value=True))


def index(request):
    if request.method == 'GET':
        # 实例化对象,然后吧对象传到前端
        user_obj = UserInfo()
        return render(request, 'index.html', {'user_obj': user_obj})
    else:
        # 同样使用Userinfo实例化的方式,获取前端提交的数据,request.POST会把获取到数据,传给实例的对象
        user_obj = UserInfo(request.POST)
        # is_valid是校验用户数据,
        if user_obj.is_valid():
            # 用户数据校验成功之后,吧数据放到cleaned_data里面
            print(user_obj.cleaned_data)
        else:
            # 用户数据校验失败,把错误信息放到errors属性里面,
            # 错误信息是ul标签,返回前端页面
            print(user_obj.errors)
            return render(request, 'index.html', {'user_obj': user_obj})
        
#前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登陆</title>
</head>
<body>
#获取所有的错误信息,以ul列表的形式展示出来,
{{ user_obj.errors }}
<form action="" method="post" novalidate>
    <div>
    	#user_obj.username.errors.0获取属于自己的报错信息
        {{ user_obj.username.label }} {{ user_obj.username }} {{ user_obj.username.errors.0 }}
    </div>
    <div>
        {{ user_obj.password.label }} {{ user_obj.password }} {{ user_obj.password.errors.0 }}
    </div>
    <input type="submit">
</form>
</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
</html>


正则校验和检验函数

from django.shortcuts import render
# Create your views here.
from django import forms
#导入正则校验器
from django.core.validators import RegexValidator


class UserInfo(forms.Form):
    username = forms.CharField(label='用户名:',
                               widget=forms.TextInput,
                               initial='lijie',
                               min_length=6,
                               required=True,
                               error_messages={'required':'不能为空','min_length':'用户名不能小于6位'},
                               #使用正则校验,设置报错信息
                               validators=[RegexValidator(r'^[0-9]+$', '请输入数字'),
                                           RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
                               )
    password = forms.CharField(label='密码:', widget=forms.widgets.PasswordInput)
    
def index(request):
    if request.method == 'GET':
        user_obj = UserInfo()
        return render(request, 'index.html', {'user_obj': user_obj})
    else:
        user_obj = UserInfo(request.POST)
        if user_obj.is_valid():
            print(user_obj.cleaned_data)
        else:
            print(user_obj.errors)
------------------------------------- 
#自定义验证函数
import re
from django.shortcuts import render
# Create your views here.
from django import forms
#导入正则校验器
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError


# 自定义验证规则
def mobile_validate(value):
    #设置一个正则校验对象
    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
    #通过math方法校验返回的数据
    if not mobile_re.match(value):
        raise ValidationError('手机号码格式错误')  #自定义验证规则的时候,如果不符合你的规则,需要自己发起错误

class UserInfo(forms.Form):
    username = forms.CharField(label='用户名:',
                               widget=forms.TextInput,
                               initial='lijie',
                               min_length=6,
                               required=True,
                               error_messages={'required':'不能为空','min_length':'用户名不能小于6位'},
                               #使用正则校验,设置报错信息,自定义验证时,使用函数名校验
                               validators=[mobile_validate,],
                               )


form内置字段

#Field
    required=True,               是否允许为空
    widget=None,                 HTML插件
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=None            Label内容后缀
 
 
#CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白
 
#IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值
 
#FloatField(IntegerField)
    ...
 
#DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度
 
#BaseTemporalField(Field)
    input_formats=None          时间格式化   
 
#DateField(BaseTemporalField)    格式:2015-09-01
#TimeField(BaseTemporalField)    格式:11:12
#DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
 
#DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...
 
#RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}
 
#EmailField(CharField)      
    ...
 
#FileField(Field)
    allow_empty_file=False     是否允许空文件
 
#ImageField(FileField)      
    ...
    注:需要PIL模块,pip3 install Pillow
    以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)
 
#URLField(Field)
    ...
 
#BooleanField(Field)  
    ...
 
#NullBooleanField(BooleanField)
    ...
 
#ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示
 
 
#ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选
     
#ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField
 
#TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值
 
#MultipleChoiceField(ChoiceField)
    ...
 
#TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值
 
#ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), #fields.EmailField(),])
 
#MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
 
#SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
 
#FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''
 
#GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
 
#SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...
 
#UUIDField(CharField)           uuid类型
复制代码


局部钩子

我们在Fom类中定义 clean_字段名() 方法,就能够实现对特定字段进行校验。
class LoginForm(forms.Form):
    username = forms.CharField(
        min_length=8,
        label="用户名",
        initial="张三",
        error_messages={
            "required": "不能为空",
            "invalid": "格式错误",
            "min_length": "用户名最短8位"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )
    password = forms.CharField(
        label='密码:',
        widget=forms.widgets.PasswordInput(attrs={'class':'c1'} ,render_value=True),
    )
    ...
    # 定义局部钩子,用来校验username字段,之前的校验正则还在,给你提供了一个添加一些校验功能的钩子
    def clean_username(self):
        value = self.cleaned_data.get("username")
        if "666" in value:
            #如果出错就会把错误返还给对象,前端就可以通过属性,获取错误值(diango提供的报错信息用'ValidationError')
            raise ValidationError("光喊666是不行的")
        else:
            #校验通过也必须把值返回,否则cleaned_data就会失去这个值
            return value
大致流程:
字段内部属性相关校验------局部钩子校验-----然后循环下一个字段进行上面两部校验----最后执行全局钩子
有局部钩子和全局钩子的执行顺序是:先把类中的username属性全部加载完,然后加载相关定义的clean_username方法,然后再加载password的属性,最后全部完事再加载全局钩子


全局钩子

class LoginForm(forms.Form):
    ...
    password = forms.CharField(
        min_length=6,
        label="密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    re_password = forms.CharField(
        min_length=6,
        label="确认密码",
        widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}, render_value=True)
    )
    ...
    # 定义全局的钩子,用来校验密码和确认密码字段是否相同,执行全局钩子的时候,cleaned_data里面肯定是有了通过前面验证的所有数据
    def clean(self):
        password_value = self.cleaned_data.get('password')可以找到所有字段的内容,这个是指定密码字段的值
        re_password_value = self.cleaned_data.get('re_password')
        if password_value == re_password_value:
            return self.cleaned_data #校验成功了把整个字段的数据全部返回,全局钩子要返回所有的数据
        else:
            self.add_error('re_password', '两次密码不一致') #在re_password这个字段的错误列表中加上一个错误,并且clean_data里面会自动清除这个re_password的值,所以打印clean_data的时候会看不到它
            raise ValidationError('两次密码不一致')


modelform

class Meta常用参数

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息
error_messages = {
    'title':{'required':'不能为空',...} #每个字段的所有的错误都可以写,...是省略的意思,复制黏贴我代码的时候别忘了删了...
}


批量添加样式

class BookForm(forms.ModelForm):
    #password = forms.CharField(min_length=10) #可以重写字段,会覆盖modelform中的这个字段,那么modelform下面关于这个字段的设置就会被覆盖,比如果设置插件啊,error_messages啊等等,
    r_password = forms.CharField() #想多验证一些字段可以单独拿出来写,按照form的写法,写在Meta的上面或者下面都可以
    class Meta:
        model = models.Book
        # fields = ['title','price']  ---这两个字段翻译成对应的字段,只校验这两个字段
        fields = "__all__" #['title,'price'] 指定字段生成form
        # exclude=['title',] #排除字段
        labels = { -----给前端显示的字段起的名字
            "title": "书名",
            "price": "价格"
        }
        error_messages = {
            'title':{'required':'不能为空',} #每个字段的错误都可以写
        }
    #如果models中的字段和咱们需要验证的字段对不齐的是,比如注册时,咱们需要验证密码和确认密码两个字段数据,但是后端数据库就保存一个数据就行,那么验证是两个,数据保存是一个,就可以再接着写form字段
    r_password = forms.CharField()。
    #同样的,如果想做一些特殊的验证定制,那么和form一昂,也是那两个钩子(全局和局部),写法也是form那个的写法,直接在咱们的类里面写:
    #局部钩子:
    def clean_title(self):
        pass
  #全局钩子
    def clean(self):
        pass
    def __init__(self,*args,**kwargs): #批量操作
        super().__init__(*args,**kwargs)
        for field in self.fields:
            #field.error_messages = {'required':'不能为空'} #批量添加错误信息,这是都一样的错误,不一样的还是要单独写。
            self.fields[field].widget.attrs.update({'class':'form-control'})


modelform前端

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="{% static 'bootstrap-3.3.0-dist/dist/css/bootstrap.min.css' %}">
</head>
<body>
<h1>编辑页面</h1>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <form action="{% url 'edit_book' n %}" novalidate method="post">
                {% csrf_token %}
                {% for field in form %}
                    <div class="form-group">
                        <label for="{{ field.id_for_label }}">{{ field.label }}</label>
                        {{ field }}
                        <span class="text-danger">{{ field.errors.0 }}</span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <input type="submit" class="btn btn-primary pull-right">
                </div>
            </form>
        </div>
    </div>
</div>
</body>
<script src="{% static 'bootstrap-3.3.0-dist/dist/jQuery/jquery-3.1.1.js' %}"></script>
<script src="{% static 'bootstrap-3.3.0-dist/dist/js/bootstrap.min.js' %}"></script>
</html>


modelform VIew页面

def edit_book(request,n):
    book_obj = models.Book.objects.filter(pk=n).first()
    if request.method == 'GET':
        # all_authors = models.Author.objects.all() #
        # all_publish = models.Publish.objects.all()
        form = BookForm(instance=book_obj)
        return render(request,'edit_book.html',{'form':form,'n':n}) #传递的这个n参数是给form表单提交数据的是的action的url用的,因为它需要一个参数来识别是更新的哪条记录
    else:
        form = BookForm(request.POST,instance=book_obj) #必须指定instance,不然我们调用save方法的是又变成了添加操作
        if form.is_valid():
            form.save()
            return redirect('show')
        else:
            return render(request,'edit_book.html',{'form':form,'n':n})


form字段组件字段表(常用)

strip=True 是否移除用户输入空白
label_suffix=None Label内容后缀
disabled=False 是否可以编辑
字段值 字段含义
localize=False, 是否支持本地化
validators=[], 自定义验证规则
error_messages=None, 错误信息
required 不能为空
invalid 必须是邮箱格式
max_length 最大长度
min_length 最小长度
placeholder 输入框提示语
initial 默认值
label input框的文本提示(用户名)
widget HTML插件
help_text='', 帮助信息(在标签旁边显示)

modelform与models对应字段

models字段 moidelform
blank=True required=False 可以为空
verbose_name='QQ昵称' label='QQ昵称' label转换(中文描述)
choices=sex_type(元祖形式) forms.Choicefield(choices=((),())) 选择转换
self 自关联
MultiSelectField 多选字段(三方包)

modelformset_factory

#modelformset_factory组件返回的数据可以在前端页面直接批量修改,最后一起提交
from django.forms.models import modelformset_factory

class StudyRecord(View):
    #get请求
    def get(self,request,studyrecord_id):
        #实例化一个modelformset_factory对象,model指定数据表,form指定modelform组件,extra指定空白的数据行默认一行
        all_studyrecord = modelformset_factory(model=models.StudyRecord,form=myforms.StudyRecordModelForm,extra=0)
        #使用queryset对象来过滤数据,
        all_studyrecord = all_studyrecord(queryset=models.StudyRecord.objects.filter(course_record_id=studyrecord_id))
        return render(request,'courserecord/studyrecord.html',{'all_studyrecord':all_studyrecord})
  
    #post请求
    def post(self,request,studyrecord_id):
        all_studyrecord = modelformset_factory(model=models.StudyRecord,form=myforms.StudyRecordModelForm,extra=0)
        all_studyrecord = all_studyrecord(request.POST)
        if all_studyrecord.is_valid():
            all_studyrecord.save()
            return redirect(request.path)
        else:
            return render(request,'courserecord/studyrecord.html',{'all_studyrecord':all_studyrecord})
'''======================================================='''
#前端页面注意样式
#用于modelformset_factory提交数据的标识,固定格式
{{ all_studyrecord.management_form }}
#在生成标签时使用id来区分每一条数据,用于提交数据时的每一条数据标识
{% for studyrecord in all_studyrecord %}
    <tr>
        '''{{ studyrecord.id }}'''
        <td>{{ forloop.counter }}</td>
        <td>{{ studyrecord.attendance }}</td>
        <td>{{ studyrecord.score }}</td>
        <td>{{ studyrecord.homework_note }}</td>
        #instance用来设置数据默认选中,不可编辑,
        <td>{{ studyrecord.instance.course_record }}</td>
        #modelformset_factory在提交数据时,要将所有的字段数据提交,需要设置一个隐藏标签
        <td class="hidden">{{ studyrecord.course_record }}</td>
        <td>{{ studyrecord.instance.student }}</td>
        <td class="hidden"> {{ studyrecord.student }}</td>
    </tr>
{% endfor %}


跨域和同源请求**

同源机制

同源机制:域名、协议、端口号相同的同源机制
    (非同源就是跨域 )
    同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指,域名,协议,端口相同。当一个浏览器的两个tab页中分别打开来 百度和谷歌的页面当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。
#支持跨域,简单请求
	服务器设置响应头:Access-Control-Allow-Origin = '域名' 或 '*'
#支持跨域,复杂请求
	由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
	“预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
	“预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers


简单请求

#浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
1:请求方法是以下三种方法之一:(也就是说如果你的请求方法是什么put、delete等肯定是非简单请求)
HEAD
GET 
POST
2:HTTP的头信息不超出以下几种字段:(如果比这些请求头多,那么一定是非简单请求)
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain,也就是说,如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。


简单请求和非简单请求的区别

简单请求:一次请求
非简单请求:两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输。
* 关于“预检”

- 请求方式:OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
     => 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
        Access-Control-Request-Method
     => 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
        Access-Control-Request-Headers


非简单请求的实例

from django.shortcuts import render
from django.http import JsonResponse
# Create your views here.
def books(request):

    # return JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)

    obj = JsonResponse(['西游记2','三国演义2','水浒传2'],safe=False)
    # obj["Access-Control-Allow-Origin"] = "*"
    obj["Access-Control-Allow-Origin"] = "http://127.0.0.1:8000"
    print(request.method)
    #处理预检的options请求,这个预检的响应,我们需要在响应头里面加上下面的内容
    if request.method == 'OPTIONS':
        # obj['Access-Control-Allow-Headers'] = "Content-Type" #"Content-Type",首字母小写也行
        # obj['Access-Control-Allow-Headers'] = "content-type" #"Content-Type",首字母小写也行。这个content-type的意思是,什么样的请求体类型数据都可以,我们前面说了content-type等于application/json时,是复杂请求,复杂请求先进行预检,预检的响应中我们加上这个,就是告诉浏览器,不要拦截
        obj['Access-Control-Allow-Headers'] = "content-type,b"  #发送来的请求里面的请求头里面的内容可以定义多个,后端需要将头配置上才能访问
    return obj



GIT

git 
版本控制
git init 初始化仓库
git status  查看状态
git add 文件夹/. 将变化的内容添加到暂存区
git commit -m '描述信息'  提交到版本库
git log 查看版本记录
git reflog 查看变更记录
git reset --hard 版本号  版本回退。




分支

默认  master
git branch 产看所有的分支
git branch 分支,新建分支
git checkout 分支,切换分支
git merge new 分支,把new分支合并到当前分支。
合并中可能出现冲突  需要手动解决冲突


个人开发(一台机器上)

master dev dubug(三个分支,在develop上开发,dubug改)
dev的分支上开发功能
合并到master分支上(master分支放上可以上线的代码)
master分支的代码有bug
	1.新建一个debug分支
	2.切换到debug分支修改bug,提交版本
	3.切换回master分支,合并debug分支
dev发开,开发到一半:
	1.git stash 隐藏当前的操作
	2.修复完线上的bug合并debug分支,删除debug分支
	3.git stash pop 拿会之前的操作,继续开发


公司上班回家也可以写:使用远程仓库
码云,GitHead,gitlap(公司自己开发的)
个人公司 家
公司下班:
git add .
git commit -m '未完成'
git push orgin dev
到家接着开发:洗个澡,。(已经配好了)
下载安装GIT配置
拿克隆网址。
git clone'克隆网址'
git branch dev
git checkut dev
git pull origin dev
接着开发功能
开发完成
git add .
git commit -m '功能'
git push origin dev

第二天上班:
git pull origin dev
有冲突解决冲突
继续开发。



协同开发

master dev 每个人创建自己的分支,master不用
每个人在自己的分支开发功能,
开发完成后add commit 推送到远程仓库自己的分支
提交pull request ,合并到dev分支上都合并完没问题了,在合并到master分支上。


****

`

posted @ 2020-05-22 16:11  鞭码形动  阅读(171)  评论(0编辑  收藏  举报