Django

1,http

小提示:"{:0>3}".formate(9) 右对齐若小于3位 --->00n 0位占位符,> 表示右对齐

响应格式

 

请求格式

 

import socket
import os
# =========================================================
def ret(path):
   dir = "../html/"
   file = os.path.join(dir,path.strip(r"[/\]"))
   if os.path.exists(file):
       with open(file,"rb") as f:
           return f.read()
   else:
       with open(os.path.join(dir,"error.html"),"rb") as f:
           return f.read()
# =========================================================
sk = socket.socket()
sk.bind(("127.0.0.1",8080))
sk.listen()
while True:
   conn,_ = sk.accept()
   data = conn.recv(8096)
   data_str = str(data,encoding="utf8")
   list = data_str.split("\r\n")
   # for i in list:
   #     print(i) # 获取每行,还可以通过 空格分隔 获取访问路径,根据路径返回不同那内容
   # # 通过请求路径,建立字典,对应但会数据,可直接返回
   firstLine = str(list[0]).split(' ')
   temp = firstLine[1].split("?")
   path = temp[0]
   #print(path)
   conn.send(b'http1.1 200 \r\n\r\n')
   conn.send(ret(path))
   conn.close()
sk.close()
"""
# http 请求
GET /sadasd/?name=dzf HTTP/1.1\r\n
Host: 127.0.0.1:8080\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36\r\n
Sec-Fetch-Mode: navigate\r\n
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3\r\n
Sec-Fetch-Site: none\r\n
Accept-Encoding: gzip, deflate, br\r\n
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7\r\n\r\n'
"""
# =========================================================
"""
Python中 Web框架的分类:
1. web框架的本质:
  socket服务端 与 浏览器的通信
2. socket服务端功能划分:
  a. 负责与浏览器收发消息(socket通信) --> wsgiref/uWsgi/gunicorn... 自己写的长时间连接会报错
  b. 根据用户访问不同的路径执行不同的函数
  c. 从HTML读取出内容,并且完成字符串的替换(动态网站的本质) --> jinja2(模板语言)
1. 按上面三个功能划分:
  1. 框架自带a,b,c                 --> Tornado
  2. 框架自带b和c(和jinja2类似),使用第三方的a(wsgiref)   --> Django
  3. 框架自带b,使用第三方的a和c(jinja2)   --> Flask
2. 按另一个维度来划分:
  1. Django   --> 大而全(你做一个网站能用到的它都有)
  2. 其他     --> Flask 轻量级
"""
# 安装jinja2
from wsgiref.simple_server import make_server #   a 功能
from jinja2 import Template # c功能


def index():
   with open("09 jinja2版web框架.html", "r", encoding="utf-8") as f:
       data = f.read()
   template = Template(data)  # 生成模板文件
   # 从数据库中取数据
   import pymysql
   conn = pymysql.connect(
       host="127.0.0.1",
       port=3306,
       user="root",
       password="123456",
       database="day59",
       charset="utf8",
  )
   cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
   cursor.execute("select * from userinfo;")
   user_list = cursor.fetchall()
   # 实现字符串的替换
   ret = template.render({"user_list": user_list})  # 把数据填充到模板里面
   return [bytes(ret, encoding="utf8"), ]
def home():
   with open("home.html", "rb") as f:
       data = f.read()
   return [data, ]
# 定义一个url和函数的对应关系
URL_LIST = [
  ("/index/", index),
  ("/home/", home),
]
def run_server(environ, start_response):
   start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  # 设置HTTP响应的状态码和头信息
   url = environ['PATH_INFO']  # 取到用户输入的url
   func = None  # 将要执行的函数
   for i in URL_LIST:
       if i[0] == url:
           func = i[1]  # 去之前定义好的url列表里找url应该执行的函数
           break
   if func:  # 如果能找到要执行的函数
       return func()  # 返回函数的执行结果
   else:
       return [bytes("404没有该页面", encoding="utf8"), ]

if __name__ == '__main__':
   httpd = make_server('127.0.0.1', 8000, run_server)
   print("Serving HTTP on port 8000...")
   httpd.serve_forever()
"""
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="x-ua-compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Title</title>
</head>
<body>


<table border="1">
  <thead>
  <tr>
      <th>ID</th>
      <th>用户名</th>
      <th>密码</th>
  </tr>
  </thead>
  <tbody>
  {% for user in user_list %}
  <tr>
      <td>{{user.id}}</td>
      <td>{{user.name}}</td>
      <td>{{user.pwd}}</td>
  </tr>
  {% endfor %}
  </tbody>
</table>
</body>
</html>
"""

2 django基础

# ==============================================================================
# 安装 pip install Django==2.2.4
# setting 中添加库 https://pypi.tuna.tsinghua.edu.cn/simple/ 防止time out
# cmd 新建项目 进入目标目录 django-admin startprojext project_name
# 或pycharm 新建Django 使用存在的解释器,第二个
# # 项目配置
"""
1,urls.py
urlpatterns:保存了路径与函数的对应关系
  在该文件中定义新的项,并写函数,支持正则
  也可以建立专门的函数文件,只需在urls 中导入即可
  import django.shortcuts import HttpResponse
  def admin(request): # request为所有请求的参数
      return HttpResponse("xxx)
  patterns,函数内容都可以动态修改,
项目启动
普通启动后,只能127.0.0.1 访问
若要使用ip访问,allowed_host = ['*'] runserver 0.0.0.0:80
1,cmd中python manage.py runserver [port]
2,选择项目 ,点击旁边三角 edit config 也可改端口
template下建立html文件
所有与html文件有关的设置都放在 setting的templates下
如何那绝对路径:在/下建立文件 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
指定html文件目录:'DIRS': [os.path.join(BASE_DIR, 'templates')]
  用到的时候 直接写文件名即可,"./写template",或"./xx/xx.html"
  不能直接写文件名
返回方式:
  字符:直接写或文件--->字符 打开文件 字节返回
  文件使用模块: render(request,"文件路径")
  return render(request,"Hello.html") # 此处可直接写文件名
此处注意,若在template下有多级文件夹,文件夹下有文件,此时访问 只写(xxx/xx.html) 不用加"/",以文件夹开头即可

对于static下的文件

2,配置静态文件,js,css等
setting 最后添加
  STATICFILES_DIRS=[
      os.path.join(BASE_DIR,"static") # static 为自己制定的文件夹,名字随意,可配置多个
  ]
STATIC_URL="/static/" 就代表的是 "D:/xxx/yy/static/" 别名 若有多个都当做在static下
html引用时使用绝对路径就不用从头开始写
  例:href="/static/bootstrap/css/bootstrap.min.css"
3,若出现403   中间价问题
  注释settings中47行 'django.middleware.csrf.CsrfViewMiddleware',
4,连接数据库,后边详细
"""
# 登录时,一个函数处理即可,若为get 表示请求html,若为post,处理数据,返回页面
# if request.method=="POST" "GET"
# 默认的页面请求是get,a标签也是get

"""
def
  if post:
      处理
  return html
   
render(request,"xx.html",{"msg":"xxx"})   html中使用{{msg}}可获取

redirect 模块 跳转
return redirect("http://www.baidu.com") # 让浏览器请求新的页面,不是服务器返回的
return redirect("/login/") # 本网站 用"/"包裹

"""
# ===============================================
# app:方便在项目中管理实现不同的业务功能
"""
  project -->项目 (学校)  
  app --> 学院(软件学院,量子力学)
  1,创建app
      在cmd ,某个project 根目录 python(3) manage.py startapp app_name
      执行后会在根目录下创建文件夹 app_name
      文件夹 migrations
      文件 __init__.py
          admin.py:管理控制台
          apps.py:app相关的配置
          models.py:ORM的类 entity
          tests.py:测试文件
          views.py:函数文件
  2,或在创建项目的时候直接添加一个
settings
  INSTALLED_APPS 配置,添加上"app_01"
  或"app_01.apps.App_01Config" 类 推荐
"""
# =============================================
# ORM 简单,开发效率高, 执行效率低
# 不能创建数据库
# 步骤
"""
1,创建数据库
2,setting 配置数据库
DATABASES = {
  'default': {
      #数据库类型
      'ENGINE': 'django.db.backends.mysql',
      'HOST':'127.0.0.1',
      'PORT':3306,
      'NAME':'test',
      'USER':'root',
      'PASSWORD':'xxxx'
  }
}
3,告诉Django用什么连接数据库??MYsqlDB(不支持python3), pymysql
  mysqldb是默认的,需要更改为pymysql
  项目下,__init__.py 不是app的init
      import pymysql
      pymysql.install_as_MySQLdb()
4,创建类 modules.py 必须在个文件,必须继承(models.Model)
class UserInfo(models.Model):
  id = models.AutoField(primary_key=True) # 创建一个自增的主键字段
  name = models.CharField(null=False, max_length=32,unique=True)   # 创建一个varchar(20)类型的不能为空的字段
5,创建表
  创建后 不能修改,若要修改只能通过 modules修改,不然函数调用会报错
  1. python3 manage.py makemigrations # 记录moduls 的变化,会记录在app/migrations下
  2. python3 manage.py migrate # 转变为sql执行
  可能pymsql驱动的mysqlclient客户端版本过低,修改G:\Program\Python\Python3.7.4\Lib\site-packages\django\db\backends\mysql\base.py
  注释掉如下,但还有错误 str 编码错误,还要修改operations.py
  # if version < (1, 3, 13):
  #   raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__)   # 需要更换pymsql
  或
  下载mysqlclient,手动下载安装 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient
  mysqlclient‑1.4.4‑cp37‑cp37m‑win32.whl 37为对应版本,还有显卡版本 注意app一定要在setting注册 否则会失败 # init 中不用写了
6, 会创建很多表,默认实体表名为 app_name_class_name 还有其他权限表,会话表等
"""
# ========================================================
"""
  request.GET[POST] 大字典
  在函数中获取数据库中数据
  from app_01 import modules
  ### 查询所有的数据
      ret = modules.Userinfo(类名).objects.all().order_by('id') 列表返回 userinfo对象,并根据id排序
       
      ret[0].id 或name 即可得到对应数据
      使用render传入html即可
  ### 创建
      modules.Userinfo(类名).objects.create(name="xxx")
  ### 删除
      request.Get.get("id",None)
      ret = modules.Userinfo(类名).objects.get(id="xxx") 请求不到会报错,可以使用 filter(id="xxx")
      ret.delete()
  ### 修改
      request.Get.get("id",None)
      ret = modules.Userinfo(类名).objects.get(id="xxx") 请求不到会报错,可以使用 filter(id="xxx")
      ret.name = new_name
      ret.save() # 提交到数据库
       
"""

3 django web实例

# 1,get 携带数据最大40k url长度不同浏览器不同2k-8k
# 2,... makemigrate app_name 只有一个时可省
# 3,外键
"""
class A(modules):pass
class B(modules):
  public = modules.Foreignkey(to=A) # A必须在前面存在
  public = modules.Foreignkey(to="A") # 没要求,只要最后有A即可
  最后 public列在库中名为 public_A主键名,好像是这个如果不是的话就是
  public_id,(好像这个可能比较大)
注:存入的时候public存的是A的id,使用create(public_id=xxx)
或使用 create(public=查到id的对象)
但获取get() B对象时 获取到所有属性,public 这个属性对应的是一个对象,若要获取某个属性,需再次加点
"""
# 4,数据库中已存在数据,此时修改modules,例如添加列,
# 会指定让你输入默认值,或直接在modules中 default="xxx"
# 5,多对多关系,需要第三张表 (id,table_id,table2_id)
"""
# 例如作者,书
  class book(modules):pass 只写 属性即可
  class auther(modules):
      .....
      book=modules.ManyToMany(to="book")
       
      auther 类执行后会生成2个表
          第一张:app_name_auther,注意没有book属性
          第二张:app_name_ManyToMany所在类名_赋值名(book)
              属性:id ,auther_id,book(book类名)_id
例:查询某个作者
1,先ret = get(id=x)得到作者
2,ret.book(是上边的manytomany的book).all() 帮助查询关联的对象
控制台打印all 有括号 列表返回所有书对象
for 显示的时候 for b in ret.book.all 没有括号
"""
# 6,request.POST.get("xxx") 若xxx中多个参数 返回值列表中对后一个
#   使用getlist("xxx") 获取列表
# 7,添加作者并关联存在的书本
#   author =....creat(name=...等属性赋值) 注意没有book
#   author.book(manytomany那个book).set(getlist中得到的books列表)
#   注意不用save()
# 8,删除作者 普通的删除,只不过是关联删除,先删书,再删作者
# 9,更改作者,与普通更改相同,获取目标对象obj,修改普通属性,
#   再obj.book.set(获取到的list) obj.save()

 

4,MVC

# 在python中称为MTV
# module(modules),
# view(Templates)
# controller(urls,views),
# 母板
"""
1,新建公共html
  ....
  {# 这里是页面不同部分 #}
  {% block page_main(名字随意) %}
  {% endblock %}
  ....
2,建立子html
  {% extends "base.html路径"%}
  {% block page-css %}{% endblock%}
  {% block page-main %}<h1>直接写即可,不用body等标签</h1>{% endblock%}
  {% block page-js %}{% endblock%}
用处:1,替换固定的模板内容
  2,替换某个html专用css ,其实在子html中可直接加入cssdeng
      但会造成css位置混乱,此方法可使css等都位于head标签
注意:
  1,模板不能嵌套,
  2,extends行一定在第一行, 路径必须加引号
  3,可有多个block,一般3个
  4,views中返回的是子html,不是base.html
"""
# 导入组件:就是替换
"""
  将导航条放入单一功能模块
  {% include "xxx.html" %}
"""
# 引入静态文件
"""
  若static别名更改,引入的css等路径都要更改
  解决方案: 路径拼接
      {%load static%}
      <link href="{% static 'bootstrap/xxx'%}" rel="stylesheet>
      或者
      {%load static%} 也要导入
      <link href="{%get_static_prefix%}bootstrap/xxx" rel="stylesheet>
多次使用的图片
{% load static%}
{%static 'xxx.jpg' as yy%}
<img src="{{yy}}"/> 即可
"""
# simpletag
"""
复杂的filter
路径是实在app下templatags下
from...
register... 注意register不能更改
@register.simple_tag(name="xx") 可不指定名字默认是函数名
def my_sum(arg1,arg2,arg3):
  return arg1+arg2+arg3

html使用
{%load xxx文件%}
{% xx arg1 arg2 arg3 %}
"""
# inclusion_tag 返回html代码
"""
  与simple类似
@register.inclusion_tag("xxx.html") 注意此处没有name ,调用使用函数名,向下12行
def my_inclusion(arg1):
  data = 处理参数args,变为其他数据,列表,字典等
  return {"result":data}
xxx.html
<ui>
{% for i in result %} # 加入为迭代对象
  <li>{{i}}</li>
{%endfor%}
</ui>
调用{{my_inclusion arg}}
"""

# ======================================
# view(接受响应的部分)
# CBV(基于类的视图) FBA(基于函数的视图:前面的都是)
"""
form django.views import View
class add_list(View):
  # urls调用时使用类名add_list ,会根据请求方式转到对应方法
  def get(self,request):pass
  def post(self,request):pass
urls 中
  path("xx",add_list.as_view()) 调用
"""
# request
"""
request.method
request.GET
request.POST
request.path_info "/xxx/yy/" 形式,不带ip,端口,参数
request.body
  若为get     ---->b''
  若为post   ----->b'提交的参数名=值(若为中文会变成%E6879AS7D54%等编码数据)'
"""
# =============================================
# 上传文件
"""
  form 注意使用post 还有enctype="multipart/form-data"
  def upload(request):
      request.FILES 字典,{"file1":obj,"file2",obj}
      filname =request.FILES['表单中name'].name
      with open0.....
          for i in request.FILES['表单中name'].chunks()
              f.write(i)
"""
# ===============================================
# response
""""
1,HttpResponse
2,render
3,redirect

4,返回json
import json
return HttpResponse(json.dumps(xxx)) 返回json字符串

from django.http import JsonHttpResponse # 封装的json ,不能串list
return JsonHttpResponse(xxxx)直接返回
  若要传list JsonHttpResponse(xxxx,safe=False) 即可
"""

5,django 路由系统

# 路由系统
"""
urlpatterns=[
  url,path(2.0以后)(正则,函数名,参数,别名)
]
  r"^book/[0-9]{2,4}/$"
  r"^book/([0-9]{2,4})/([a-zA-Z]{2})/$"
分组匹配时,会把分组的内容当做参数传入:位置参数
  例: book/11/ac   对应函数应该为(request,arg1,arg2) 接受
  <(?P<name1>\w*)> 匹配"<里边是字符>" (?P=name1) 复制匹配规则
多路径匹配, 最后注意加 / $
  对于路径上的"/" 最后会默认添加"/" 若要取消
  settings APPEND_SLASH=False

分组命名匹配
  r"^book/(?P<year>[0-9]{2,4})/(?P<month>[a-zA-Z]{2})/$"
  def fun(request,year,month) 要明确变量
  def fun(request,**kwargs) 也可 接收到kwargs为{"year":xx,"month":yy}
  注意不能混用,若month不写,后边分组匹配不到
多个路径对应一个函数:通常适用于 无参时访问第一页,有参访问对应页面
  def fun(res,id="1"):指定默认值即可

app中建立app01_urls.py 名字无规定
  import django.urls import url
  urlpatterns=[] 必须有这个属性
在主urls中导入
  from ... import app01_urls
  urlpattrens=[
      ...
      url(r"^app01/",include(urls)) # 所有以app01开头的 分流
  ]
  查找时 /app01/xx/yy 注意为r"^app01",后边没有"/" 会报错
注:url(xxx,yy,{"age":18}) 对应的yy函数 使用def yy(req,age)
  可以传参,但不常用
"""
# 页面之间跳转的时候 不能写死跳转路径:反向解析 ->通过别名方式
# 命名url与url解析:urlpattern中的url会发生变化,html中写死了
#   url(xx,yy,name="zzz") 此时html中 写 href="{url 'zzz'}" 相当于xxx 即可跳转到yy函数
# 若在函数中跳转,也不能写死
#   from django.urls import reverse
#   analysis_url=reverse("zzz")
#   return redirect(analysis_url)
# 注意对于传参数的url 需要传参数
#   reverse("zzz",kwargs={"xx":1,"yy":2}) 当分组有别名时字典传入,若不是 args=(..) 元祖传入即可
#   {% url 'zzz' arg1 arg2 ...%}
# 在不同的app中了能会出现相同的别名
#   可以再父urls中 url(xx,include(app01_urls.py,namespace="app01"))
#       html {% url 'app01:zzz'%} 找到app01 里面的别名zzz
# ==========================================
# orm 配合 路由系统
"""
  1,不同的表的删除,可以使用一个方法:
  url(r"^delete/([a-zA-Z]+)/(\d)/$",delete) 传递表名,id
  对于比较长的正则,可以建文件夹,编译 r = re.complie(xxx),再引入即可
  2,def fun(res,arg1,arg2):
      hasttr(py文件,"xxx") getattr(py文件,"xxx")得到目标
      if hasattr(modules,arg1.capitalized()) 首字母打大写
          cls = get....
          cls.Object.get(id=arg2).delete try本句
"""
# ==========================================
# orm常用字段 charfiled 对应varchar,没有固定长度的字段,可自定义
"""
class FixedCharField(models.Field):
  自定义的char类型的字段类
  def __init__(self, max_length, *args, **kwargs):
      self.max_length = max_length
      super(FixedCharField, self).__init__(max_length=max_length, *args, **kwargs)

  def db_type(self, connection):

      限定生成数据库表的字段类型为char,长度为max_length指定的值
      return 'char(%s)' % self.max_length
class Person(models.Model):
  name = models.CharField(max_length=32)
  new_name = FixedCharField(max_length=64, default="张三") # char(64)
  age = models.IntegerField(default=18)
  birthday = models.DateField(auto_now_add=True)

  def __str__(self):
      return self.name

  class Meta:
      db_table = "person" 指明 生成的数据库名


"""
#   1,AutoField int自增列,必须primary_key=True,若没有自增列,会自动创建id自增列
#   2,IntegerField 整数范围 21亿那个
#   3,CharField 必须max_length, 指定长度
#   4,DateTimeField 相当于datetime模块
#       yyyy-mm-dd hh:MM[:ss[.uuuuu]][TZ]
#   5,DateField
#         auto_now_add=True 创建记录时会添加当前是时间,不用写default
#         auto_now=True 每次更新记录时会更新该字段
# 不常用字段
#   BigAutoField,大整数
#   SmallIntegerField 小整数 -32768-32767
#   PositiveSmallIntegerField 正小整的 0-32767
#   PositiveIntegerField 0-21亿
#   BigIntegerField -922..... --- 922....
#   BooleanFiled
#   NullBooleanField 可为空的bool
#   TextField 大文本
#   EmailField 还是charfiled django做了校验
#   IPAddressField 有验证
#   GenericIPAddressField 支持ipv4,ipv6
#   URLFiled charfiled 验证
#   SlugFiled 字符串类型,字母数字下划线连字符等
#   CommaSeparatedIntegerFiled 格式为逗号分割的数字
#   UUIDField UUID验证
#   TimeFiled
#   DurationFiled 长整数,时间间隔orm中获取到的类型为datetime.timedel类型
#   FloatField 浮点型
#   DecimalField 10进制小数
#       max_dights,小数总长度,decimal_places,小数位长度
#   BinaryField 二进制数据
# =========================================
# 字段参数
#   null 没写=True 表示不能为空
#   unique
#   db_index=true 表示为此字段设置索引
#   default
#   to 设置关联的表
#   to_field="xxx" 设置关联的列 默认是id
#   related_name 反向操作时使用的字段名,用于代替反向查询时的"表名_set"
#   related_query_name 反向查询操作时,使用的链接前缀,用于替换表明
#   on_delete 当删除关联表中的数据时,当前表与其的关联行为
#       modules.Cascade 关联删除 默认的
#       modules.DO_NOTHING 引发Integrity错误
#       modules.PROJECT 引发protected错误
#       modules.SET_NLL 关联的字段置位NULL(前提字段可空)
#       modules.SET_DEFAULT 关联的字段为默认值(前提字段有默认值)
#       modules.SET(func) 可自定义函数
#   db_constraint=Flase 用上了外键,但是没有级联操作,
#   软外键:方法1,代码实现 方法2,db_constraint=Flase
# ======================================
# class Mate:
# db_table 重写表名
# index_together 联合索引   2列做索引
# unique_together 联合唯一索引 2列可以有一个重复,不能都重复
# ordering 指定an什么字段排序,设置了该属性,结果才可以reverse()

5_orm_1

# ORM小练习 如何在一个Python脚本或文件中 加载Django项目的配置和变量信息
# 常用的查询方法
import os
if __name__ == '__main__':
   # 加载Django项目的配置信息
   os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ormday69.settings")
   # 导入Django,并启动Django项目
   import django
   django.setup()

   # from app_01 import models

   # # 查询所有的人
   # ret = models.Person.objects.all()

   # # get查询
   # ret = models.Person.objects.get(name="小黑") 只查询到 第一个

   # # filter
   # ret = models.Person.objects.filter(id=100) # 不存在返回一个空的QuerySet,不会报错
   # # 就算查询的结果只有一个,返回的也是QuerySet,我们要用索引的方式取出第一个元素
   # ret = models.Person.objects.filter(id=1)[0]

   # print("exclude".center(80, "*")) # 占80个字符居中,*号填充
   # # exclude
   # ret = models.Person.objects.exclude(id=1) 反向查询 id!=1

   # print("values".center(80, "*"))
   # # values 返回一个QuerySet对象,里面都是字典。 不写字段名,默认查询所有字段
   # ret = models.Person.objects.values("name", "birthday")

   # print("values_list".center(80, "*"))
   # # values_list 返回一个QuerySet对象,里面都是元祖。 不写字段名,直接写字段值,默认查询所有字段
   # ret = models.Person.objects.values_list()

   # print("order_by".center(80, "*"))
   # # order_by 按照指定的字段排序
   # ret = models.Person.objects.all().order_by("birthday")

   # print("reverse".center(80, "*"))
   # # reverse 将一个有序的QuerySet 反转顺序
   # # 对有序的QuerySet才能调用reverse
   # ret = models.Person.objects.all().reverse()

   # print("count".center(80, "*"))
   # # count 返回QuerySet中对象的数量
   # ret = models.Person.objects.all().count()

   # print("first".center(80, "*"))
   # # first 返回QuerySet中第一个对象
   # ret = models.Person.objects.all().first()

   # print("last".center(80, "*"))
   # # last 返回QuerySet中最后一个对象
   # ret = models.Person.objects.all().last()

   # print("exists".center(80, "*"))
   # # exists 判断表里有没有数据
   # ret = models.Book.objects.exists()
   # print(ret)
# ===================================================

   # 单表查询之神奇的双下划线
   # # 查询id值大于1小于4的结果
   # ret = models.Person.objects.filter(id__gt=1, id__lt=4)
# ===================================================
   # # in
   # # 查询 id 在 [1, 3, 5, 7]中的结果
   # ret = models.Person.objects.filter(id__in=[1, 3, 5, 7])
   # ret = models.Person.objects.exclude(id__in=[1, 3, 5, 7]) 等于not in
# ===================================================
   # 模糊查询
   # # contains 字段包含指定值的
   # # icontains 忽略大小写包含指定值
   # ret = models.Person.objects.filter(name__contains="小")
   # isstartwith,startwith,endwith,isendwith
# ===================================================
   # # range
   # # 判断id值在 哪个区间的 SQL语句中的between and 1<= <=3
   # ret = models.Person.objects.filter(id__range=[1,3])
# ===================================================
   # # 日期和时间字段还可以有以下写法,按照日期中的某一项查询
   # ret = models.Person.objects.filter(birthday__year=2000)
   # ret = models.Person.objects.filter(birthday__year=2000, birthday__month=5)
# ===================================================
   # 注意 obj得到的是对象,不能直接.value或value_list

   # 外键的查询操作

   # 正向查询,通过有外键字段的表查没得是正向
   # 基于对象 跨表查询
   # book_obj = models.Book.objects.all().first()
   # ret = book_obj.publisher # 和我这本书关联的出版社对象
   # ret = book_obj.publisher.name # 和我这本书关联的出版社对象

   # 查询id是1的书的出版社的名称
   # 利用双下划线 跨表查询
   # 双下划线就表示跨了一张表
   # ret = models.Book.objects.filter(id=1).values_list("publisher__name")或value
# =================================================================

   # 反向查询
   # 1. 基于对象查询
   # publisher_obj = models.Publisher.objects.get(id=1) # 得到一个具体的对象
   # # ret = publisher_obj.book_set.all() # 反向得到书的信息,默认是表名_set
   # 建表时通过related_name="books"可更改
   # ret = publisher_obj.books.all()

   # # 2. 基于双下划线
   # ret = models.Publisher.objects.filter(id=1).values_list("(related_query_name)__title")
# =================================================================
   # 多对多
   # 查询
   # author_obj = models.Author.objects.first()
   # print(author_obj.name)
   # 查询金老板写过的书
   # ret = author_obj.books.all() 前边学过 author_obj.books:关联管理器
# ==========================
   # 1. create
   # 通过作者创建一本书,会自动保存
   # 做了两件事:
   # 1. 在book表里面创建一本新书,2. 在作者和书的关系表中添加关联记录
   # author_obj.books.create(title="金老板自传", publisher_id=2)
# =============
   # 2. add
   # 在金老板关联的书里面,再加一本id是4的书
   # book_obj = models.Book.objects.get(id=4)
   # author_obj.books.add(book_obj) # 加已经存在的书
   # 添加多个
   # book_objs = models.Book.objects.filter(id__gt=5)
   # author_obj.books.add(*book_objs) # 要把列表打散再传进去
   # 直接添加id
   # author_obj.books.add(9)
# =======================================
   # remove
   # 从金老板关联的书里面把 开飞船 删掉
   # book_obj = models.Book.objects.get(title="跟金老板学开飞船")
   # author_obj.books.remove(book_obj)
   # 从金老板关联的书里面把 id是8的记录 删掉
   # author_obj.books.remove(8)
# =======================================
   # clear
   # 清空
   # 把景女神 关联的所有书都删掉
   # jing_obj = models.Author.objects.get(id=2)
   # jing_obj.books.clear()
# =======================================

   # 额外补充的,外键的反向操作

   # 找到id是1的出版社
   # publisher_obj = models.Publisher.objects.get(id=2)
   # publisher_obj.books.clear() # 注意books的 外键是否可为空
# =================================================================
   # 聚合
   from django.db.models import Avg, Sum, Max, Min, Count
   # ret = models.Book.objects.all().aggregate(price_avg=Avg("price"))
   # print(ret) 返回字典{"price":xxx} 按price列指定, 可修改(k=Avg("price"))

   # ret = models.Book.objects.all().aggregate(price_avg=Avg("price"), price_max=Max("price"), price_min=Min("price"))
   # 也是字典返回
   # print(ret.get("price_max"), type(ret.get("price_max")))
# =================================================================
   # 分组查询

   # 查询每一本书的作者个数
   # ret = models.Book.objects.all().annotate(author_num=Count("author")) queryset 书对象
   # # print(ret)
   # for book in ret:
   #     print("书名:{},作者数量:{}".format(book.title, book.author_num)) # 书多了一个author_num属性

   # 查询作者数量大于1的书
   # ret = models.Book.objects.all().annotate(author_num=Count("author")).filter(author_num__gt=1)
   # print(ret)

   # 查询各个作者出的书的总价格
   # ret = models.Author.objects.all().annotate(price_sum=Sum("books__price")).values_list("name", "price_sum")
   # ret = models.Author.objects.all().annotate(price_sum=Sum("books__price"))
   # print(ret)
   # for i in ret:
   #     print(i, i.name, i.price_sum)
   # print(ret.values_list("id", "name", "price_sum")) 与for类似
# =================================================================
   # F(两个字段作比较)和Q
       # ret = models.Book.objects.filter(price__gt=9.99)
       # print(ret)

   # 查询出 库存数 大于 卖出数的 所有书(两个字段做比较)
   from django.db.models import F
   # ret = models.Book.objects.filter(kucun__gt=F("maichu"))
# =================================================================
   # 刷单 把每一本书的卖出数都乘以3
   # obj = models.Book.objects.first()
   # obj.maichu = 1000 * 3
   # obj.save() 只修改一个对象
   # 具体的对象没有update(),QuerySet对象才有update()方法。

   # models.Book.objects.update(maichu=F("maichu")*3) 全都修改了

   # 给每一本书的书名后面加上 第一版
   # from django.db.models.functions import Concat
   # from django.db.models import Value
   # 修改字符串
   # models.Book.objects.update(title=Concat(F("title"), Value("第一版")))


   # Q查询
   from django.db.models import Q
   # 查询 卖出数大于1000,并且 价格小于100的所有书
   # ret = models.Book.objects.filter(maichu__gt=1000, price__lt=100) # 并且
   # print(ret)
   # 查询 卖出数大于1000,或者 价格小于100的所有书
   # ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100))
   # print(ret)
   # Q查询和字段查询同时存在时, 字段查询要放在Q查询的后面 后边的与前边是 与的关系
   # ret = models.Book.objects.filter(Q(maichu__gt=1000) | Q(price__lt=100), title__contains="金老板")
   # print(ret)


   # Django ORM 事务

   # try:
   #     from django.db import transaction
   #
   #     with transaction.atomic():   # 加入事务
   #         # 先创建一个出版社
   #         new_publisher = models.Publisher.objects.create(name="火星出版社")
   #         # 创建一本书
   #         models.Book.objects.create(
   #             title="橘子物语",
   #             price=11.11,
   #             kucun=10,
   #             maichu=10,
   #             publisher_id=1000 # 指定一个不存在的出版社id
   #         )
   # except Exception as e:
   #     print(str(e))

   # 没有指定原子操作
   # try:
   #
   #     # 先创建一个出版社
   #     new_publisher = models.Publisher.objects.create(name="火星出版社")
   #     # 创建一本书
   #     models.Book.objects.create(
   #         title="橘子物语",
   #         price=11.11,
   #         kucun=10,
   #         maichu=10,
   #         publisher_id=1000 # 指定一个不存在的出版社id
   #     )
   # except Exception as e:
   #     print(str(e))

   # 执行原生SQL
   # 更高灵活度的方式执行原生SQL语句
   # from django.db import connection
   # cursor = connection.cursor() # cursor = connections['default'].cursor()
   # cursor.execute("SELECT * from app01_book where id = %s", [1])
   # ret = cursor.fetchone()
   # print(ret)


   # 在QuerSet查询的基础上自己指定其他的SQL语句(了解即可)
   ret = models.Book.objects.extra(
       # 把出版社计数 赋值给newid
       select={'newid': 'select count(1) from app01_publisher where id>%s'},
       select_params=[1, ],

       where=["app01_book.id=%s"],

       params=[1, ],
       tables=['app01_publisher']
  )

   print(ret)
   for i in ret:
       print(i)

6_orm_2

# orm一对一:
#   当一个表中属性过多的时候,将属性分开,分为常用的属性表,和不常用的
#   例如第三方登录时,常请求的用户名,密码,而爱好,身份证号等不常用信息,可存放在另一张表中
#   构成一对一关系
"""
class A(model.Model):
  ...
  detail=models.OneToOneField(to="B") 逐数据库中 列名为detail_id
class B(models.Model):
  ...若没有主键会自动创建id列
通过A获取B a = models.A.objects.get(id=1) b = a.deatil b.xxx可获取属性
"""
# orm多对多:
#   1,自动创建第三张表
#   2,自己创建
""" 自己创建第三张表 查询比较慢,不能使用author.books了
class Author_Book(model.Model):
  ...
  author = modelsForeignKey("Author") author_id
  book = modelsForeignKey("Book) book_id
查询时,手动经
过第三张表查询
"""
#   3,自己创建表,但使用ManyToMany
"""
  第三张表的建立与2相同
  class Author():
      ...
      books = models.ManyToMany(to="Book",throuth="第三张表",throght_fields=(author,book))
      里边是元祖,顺序固定,应为是在author中 所以先写author(第三张表中的author)
      注意第三张表 class Meta:
                              unique_tgether = ("author","book")
"""
# 第三张表无其他字段,第一种
# 有其他字段:第三种 如聊天记录,不仅要双方那个id,还有时间,内容.... 因此需要自己创建
#   使用第三种方法时,是没有add方法,remove()方法等
#   例:给作者1加一本书 obj.get(id=1).books.add(xxx) 第一种
#   第三种:直接添加记录
#   反查有book查author   ...filter(id=1).value("related__name__author的属性")

# ===========================================
# csfr 跨站请求伪造 服务器返回给用户一个html页面,里面有form表单   action="xxxx"
#   此时即可得到xxxx请求地址, 即可自定义html,form添加其他input 请求服务器,即跨站请求伪造
#   解决方案:服务器返回的html中有一项input name=key value="每个用户不同,或每次请求不同",以此识别用户
# 默认是开启的, 即不能跨站请求,弱不需要注释即可
"""
  使用方法:在form中 {% csrf_token%} 在html中会变为 <input type="hidden" name="csrfmiddlewaretoken"
  value="随机字符串"/>
  若出现跨站请求即会禁止 一般就是403
  在render中 生成html时,会自动产生key,通过Post接受参数时,会自动校验,不通过即拒绝
"""

7_分页_session_cookies

 

# 分页
class Page():

   def __init__(self, page_num, total_count, url_prefix, per_page=10, max_page=11):
       """
      :param page_num: 当前页码数
      :param total_count: 数据总数
      :param url_prefix: a标签href的前缀
      :param per_page: 每页显示多少条数据
      :param max_page: 页面上最多显示几个页码
      """
       self.url_prefix = url_prefix
       self.max_page = max_page
       # 每一页显示多少条数据
       # 总共需要多少页码来展示
       total_page, m = divmod(total_count, per_page)
       if m:
           total_page += 1
       self.total_page = total_page

       try:
           page_num = int(page_num)
           # 如果输入的页码数超过了最大的页码数,默认返回最后一页
           if page_num > total_page:
               page_num = total_page
       except Exception as e:
           # 当输入的页码不是正经数字的时候 默认返回第一页的数据
           page_num = 1
       self.page_num = page_num

       # 定义两个变量保存数据从哪儿取到哪儿
       self.data_start = (page_num - 1) * 10
       self.data_end = page_num * 10

       # 页面上总共展示多少页码
       if total_page < self.max_page:
           self.max_page = total_page

       half_max_page = self.max_page // 2
       # 页面上展示的页码从哪儿开始
       page_start = page_num - half_max_page
       # 页面上展示的页码到哪儿结束
       page_end = page_num + half_max_page
       # 如果当前页减一半 比1还小
       if page_start <= 1:
           page_start = 1
           page_end = self.max_page
       # 如果 当前页 加 一半 比总页码数还大
       if page_end >= total_page:
           page_end = total_page
           page_start = total_page - self.max_page + 1
       self.page_start = page_start
       self.page_end = page_end

   @property
   def start(self):
       return self.data_start

   @property
   def end(self):
       return self.data_end


   def page_html(self):
       # 自己拼接分页的HTML代码
       html_str_list = []
       # 加上第一页
       html_str_list.append('<li><a href="{}?page=1">首页</a></li>'.format( self.url_prefix))

       # 判断一下 如果是第一页,就没有上一页
       if self.page_num <= 1:
           html_str_list.append('<li class="disabled"><a href="#"><span aria-hidden="true">&laquo;</span></a></li>'.format(self.page_num-1))
       else:
           # 加一个上一页的标签
           html_str_list.append('<li><a href="{}?page={}"><span aria-hidden="true">&laquo;</span></a></li>'.format( self.url_prefix, self.page_num-1))

       for i in range(self.page_start, self.page_end+1):
           # 如果是当前页就加一个active样式类
           if i == self.page_num:
               tmp = '<li class="active"><a href="{0}?page={1}">{1}</a></li>'.format(self.url_prefix, i)
           else:
               tmp = '<li><a href="{0}?page={1}">{1}</a></li>'.format( self.url_prefix, i)

           html_str_list.append(tmp)

       # 加一个下一页的按钮
       # 判断,如果是最后一页,就没有下一页
       if self.page_num >= self.total_page:
           html_str_list.append('<li class="disabled"><a href="#"><span aria-hidden="true">&raquo;</span></a></li>')
       else:
           html_str_list.append('<li><a href="{}?page={}"><span aria-hidden="true">&raquo;</span></a></li>'.format( self.url_prefix, self.page_num+1))
       # 加最后一页
       html_str_list.append('<li><a href="{}?page={}">尾页</a></li>'.format( self.url_prefix, self.total_page))

       page_html = "".join(html_str_list)
       return page_html
   # from utils.mypage import Page
   # page_obj = Page(page_num, total_count, per_page=10, url_prefix="/books/", max_page=9,)
   # ret = models.Book.objects.all()[page_obj.start:page_obj.end]
   # page_html = page_obj.page_html()
   # return render(request, "books.html", {"books": ret, "page_html": page_html})
# ====================================================================
# cookies 和session 因为http请求是无状态的,请求之间无关系
# cookie 保存在浏览器上的键值对,访问时会自动添加
#   例如:登录,输入密码登陆后,若成功,响应请求,让浏览器保存cookie本机,下次访问会默认带上cookie
#   都是键值对name value
"""
用法:得到响应对象 rep = render()或redirect或httpresponse
1,rep.set_cookies("xxx","yyy") 默认关闭浏览器失效
2,rep.set_signed_cookie("xxx","yyy",salt="zzz",max_age=10 单位是s) 设置加盐cookies
1,request.COOKIES['xxx'] 获取到
2,rep.get_signed_cookie("xxx",default="yy"取不到异常,加default,salt="zzz")

清除cookies:rep.delete_cookie("xxxx")

当多个页面需要校验cookies时
使用装饰器
def cookies(fun):
  @wraps(fun) 修复注释,文档名等
  def inner(request,*args,**kwargs):
      if not request.get_singed_cookies.get("xxx",default="yyy",salt=""):
          return render()
      else:
          跳转登录,   跳转后应该再次到请求的页面,不能固定,写死
          1,获取请求的url:Request.path_info
          redirect("..html/?next={}".format(url))   在对应方法中取出next值,动态跳转即可
      ret = fun(request,*args,**kwargs)
      return ret
  return inner
request.get_full_path:全路径带参数
request.path_info:不带参数
HTML中 action={{request.get_full_path}} 跳转到url中的路径   action 为空的时候默认跳转到当前url
action 要么不写要么 使用{{}}
expire= 针对ie的超时参数
path="/" 生效路径 默认是"/"
domain= 生效域名
source=False https传输
httponly=False 只能使用http协议传输,无法被javascript获取,不是绝对的,抓包可以修改

最大4kb
"""
# =============================================
# session 它依赖于cookie
#   cookies 键值保存在客户端
#   session中的键值保存在服务器,通过sessionid连接,保存在cookie中
#   Django session 存
#       1,生成字符串
#       2,生成大字典对应1中字符串
#       生成的数据是在数据库中的 django_session session_key session_date(加密过得) expire_date session默认两周
#       3,返回给cookie 浏览器
#   取
#   1,从cookie中找到字符串
#   2,找到的大字典
#   3,大字典取值
""""
request.session['k1']
request.session.get('k1',None)
request.session.set('k1',"123") 存在则不设置
del request.session['k1']
request.session.delete() 删除所有session中的值,没神魔用,会使cookiezhaobudao对应值,等于无效
request.session.flush() session,cookie都删除

request.session.clear_expired() 数据库中的session记录不会自动删除,本语句就是删除过期的session
request.session.exists("key") 判断key存在
request.session.set_expiry(整数秒,datatime或timedelta,0表示关闭失效,None settings中设置) 失效的是cookie

"""
# settings中配置全局session信息
# 默认引擎
SESSION_ENGINE="django.contrib.session.backends.db"
# 缓存session
SESSION_ENGINE = "django.contrib.session.backends.cache"
SESSION_CACHE_ALLAS="default" # 使用的缓存别名默认内存缓存,也可memcache
# 文件Session
SESSION_ENGINE = "django.contrib.session.backends.file"
SESSION_FILE_PATH=None # 若为None tempfile.gettempdir获取临时地址
# 缓存加数据库
SESSION_ENGINE="django.contrib.session.backends.cached_db"
# 加密Cookie Session 加密的cookies 相当于没有使用session
SESSION_ENGINE="django.contrib.session.backends.singed_cookies"

# 其他设置
SESSION_COOKIE="sessionid"
SESSION_COOKIE_PATH="/"
SESSION_COOKIE_DOMAIN=None
SESSION_COOKIE_SECURE=False
SESSION_COOKIE_HTTPONLY=True
SESSION_COOKIE_AGE=1209600 # 2 WEEK
SESSION_EXPIRE_AT_BROWSER_CLOSE=False # 浏览器关闭清除Session
SESSION_SAVE_EVERY_REQUEST=False #每次请求后更改SESSION
# ==========================================
# cvb(类中get) 使用装饰器
#   get,post (self,request) 装饰器中第一个参数是request,不匹配
#   解决方案:form django.utils.decorators import method_decorator
#   get或post方法上 @method_decorator(自定义的装饰器) 也可以加在类上 但要指明方法
#   @method_decorator(自定义的装饰器,name="get")
# 注意:其实在cvb中有dispatch方法
"""
  def dispatch(self,request,*args,**kwargs): 用来选择是get请求还是post请求
      return super(本类名,self).dispatch(request,*args,**kwargs)
      若在此上边加,表示get,post都加装饰器
"""
# 补充 csfr token
# 对于cvb中
"""
  from django.views.decorator.csrf import csrf_protect,csrf_exempt
  @method_decorator(csfr_protect) 即时sttting中注释了,不验证跨站请求,加了本装饰器,也会验证
  @method_decorator(csfr_exempt) 即时sttting中验证跨站请求,加了本装饰器,也不会验证,即取消跨站验证
  只能放在cvb的dispatch方法上
   
"""

8_Django 中的html javascript

# json (javascript object Notation)
# 是javascript中的对象类型
# javascript 数字,字符串,布尔,数组,对象,null
# python 整形浮点,字符串,布尔,列表,字典,None 因此python不能json对象
# 二者通过json字符串转化
# json注意事项: 1,key必须双引号,
#              2,不能16进制,
#              3,不能undefined,
#              4,value不能是函数,或日起对象
# ====================================================
# JQuery 发ajax请求
"""
$.ajax(
    {
        url:"xxx",
        type:"get"
        data:{字典}
        success:function(data){
        }
    }
)
ajax 发送跨站请求时
    方法1:要把{{csrf_token}} 的值取到拼接到发送的数据中
    方法2:引入jquery.cookie.js
        type下多个headers:{"X-CSFRToken":$.cookie("csfrtoken")},
    方法3:存入js文件,发ajax前引入,发送步骤与普通相同,不用再额外添加
    function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) === (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
  // these HTTP methods do not require CSRF protection
  return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}

$.ajaxSetup({
  beforeSend: function (xhr, settings) {
    if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
      xhr.setRequestHeader("X-CSRFToken", csrftoken);
    }
  }
});
"""
# ==============================================
# 对象的序列化 需要转为字典 然后变为json字符串,
# from django.core import serializers
#   s= serializers.serializer("json",数据库查询的对象列表)
# ========================================
# sweetalert 插件,漂亮的弹出框
"""
1,swal("title","content","css") 
css:success,warning,info,error
2,
 swal({
          title: "你确定要删除吗?",
          text: "一旦删除就找不回来了",
          type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-warning",
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          closeOnConfirm: false,
          showLoaderOnConfirm: true //若删除耗时,显示加载状态
        },
        function(){
            // 向后端发送删除的请求
            $.ajax({
                url: "/delete/",
                type: "post",
                datatype:"json", //返回值的格式,回调函数中,按照此类型解析,不写就是字符串类型
                traditional:True,// 为true,阻止深度序列化,不懂什么意思,反正就可以传list了
                data: {"id":delId,"list":[1,2,3]}, 接受的时候使用getlist接受,但不能是双层列表 
                success:function (arg) {
                    swal(arg, "你可以跑路了!", "success");
                    $trEle.remove();
                }
            });
            
        }
    );
"""
# ==================================================
# Django 的form组件
# 第一步:定义class
# from django import  forms
# from django.forms import  widgets
# class RegForm(forms.Form):
#     name = forms.CharField(max_length=16,label="用户名") 校验规则
#     pwd = forms.CharField(
#     label="密码",
#     widget = widgets.PasswordInput(attr={'class':'xx'},render_value=True), //表示为密码类型,切验证后密码还存在 html相关
#     min_length=6,
#     error_message={"required":"不能为空","invalid":"格式错误","min_length":"最短6位"}
#     )

# def fun(request):
#     form_obj = RegForm()
#     if reauest.method=="post":
#         form_obj=RegForm(request.POST)
#         if form_obj.is_valid :存入数据库
#               name = form_obj.cleanned_data.get("name") 经过验证的数据
#               先从字典中去掉无用字符串pop("xxx")这种不报错
#               .create(**form_obj.cleanned_data) 但要注意名字与数据库中列相同
#     return render(request,"xxx.html",{"html":form_obj})
# 第二部,html中调用属性
# <form  novalidate> 关闭浏览器校验
# {{csrf_token}}
# {{form_obj.as_p}} 每个都用p标签包裹
# {{form_obj.errors.pwd}}
# <p><input type="submit" /></p>
# </form>
# 或在form中自己写
"""<div>
{{form_obj.name.label}}
{{form_obj.name}}
<span>{{form_obj.name.errors.0}}</span>
</div>这种,只不过每个都要自己写

服务器端校验,通常是request中获取值,再返回,比较麻烦
django 校验后还可以保留信息
其他组件
单选按钮:forms.fields.ChoiceField(
        choices=((1,"男"),(2,"女")),
        label="性别",
        initial=3,默认是3选中
        widget = forms.widgets.RadioSelect
    )
单选框,修改为widget = forms.widgets.Select 就是下拉菜单
多选框forms.MutipleChoiceField
        initial=[1,3]
        widget = forms.widgets.SelectMultiple  下拉菜单全部显示的那种
单选框,记住密码那种:forms.fields.ChoiceField
            widget = forms.widgets.CheckboxInput
            initial="checked"
多选框,
forms.fields.MultipleChoiceField(
        choices=((1,"男"),(2,"女")),
        label="爱好",
        initial=[1,3],默认是3选中
        widget = forms.widgets.CheckBoxSelectMultiple
    )
"""
"""
自定义校验规则
from django.core.validators import RegexValidator
Charfield(
    加上字段validators=[ 正则列表
    RegexValidator(r"[0-9]+$","请输入数字"),
    RegexValidator(r"^1[3-9][0-9]{9}$","159开头"),
    ]
)
在类中定义方法
def clean_name(self): 名字固定 clean_列名
    value = self.cleanned_data.get("校验的列名 如name")
    if "非法字符" in value:
        raise ValidationError("含有非法字符")
    return value
def  clean(self): 重写父类clean方法,父类的方法默认什么都没干
    pwd = self.cleaned_date.get("pwd")
    ...
    if xx==yy
        self.add_error("字段",ValidatorError("不一致") )  应该在那里显示呢?
        raise ValidatorError("不一致")
    return self.cleaned_data
    
reg_pwd  {{ form_obj.errors}}
"""
# =====================多选框从数据库取值
forms.CharField(
    choices=models.xxx.objects.all().value_list("id",....)
    #此时虽然可以获取,但是静态字段,若数据库增加,不重启服务器,新的数据不能获取
)
需要从写from的__init__方法
def __init__(self,*args,**kwargs):
    super().__init__(*args,**kwargs)
    self.fields['那一列'].widget.choices=models......
内置字段:等待添加....

9_ 中间件

https://www.cnblogs.com/liwenzhou/

 

# importlib的使用
import importlib
o = importlib.import_module("模块字符串")
# 这样即可导入模块 ,其实是通过反射

# 要实现权限验证,如登陆后访问,原来是装饰器,但若函数过多...
# 中间件:官方说是 用来处理Django的请求和响应的框架级级别的钩子,轻量级
#   全局的,慎用,使用不当,影响性能 说白了就是在执行urls.py前后,执行某些方法
# setting  MIDDLEWARE   定义后加入列表即可
# 固定方法 其实中间件相当于过滤器
from django.utils.deprecation import MiddlewareMixin
class A(MiddlewareMixin):
    # 先request,次view 最后response
    def process_request(self,request):pass
    # 按照setting中的顺序,请求时 123 响应是 321 是反向
    # 返回None 继续执行url,若有return httpresponse 则直接返回
    def process_request(self,request,response):# 必须要两个参数,且必须返回值,要么自定义返回 ,或返回视图的response
        pass
    def process_view(self,request,view_func,view_args,view_kwargs):
        """
        :param request: 请求
        :param view_func: 要执行的函数名字
        :param view_args: 位置参数
        :param view_kwargs: 关键字参数
        :return:
        在找到urls.py前执行,按顺序执行,
        """
    def process_exception(self):pass
    # 只有在视图函数中出现异常的时候执行, 也是倒序
    # 返回None,继续执行其他中间件的exception
    # HttpResponse,跳过其他中间件的exception ,通常是视图页面错误,既不能正常返回,在此定义返回页面
    def process_template_response(self,request,response):pass
    #  倒序执行, 在视图函数执行完毕 但没有返回render()之前
    # 返回Noneh或HttpResponse
    # 因此返回的对象中必须存在render()方法,然后会执行render()方法,名字必须为render(),内容可自定义



    # request列表没执行完毕,某一个request返回了响应,则直接跳到该中间件的response
    # request列表执行完毕,会继续第一个中间件的view --->到最后一个view
    #   若view列表没执行完毕,某一个返回响应了,后边的view不执行,  跳到第一个response(返回时候的第一个res) (urls中的view不执行)
    #   若view列表执行完毕,执行urls view ,然后response列表
#
#     1,process_request
#         urls.py
#     2,process_view
#         view
#     3,有异常process_exception
#     4,若视图函数返回对象有render()方法,执行
#     5,process_response
#
# day74
# 2018 - 05 - 21
#
# 课程安排
# 周一:
# 中间件
# auth模块 + 分析BBS项目需求(小组讨论把表结构设计出来)
#
#
# 1.
# 今日内容
# 中间件:http: // www.cnblogs.com / liwenzhou / p / 8761803.
# html
#
# 1.
# URL的白名单
# url = ["/xx/", "/oo/", "/haha/"]
# 2.
# 登陆之后才能访问某些URL
# 之前使用装饰器
#
# 中间件的定义:
# wsgi之后
# urls.py之前
# 在全局
# 操作Django请求和响应的模块!
#
# 中间件的使用:
# 5
# 个固定的方法
# process_request(self, request)
# 执行顺序:
# 按照注册的顺序(在settings.py里面设置中
# 从上到下的顺序)
# 何时执行:
# 请求从wsgi拿到之后
# 返回值:
# 返回None,继续执行后续的中间件的process_request方法
# 返回response, 不执行后续的中间件的process_request方法
#
# process_response
# 执行顺序:
# 按照注册顺序的倒序(在settings.py里面设置中
# 从下到上的顺序)
# 何时执行:
# 请求有响应的时候
# 返回值:
# 必须返回一个response对象
#
# process_view(self, request, view_func, view_args, view_kwargs):
# 执行顺序:
# 按照注册的顺序(在settings.py里面设置中
# 从上到下的顺序)
# 何时执行:
# 在urls.py中找到对应关系之后
# 在执行真正的视图函数之前
# 返回值:
# 返回None,继续执行后续的中间件的process_view方法
# 返回response,
#
# process_exception(self, request, exception)
# 执行顺序:
# 按照注册顺序的倒序(在settings.py里面设置中
# 从下到上的顺序)
# 何时执行:
# 视图函数中抛出异常的时候才执行
# 返回值:
# 返回None, 继续执行后续中间件的process_exception
# 返回response,
#
#
#
# process_template_response(self, request, response)
# 执行顺序:
# 按照注册顺序的倒序(在settings.py里面设置中
# 从下到上的顺序)
# 何时执行:
# 视图函数执行完,在执行视图函数返回的响应对象的render方法之前
# 返回值:
# 返回None, 继续执行后续中间件的process_exception
# 返回response,
#
#
# Django调用
# 注册的中间件里面五个方法的顺序:
# 1.
# process_request
# urls.py
# 2.
# process_view
# view
# 3.
# 有异常就执行
# process_exception
# 4.
# 如果视图函数返回的响应对象有render方法, 就执行process_template_response
# 5.
# process_response
#
# Django已经学过的知识点:
# 1.
# Urls.py
# 路由系统:
#
# 正则
# 分组匹配 --> 位置参数
# 分组命名匹配 --> 关键字参数
#
# 分级路由
# include
#
# 给路由起别名
# name = "xx"
#
# 反向解析url
# view
# from django.urls import reverse
#
# reverse("xx", args=[1, 2, 3])
# reverse("xx", kwargs={”k
# ": "
# v
# "})
#
# 自取其辱
#
# 2.
# 视图
# views.py
# request
# request.method
# request.GET --> URL里面的参数
# request.POST --> post请求的数据
#
# request.path_info --> 路径
# request.get_full_path() --> 路径加路径的参数
#
# response
# 新手必备3件套
# render(request, "xx.html", {“k”: "v", ...})
# HttpResponse("响应")
# redirect("/index/")
# redirect("http://www.luffycity.com")
#
# from django.http import JsonResponse
#
# JsonResponse()
#
# FBV和CBV
#
# 函数装饰器和方法装饰器的区别
#
# 3.
# 模板
#
# filter
# 内置的filter方法
# 自定义的filter方法
#
# tag
# 内置的tag
# 自定义的simpleTag
# 自定义的inclusionTag
#
# 母版和继承
#
# { % extends ‘base.html’ %}
#
# { % block
# page - main %}
# { % block
# small %}
# { % endblock
# small %}
# { % endblock
# page - main %}
#
#
# 组件
# { % include
# nav %}
#
#
# 静态文件相关的tag
#
# 在模板语言里面反向解析url
#
# { % url
# 'url的别名'
# xx %}
#
#
# 4.
# ORM
#
# 对应关系
# 类 --> 数据表
# 对象 --> 数据行
# 属性 --> 字段
#
# Django连接MySQL数据库的步骤:
# 1.
# 手动创建库
# 2.
# 配置settings.py中数据库的连接信息
# 3.
# 修改settings.py同目录下的__init__.py文件,添加两句
# import pymysql
#
# pymysql.install_as_MySQLdb()
# 4.
# 在app下面的models.py中定义类,类一定要继承mdoels.Model
# 5.
# 执行两句命令
# 1.
# python
# manage.py
# makemigrations
# 2.
# python
# manage.py
# migrate
#
# 操作数据表
#
# 操作数据行(增删改查)
# 单表
# 外键
# 多对多
# 一对一
#
# ORM高级:
# 常用字段和方法
# 必知必会13条
# 神奇的双下划线
# 跨表的正向查询反向查询
#
# F和Q
#
# 聚合和分组
#
# 事务
#
# 执行原生的SQL语句
#
# 5.
# Cookie和Session, 分页
#
# 6.
# AJAX
#
# $.ajax({
#     url: “”,
# type: "post",
# data: {"k1": JSON.stringify([1, 2, 3])},
# success: function(data)
# {
#
# }
# })
# 7.
# form表单
#
# 8.
# 中间件

10_auth模块

# 当在html要获取当前用户信息的时候,通常是session存id
# 视图中查询到,传入html,存在问题:需要的页面都要获取,再传入
# 解决方案:自带的中间件,自带的倒数第三个 auth有关的
"""
python manage.py createsuperuser 向auth_user 添加用户 
from django.contrib import auth
  obj = auth.authenticate(username="xx",password="xx")
    将认证的用户放入request.user:
  auth.login(request,obj)根据cookie,将用户放入session, 检测当前用户,下次来的是会有请求中会放入user
  auth.lgout(request) 相当于request.session.flush()

from django.contrib.auth.decorators import login_required

需要登陆权限的视图 @login_requried() 装饰器 默认""
默认LOGIN_URL="/accounts/login/" 不存在 添加 修改即可  而且实现的登陆跳转

request.user.is_authenticated() 返回

from django.contrib.auth.models import User
    注意username不能重复
    User.Objects.create(...)  不能使用这个创建,创建后是明文密码
    User.Objects.create_superuser()
    User.Objects.create_user(....) 密文密码


    obj.check_password("密码") 检测密码正确与否
    obj.set_password("xxx") 修改密码
    obj.save() 修改后要保存

"""
# ====================================
# 扩展自带的表
# 1,models.OneToOneField(to=User)
# 2,继承自带的类,auth_user 不会创建了
"""
    from django.contrib.auth.models import User,AbstractUser
    继承后添加额外属性
    若使用本方法 sttings指定 AUTH_USER_MODELS='appname.类名'
"""
python manager.py createsuperuser

 

posted @ 2019-10-12 14:01  下雨天,真好  阅读(257)  评论(0编辑  收藏  举报