【Django】Django笔记

1. 创建/运行项目

shell 复制代码
# 安装
pip install django==3.2
# 创建项目
django-admin startproject [项目名称]
# 进入项目目录
cd [项目目录]
# 创建app(项目目录下)
python manage.py startapp [app名称]
# 启动项目(项目目录下)
python manage.py runserver [默认127.0.0.1:8080]

2. 项目分支

python 复制代码
mysite
├── manage.py					【项目的管理工具】
├── mysite
│   ├── __init__.py				【若import mysite则导入的是该文件】
│   ├── asgi.py					【异步】
│   ├── settings.py				【部分配置文件。程序启动时,先读取django内部配置,再读settings.py】
│   ├── urls.py					【主路由,在里面编写  /xxx/xxx/xxx ---> index 】
│   └── wsgi.py					【同步,主】  项目上线使用的是uwsgi,性能更高
└── web
    ├── __init__.py
    ├── admin.py			
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py				【ORM,基于models可以对数据库进行简便的操作】
    ├── tests.py
    └── views.py				【视图函数】

django内部配置文件是 [python目录]\Lib\site-packages\django\conf\global_settings.py

注意:以后创建项目时,不要一直用系统解释器,为每个项目:虚拟环境+项目文件

3. 虚拟环境

  • venv,Python官方用于创建虚拟环境的工具
python 复制代码
cd xxx/xxx/crm
python -m venv [虚拟环境名称]  # 创建了一个对应python版本的虚拟环境
  • virtualenv【推荐】
shell 复制代码
pip install virtualenv
shell 复制代码
cd xxx/xx/
virtualenv [虚拟环境名称] --python=python3.10

3.1 命令行创建

  • D:\envs\创建虚拟环境

    shell 复制代码
    cd D:\envs
    virtualenv [虚拟环境目录] --python=python3.10   # 使用virtualenv指定创建Python3.10版本的虚拟环境
  • 激活虚拟环境

    • win

      shell 复制代码
      cd D:\envs\[虚拟环境目录]\Scripts
      activate	# 激活
      (crm) D:\envs\[虚拟环境目录]\Scripts>    # 激活后
    • mac

      shell 复制代码
      source /[虚拟环境目录]/bin/activate
  • 安装包

    shell 复制代码
    (crm) D:\envs\[虚拟环境目录]\Scripts>pip install [包名]    # 将包安装在此虚拟环境中
  • 在虚拟环境下创建django项目 D:\project\python\Practice

    shell 复制代码
    (crm) cd D:\project\python\Practice
    (crm) djanbo-admin startproject crm
    (crm) python manage.py startapp xxx
    (crm) python manage.py runserver
  • 退出虚拟环境

    shell 复制代码
    deactivate

3.2 Pycharm创建

3.2.1 Django+虚拟环境【最新版本Django】
复制代码
pip install django

注意:创建django最新版可以。

3.2.2 Django+虚拟环境【指定版本】


shell 复制代码
pip install django==3.2
django-admin startproject [项目名] .   # 这样把项目直接创建在.venv同级目录。     不加.则多一个文件夹包裹着

配置Django Server


4. 创建app

  • 项目只需要一个app,目录结构建议如下

  • 项目需要多个app,目录结构建议如下

    复制代码
    django_practice
    	.venv
    	django_practice
    		...
    		...
    	manage.py
    	apps
    		web
    		backend
    		api

4.1 多app创建方法

5. 纯净版Django

6. 打开项目/导出项目

  • 给别人项目 即代码+requirements.txt

    shell 复制代码
    pip freeze > requirements.txt  # 导出依赖,放在项目目录下
    # 压缩文件,排除虚拟环境。
  • 要一个项目

    shell 复制代码
    # 1.创建虚拟环境+项目和环境关联
    pip install -r requirements.txt  # 2.安装依赖
    # 3.运行项目

7.路由系统

本质上:URL和函数的对应关系。

7.1 传统的路由

python 复制代码
from django.contrib import admin
from django.urls import path
from apps.web import views

urlpatterns = [
    path('home/', views.home),
    path('news/<int:nid>/edit/', views.news),
    path('article/', views.article),
]
python 复制代码
from django.shortcuts import render, HttpResponse


def home(request):
    return HttpResponse("成功")


def news(request, nid):   # 会将动态的<int:nid>传值给函数中的参数nid
    print(nid)
    page = request.GET.get("page")   # 会将?后面的参数传值给request.GET
    return HttpResponse("新闻")


def article(request):
    nid = request.GET.get("nid")   # 会将?后面的参数传值给request.GET
    print(nid)
    return HttpResponse("文章")
  • int,整数
  • str,字符串 /
  • slug,字母+数字+下滑线+ -
  • uuid,uuid格式
  • path,路径,可以包含 /

7.2 正则表达式路由

  • 在django1版本用的多。
  • 在django2+版本用的少

7.3 路由分发

7.3.1 include分发

inlucde + app(一般情况),将功能拆分不到不同的app中。

7.3.2 手动路由分发

手动路由分发,可以与app无关。

python 复制代码
path('user/add/', views.login),
path('user/delete/', views.login),
path('user/edit/', views.login),
path('user/list/', views.login),


# 同样前缀,可以提取为这种形式
path('user/', ([
                   path('add/', views.login),
                   path('delete/', views.login),   # /user/delete/
                   path('edit/', views.login),
                   path('list/', views.login),
               ], None, None)),  

纯粹帮助提取功能的URL,防止重复编写。
7.3.3 路由分发的本质
  • URL对应函数

    python 复制代码
    path('user/add/', views.login),
  • URL对应元组

    复制代码
    path('user/add/',    (元素,appname元素,namespance元素)    ),
    python 复制代码
    path('user/add/',    include("apps.api.urls")    ),
    path('user/add/',     ([],None,None)     ),
7.3.4导入模块与反射
python 复制代码
# Python中的知识点
path = "app.api.urls"
import importlib

md = importlib.import_module(path)   # 相当于from app.api import urls    导入模块
v1 = getattr(md, "url_patterns")  # 相当于md.url_patterns                反射机制
print(v1)
7.3.5 实际应用场景
python 复制代码
# 常规罗列
path('user/add/', views.login),
path('user/delete/', views.login),
path('user/edit/', views.login),
path('user/list/', views.login),

# 手动路由分发
path('user/', ([
                   path('add/', views.login),
                   path('delete/', views.login),   # /user/delete/
                   path('edit/', views.login),
                   path('list/', views.login),
               ], None, None)),
               
# 手动路由分发
path('user/', include(([
                           path('add/', views.login),
                           path('delete/', views.login),  # /user/delete/
                           path('edit/', views.login),
                           path('list/', views.login),
                       ], None))),

# include分发
include("apps.api.urls")  # 一般是每个app中urls
urlpatterns = [

]

7.4 name

给一个路由起个名字 + 根据名字反向生成URL。

python 复制代码
urlpatterns = [
    path('login/', views.login),
]
python 复制代码
# 很多功能,很多URL
urlpatterns = [
    path('login/', views.login, name="v1"),
    path('auth/', views.auth, name="v2"),
]

有了名字后,以后一般有两处会用到:

  • 在视图函数中生成URL

    复制代码
    from django.urls import reverse
    url = reverse("v2")   # /auth/
    url = reverse("v1")   # /login/
  • HTML模板,页面上有一个a标签,添加xx。

    html 复制代码
    <a href="/xxx/xxx/xx/">添加</a>
    html 复制代码
    <a href="{% url 'v1' %}">添加</a>
    <a href="{% url 'v2' %}">添加</a>
  • 扩展

    复制代码
    以后做权限管理,让name属性配合。

基于reverse反向生成url:


7.5 namespace

意义:reverse反向生成url时使得相同的name对应不同的url。

  • 主路由

    python 复制代码
    from django.urls import path, re_path, include
    
    # 很多功能,很多URL
    urlpatterns = [
        path('api/', include("apps.api.urls", namespace='x1')),
        path('web/', include("apps.web.urls", namespace='x2')),
    ]
  • api/urls.py

    python 复制代码
    from django.urls import path, re_path
    from . import views
    # 很多功能,很多URL
    urlpatterns = [
        path('login/', views.login, name="login"),
        path('auth/', views.auth, name='auth'),
    ]
  • web/urls.py

    python 复制代码
    from django.urls import path, re_path
    from . import views
    # 很多功能,很多URL
    urlpatterns = [
        path('home/', views.home, name='home'),
        path('order/', views.order, name='order'),
        path('auth/', views.order, name='auth'),
    ]

以后在某个URL或者视图中反向生成:

python 复制代码
from django.urls import reverse
url = reverse("x1:login")    # /api/login/
url = reverse("x1:order")    # /web/login/

url = reverse("x1:auth")    # /api/login/
url = reverse("x2:auth")    # /web/login/
7.5.1 include分发路由

在使用namespace的情况下需要设置app_name,否则报错

python 复制代码
#  项目的urls.py文件中使用了namespace
urlpatterns = [
    path('api/', include("apps.api.urls", namespace='x1')),
]
python 复制代码
#  在apps/api/urls.py文件中
from django.urls import path, re_path
from apps.api import views

urlpatterns = [
    path('login/', views.login, name="login"),
    path('auth/', views.auth, name='auth'),
]

app_name = "api"  # 这里若不设置app_name会报错
7.5.2 手动分发路由
python 复制代码
urlpattern = [
    path('api/', (
    	[
            path('login/', views.login, name="login"),
            path('auth/', views.auth, name="auth"),
        ], 'api', 'x1')),  # 元组中第二个参数为app_name, 第三个参数为namespace。开发中一般会让app_name和namespace同名
]
  • 关于namespace多层嵌套。使用reverse反向生成url时,需要将所有的namespace从外到内全都加进去。如示例x1:yy:xx

7.6 最后的 / 如何解决?

settings中配置 APPEND_SLASH = True(默认为True)

python 复制代码
path('login/', views.login),
	http://127.0.0.1:8000/login/   成功

	http://127.0.0.1:8000/login    django,重定向301
	http://127.0.0.1:8000/login/   成功
python 复制代码
path('login', views.login),
	http://127.0.0.1:8000/login    成功

	http://127.0.0.1:8000/login    
	http://127.0.0.1:8000/login/   失败

APPEND_SLASH = False

python 复制代码
path('login/', views.login),
	http://127.0.0.1:8000/login/   成功

	http://127.0.0.1:8000/login    失败
python 复制代码
path('login', views.login),
	http://127.0.0.1:8000/login/   失败

	http://127.0.0.1:8000/login    成功

所以推荐 settings配置文件中不需要添加内容,但是在路由中路径后添加 / 以便于兼容用户访问时不添加 / 的情况

7.7 当前匹配对象

python 复制代码
request.resolver_match

有什么用呀?

复制代码
某用户,具有一些权限。   permissions = ["xx","login",'account']
某用户,具有一些权限。   permissions = ["login",'account']

补充:关于 partial

补充partial知识点(与django路由无关,看源码时有关)

python 复制代码
def _xx(a1, a2):
    return a1 + a2


data = _xx(11, 22)
print(data)
python 复制代码
from functools import partial

def _xx(a1, a2):
    return a1 + a2

yy = partial(_xx, a2=100)

data = yy(2)
print(data)

8. 视图

8.1 形式(文件 / 文件夹)

文件形式:创建项目 / app自带的views.py

文件夹形式:若视图中函数较多,不适合放在同一视图文件中,则需要删除views.py文件,创建views文件夹,在文件夹中创建不同名称的视图文件,而后只需要在urls.py中导入不同的视图文件即可。

8.2 相对和绝对导入urls

注意实现:不要在项目根目录做相对导入。

原则:

  • 绝对导入(推荐)
  • 相对导入(层级深才用)

8.3 视图参数

python 复制代码
urlpatterns = [
    path('login/', account.login, name="login"),
    path('auth/', order.auth, name='auth'),
]
python 复制代码
from django.shortcuts import HttpResponse


def login(request):
    return HttpResponse("login")

requests是什么呢?

复制代码
对象,包裹,可以放很多东西。

requests是一个对象,存放了浏览器给咱们发过来的所有内容,所以含有:
- 请求相关所有的数据: 当前访问的url、请求方式、...
- django额外添加的数据
python 复制代码
from django.shortcuts import HttpResponse


def login(request):
    # 1.当前URL  /api/login/
    print(request.path_info)

    # 2.URL传递的参数
    print(request.GET)
    print(request.GET.get("age"))  # 获取get请求中的age参数

    # 3.请求方式  GET/POST
    print(request.method)

    # 4.如果post请求,传递请求体(原始数据)
    print(request.body)  
    # b'{"code":"083Sjmll2yla694F3bll2DguCM2SjmlG","unionId":"oP6QCsyT_9bk1dfSaVf0GEV5Y-yE"}'  b'v1=123&v2=456'

    # 4.1 请求体+请求头  
    # 当请求体为b'v1=123&v2=456'这种格式,且content-type为application/x-www-form-urlencoded时,可以通过request.POST.get("v1")直接获取请求体中传递的参数值
    print(request.POST)
    print(request.POST.get("v1"))   
    print(request.POST.get("v2"))

    # 4.2 请求体+请求头   文件
    print(request.FILES)  # 文件格式           + multipart/form-data
    print(request.FILES.get("n1"))
    print(request.FILES.get("n2"))

    # 5.请求头
    # {'Content-Length': '', 'Content-Type': 'text/plain', 'Host': '127.0.0.1:8000', 'Connection': 'keep-alive', 'Cache-Control': 'max-age=0', 'Sec-Ch-Ua': '" Not A;Brand";v="99", "Chromium";v="102", "Google Chrome";v="102"', 'Sec-Ch-Ua-Mobile': '?0', 'Sec-Ch-Ua-Platform': '"macOS"', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36', 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Sec-Fetch-Site': 'none', 'Sec-Fetch-Mode': 'navigate', 'Sec-Fetch-User': '?1', 'Sec-Fetch-Dest': 'document', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7', 'Cookie': 'csrftoken=CdidpKSGbLxzmOXnbmlkvrZep1eJmKLAA81T73UjcjxEnMOa4YOZqtc849AkYfUy'}
    print(request.headers)

    # 5.1 请求头有个特殊的cookie  这是Django setttings配置中自带的
    # request.headers['cookie']  # 'csrftoken=CdidpKSGbLxzmOXnbmlkvrZep1eJmKLAA81T73UjcjxEnMOa4YOZqtc849AkYfUy;session=xxxx'
    # {'csrftoken': 'CdidpKSGbLxzmOXnbmlkvrZep1eJmKLAA81T73UjcjxEnMOa4YOZqtc849AkYfUy'}
    print(request.COOKIES)

    # 6.requests中其他值
    print(request.resolver_match)

    return HttpResponse("login")

8.4 返回值

  • HttpResponse
  • JsonResponse
  • render
  • redirect
python 复制代码
from django.shortcuts import HttpResponse, redirect, render
from django.http import JsonResponse


def auth(request):
    pass


def login(request):
    # 1.获取请求数据
    print(request)

    # 2.根据请求数据进行条件的判断 GET/POST   GET.get("xx")    POST.get("xx")

    # 3.返回数据

    # 3.1 字符串/字节/文本数据(图片验证码)
    return HttpResponse("login")

    # 3.2 JSON格式(前后端分离、app小程序后端、ajax请求)
    data_dict = {"status": True, 'data': [11, 22, 33]}
    return JsonResponse(data_dict)

    # 3.3 重定向
    return redirect("https://www.baidu.com")
    # return redirect("http://127.0.0.1:8000/api/auth/")
    # return redirect("http://127.0.0.1:8000/api/auth/")
    return redirect("/api/auth/")
    # return redirect("/api/auth/")  # name
    #
    from django.urls import reverse
    url = reverse("auth") 
    return redirect(url) 
    # return redirect("auth")  # 这里的auth代表路由中的name参数 不推荐使用

    # 3.4 渲染
    # - a.找到 'login.html' 并读取的内容,问题:去哪里找?
    # -   默认先去settings.TEMPLATES.DIRS指定的路径找。(公共)
    # -   按注册顺序每个已注册的app中找他templates目录,去这个目录中寻找'login.html'
    # -   一般情况下,原则,那个app中的的模板,去哪个那个app中寻找。
    # - b.渲染(替换)得到替换完成的字符串
    # - c.返回浏览器
    return render(request, 'api/login.html')

8.5 响应头

python 复制代码
from django.shortcuts import HttpResponse, redirect, render
from django.http import JsonResponse


def login(request):
    res = HttpResponse("login")
    res['xx1'] = "hahaha"
    res['xx2'] = "hahaha"
    res['xx3'] = "hahaha"

    res.set_cookie('k1',"aaaaaaaa")
    res.set_cookie('k2',"bbbbbb")

    return res

8.6 FBV和CBV

  • FBV,视图用函数的形式编写。(前后端不分离项目主流)
  • CBV,视图用类的形式编写。(DRF分离项目主流)

请注意,这一些都是表象,本质一模一样。

9.静态资源

静态资源:

  • 开发需要:css、js、图片。

    复制代码
    - 根目录的 /static/
    - 已经app目录下载 /static/ 文件夹下
  • 媒体文件:用户上传的数据(excel/pdf/video)

    复制代码
    - 根目录的 /media/   
    - 其他服务器

9.1 静态文件

python 复制代码
INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    "apps.api.apps.ApiConfig",
    "apps.web.apps.WebConfig",
]
...

STATIC_URL = '/static/'
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
  • 顺序:...

  • 多app开发:各自app的图片放在各自 /static/app名字/。。。

  • 在开发过程中

    • 禁止

      html 复制代码
      <img src="/static/api/1.png">
    • 建议

      html 复制代码
      {% load static %}
      
      <!DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      <h1>登录页面</h1>
      <a href="/xxx/xxxxx/">调换dao xx</a>
      <a href="{% url 'login' %}">跳转</a>
      
      <img src="{% static 'api/1.png' %}">
      
      </body>
      </html>

9.2 媒体文件

urls.py

python 复制代码
from django.contrib import admin
from django.urls import path, re_path, include
from django.conf.urls.static import static  # 导入
from django.conf import settings  # 导入

from apps.api import views



urlpatterns = [
    path('api/', include('apps.api.urls')),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)  # 添加


10.Git版本控制

详见【Git】Git实战笔记

11.中间件、ORM、缓存、session、cookie(全家桶)

复制代码
- 先看全家桶
- 笔记 https://www.cnblogs.com/wupeiqi/articles/6216618.html

12. 导入配置文件

python 复制代码
from django.conf import settings  # 会先导入全局的global_settings,再去导入用户的settings内容

13. 模板

13.1 寻找html模板

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

优先去项目根目录 > 每个已注册的app的templates目录找。

如何选择:

  • 简单的项目,模板都放在根目录。
  • 复杂的项目,模板放在各自的app中,公共部分放在templates目录。

扩展:修改内置app的模板也是同样的套路。

13.2 模板处理的本质

渲染完成后,生成了字符串,再返回给浏览器。这里如果alert("{``{n2}}")放在js文件中通过src属性引入的话,是不能被替换的。


13.3 常用语法

13.4 内置函数

在django模板语法中提供了内置函数。

13.5 自定义模板功能

自定义模板的三种方式:

  • filter

    复制代码
    - 数据处理,参数:1~2个
    - 数据处理,if条件
  • simple_tag

    复制代码
    参数无限制 & 返回文本
  • inclusion_tag

    复制代码
    参数无限制 & HTML片段

13.6 继承和模板

先继承,将全部内容拿到以后,再进行渲染。所以如果继承的模板中有占位符,也有传递参数


13.7 模板的导入

13.8 verbatim

python 复制代码
{% verbatim %}
     # 可将内容不被渲染
{% endverbatim %}

14. django中间件

  • 定义方法
  • 注册

14.1 原始方式

请求通过中间件进入,再从中间件出去,通过__call__回调函数体现

中间件注册:

14.2 MiddlewareMixin(建议)


注意:django1版本。

疑问:prcess_request的执行时,是否已执行了路由匹配?

request.resolver_match告诉我们答案。

注意:process_view是在django中源码中写死了。

流程(如下图) :这里请求发到Django,先要经过每一个中间件的process_request(如有),然后进行路由匹配,而后再从头执行每一个中间件的process_view方法(如有),而后将请求发到视图函数,后将视图函数中的response返回通过中间件的每一个process_response方法。其中如果md2的process_request直接返回响应,则直接进行md2的process_response,而后通过md1的process_response返回给客户端。如果md2的process_view直接返回响应,则直接从最后一个md3的process_response进入,依次通过md2、md1的process_response返回给客户端。

14.3 其他

process_exception

process_exception可以捕获视图函数中的异常,进而进行处理。它不是在response返回时调用的,而是在view执行过程中一旦抛出异常时立即调用。如果某个中间件的process_exception返回了一个 HttpResponse,异常处理链停止,这个HttpResponse继续往外传递,会像正常的 response一样,经过剩下的中间件的 process_response(反向执行),而不会再走process_exception


process_template_response
python 复制代码
# 视图函数view.py中
def user():
   	from django.template.response import TemplateResponse
	return TemplateResponse(request, 'app01/user.html')   # 返回这个对象可以触发中间件中的process_template_response方法

# 中间件md.py中
def process_template_response(self, request, response)
	return response

小结

  • 定义中间类
  • 类方法
    • process_request
    • process_view
    • process_reponse
    • process_exception,视图函数出现异常,自定义异常页面。
    • process_template_response,视图函数返回TemplateResponse对象 or 对象中含有.render方法。

应用场景

  • 用户登录,后续用户验证
  • 跨域请求,做添加响应头允许跨域访问(浏览器因为同源策略会阻止跨域请求,这时候需要服务器返回特定响应头允许跨域)

15. ORM操作

orm,关系对象映射,本质翻译的。

15.1 表结构

实现:创建表、修改表、删除表。

在app中的models.py中按照规则编写类 ===> 表结构。

  • 编写类

    python 复制代码
    from django.db import models
    
    
    class UserInfo(models.Model):
        name = models.CharField(max_length=16)
        age = models.IntegerField()
  • 注册app

    python 复制代码
    INSTALLED_APPS = [
        # 'django.contrib.admin',          # Django提供的后台
        # 'django.contrib.auth',           # 后台权限认证
        # 'django.contrib.contenttypes',   # 复杂表设计
        # 'django.contrib.sessions',       # 前后端不分离项目使用
        # 'django.contrib.messages',       # A页面跳转B页面 数据传递使用
        'django.contrib.staticfiles',
        'apps.web.apps.WebConfig',
        'apps.api.apps.ApiConfig',
    ]
  • 命令,django根据models中的类生成一个 对数据库操作的配置文件 => migrations

    复制代码
    python manage.py makemigrations
    • 哪个数据库?

    • 数据库账户和密码?

      DATABASES = {
      'default': {
      'ENGINE': 'django.db.backends.sqlite3',
      'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
      }
      }

常见问题:请不要再手动去修改数据的表结构 + 时刻保证 ORM和数据表是对应。

15.1.1 常见字段和参数
  • 字段

    复制代码
    CharField
    
    SmallIntegerField
    IntegerField
    BigIntegerField
    
    DateField
    DateTimeField
    
    BooleanField  -> 其实数据库不支持真假,根据SmallIntegerField创造出来出来。 0  1
    
    DecimalField  -> 精确的小数
  • 参数

    python 复制代码
    name = models.CharField(verbose_name="姓名", max_length=16)
    name = models.CharField(verbose_name="姓名", max_length=16, default="哈哈哈")
    
    # 经常查询,速度快(MySQL,https://www.bilibili.com/video/BV15R4y1b7y9)
    name = models.CharField(verbose_name="姓名", max_length=16, default="哈哈哈", null=True, blank=True, db_index=True)
    email = models.CharField(verbose_name="姓名", max_length=16, default="哈哈哈", null=True, blank=True, unique=True)
    
    # 在数据库存储时只能是:sh、bj (上海、北京一般用于页面显示中文)
    code = models.CharField(verbose_name="姓名", max_length=16, choices=(("sh", "上海"), ("bj", "北京")),default="sh")
    python 复制代码
    # 不用 max_length=16
    count = models.IntegerField(verbose_name="数量", default=1, null=True, blank=True, unique=True)
    code = models.IntegerField(verbose_name="性别",choices=((1, "男"), (2, "女")),default=1)
    python 复制代码
    register_date = models.DateField(verbose_name="注册时间", auto_now=True)
    python 复制代码
    amount = models.DecimalField(verbose_name="余额", max_digits=10, decimal_places=2)

示例:

python 复制代码
from django.db import models


class UserInfo(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=16, db_index=True)
    age = models.PositiveIntegerField(verbose_name="年龄")
    email = models.CharField(verbose_name="邮箱", max_length=128, unique=True)
    amount = models.DecimalField(verbose_name="余额", max_digits=10, decimal_places=2, default=0)
    register_date = models.DateField(verbose_name="注册时间", auto_now=True)


class Goods(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
    # detail = models.CharField(verbose_name="详细信息", max_length=255)
    detail = models.TextField(verbose_name="详细信息")
    price = models.PositiveIntegerField(verbose_name="价格")
    count = models.PositiveBigIntegerField(verbose_name="库存", default=0)
15.1.2 表关系
  • ORM - 表关系之一对多
  • ORM - 表关系之多对多

在男女表中的任何一张表中添加models.ManyToManyField,Django帮助生成第三张表,结果和上面手动添加第三张表一样。

注意:ManyToManyField生成的表字段只能id/bid/gid

小结

设计自己项目的业务时,理清楚表与表之间的关系。

强调:设计项目表结构:表名和字段都不要拼音。

15.2 基本操作

orm,关系对象映射。

复制代码
类      --> SQL -->     表
对象    --> SQL -->     数据

特点:开发效率高、执行效率低( 程序写的垃圾SQL )。

编写ORM操作的步骤:

  • settings.py,连接数据库

    python 复制代码
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': BASE_DIR / 'db.sqlite3',
        }
    }
  • settings.py,注册app

    复制代码
    INSTALLED_APP = [
    	...
    	"app01.apps.App01Config"
    ]
  • 编写models.类

    python 复制代码
    class UserInfo(models.Model):
        ....
        .....
  • 执行命令

    复制代码
    python manage.py makemigrations    # 找到所有已注册的app中的models.py中的类读取 -> migrations配置
    python manage.py migrate           # 读取已注册的app下的migrations配置 -> SQL语句  -> 同步数据库

15.3 连接数据库

python 复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}
python 复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'xxxxxxxx',  # 数据库名字
        'USER': 'root',
        'PASSWORD': 'root123',
        'HOST': '127.0.0.1',  # ip
        'PORT': 3306,
    }
}

项目连接MySQL:

  • 安装MySQL & 启动MySQL服务

  • 手动创建数据库

  • django的settings.py配置

    python 复制代码
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'xxxxxxxx',  # 数据库名字
            'USER': 'root',
            'PASSWORD': 'root123',
            'HOST': '127.0.0.1',  # ip
            'PORT': 3306,
        }
    }
  • 安装第三方组件

    • pymysql

      复制代码
      pip install pymysql
      
      项目根目录/项目名目录/__init__.py
      	import pymysql
      	pymysql.install_as_MySQLdb()
    • mysqlclient

      复制代码
      pip install mysqlclient
      
      电脑上先提前安装MySQL。

其他数据库:

python 复制代码
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': 5432,
    }
}

# 需要 pip install psycopg2
python 复制代码
DATABASES = {
	'default': {
        'ENGINE': 'django.db.backends.oracle',
        'NAME': "xxxx",  # 库名
        "USER": "xxxxx",  # 用户名
        "PASSWORD": "xxxxx",  # 密码
        "HOST": "127.0.0.1",  # ip
        "PORT": 1521,  # 端口
    }
}
# 需要 pip install cx-Oracle

15.4 连接池

django默认内置没有数据库连接池 。

复制代码
pymysql   -> 操作数据库
DBUtils   -> 连接池

https://pypi.org/project/django-db-connection-pool/

复制代码
pip install django-db-connection-pool
python 复制代码
DATABASES = {
    "default": {
        'ENGINE': 'dj_db_conn_pool.backends.mysql',
        'NAME': 'day04',  # 数据库名字
        'USER': 'root',
        'PASSWORD': 'root123',
        'HOST': '127.0.0.1',  # ip
        'PORT': 3306,
        'POOL_OPTIONS': {
            'POOL_SIZE': 10,  # 最小
            'MAX_OVERFLOW': 10,  # 在最小的基础上,还可以增加10个,即:最大20个。
            'RECYCLE': 24 * 60 * 60,  # 连接可以被重复用多久,超过会重新创建,-1表示永久。
            'TIMEOUT':30, # 池中没有连接最多等待的时间。
        }
    }
}

注意:组件django-db-connection-pool不是特别厉害。拿了另外一个支持SQLAchemy数据库连接池的组件。

15.5 多数据库

django支持项目连接多个数据库。

python 复制代码
DATABASES = {
    "default": {
        'ENGINE': 'dj_db_conn_pool.backends.mysql',
        'NAME': 'day05db',  # 数据库名字
        'USER': 'root',
        'PASSWORD': 'root123',
        'HOST': '127.0.0.1',  # ip
        'PORT': 3306,
        'POOL_OPTIONS': {
            'POOL_SIZE': 10,  # 最小
            'MAX_OVERFLOW': 10,  # 在最小的基础上,还可以增加10个,即:最大20个。
            'RECYCLE': 24 * 60 * 60,  # 连接可以被重复用多久,超过会重新创建,-1表示永久。
            'TIMEOUT': 30,  # 池中没有连接最多等待的时间。
        }
    },
    "bak": {
        'ENGINE': 'dj_db_conn_pool.backends.mysql',
        'NAME': 'day05bak',  # 数据库名字
        'USER': 'root',
        'PASSWORD': 'root123',
        'HOST': '127.0.0.1',  # ip
        'PORT': 3306,
        'POOL_OPTIONS': {
            'POOL_SIZE': 10,  # 最小
            'MAX_OVERFLOW': 10,  # 在最小的基础上,还可以增加10个,即:最大20个。
            'RECYCLE': 24 * 60 * 60,  # 连接可以被重复用多久,超过会重新创建,-1表示永久。
            'TIMEOUT': 30,  # 池中没有连接最多等待的时间。
        }
    },
}
15.5.1 读写分离
复制代码
192.168.1.2       default master   [写]
                  组件
192.168.2.12      bak slave    [读]
  • 生成数据库表

    复制代码
    python manage.py makemigrations    # 找到所有已注册的app中的models.py中的类读取 -> migrations配置
    
    python manage.py migrate
    python manage.py migrate --database=default
    python manage.py migrate --database=bak
  • 后续再进行开发时

    python 复制代码
    models.UserInfo.objects.using("default").create(title="武沛齐")
    
    models.UserInfo.objects.using("bak").all()
  • 编写router类,简化【后续再进行开发时】,帮助model操作找到对应的数据库

    python 复制代码
    class DemoRouter(object):
        def db_for_read(self, model, **hints):
            from django.db.models.options import Options
            # print(model._meta, type(model._meta))  # 通过打印type,知道是这个类django.db.models.options.Options, 可以看model._meta有什么属性
            print(model._meta.app_label)  # app01,可以通过这个方法判断调用model的app
            print(model._meta.model_name)  # userinfo,可以通过这个方法判断调用的model
            print(hints)  # {}
            return "bak"
    
        def db_for_write(self, model, **hints):
            return "default"
    python 复制代码
    #  settings配置文件中添加配置
    DATABASE_ROUTERS = ["utils.router.DemoRouter"]
    python 复制代码
    # views.py中则可以简化
    models.UserInfo.objects.create(title="张三")
    
    models.UserInfo.objects.all()
15.5.2 分库(多个app ->多数据库)

100张表,50表-A数据库【app02】;50表-B数据库【app02】。

  • app01/models

    python 复制代码
    from django.db import models
    
    
    class UserInfo(models.Model):
        title = models.CharField(verbose_name="标题", max_length=32)
  • app02/models

    python 复制代码
    from django.db import models
    
    
    class Role(models.Model):
        title = models.CharField(verbose_name="标题", max_length=32)
  • 命令

    python 复制代码
    python manage.py makemigrations
    shell 复制代码
    python manage.py migrate app01 --database=default  # app01的表要生成到default数据库中
    shell 复制代码
    python manage.py migrate app02 --database=bak  # app02的表要生成到bak数据库中
  • 读写操作

    python 复制代码
    from django.shortcuts import render, HttpResponse
    
    from app01 import models as m1
    from app02 import models as m2
    
    
    def index(request):
        # app01中的操作 -> default
        v1 = m1.UserInfo.objects.all()
        print(v1)
    
        # app02中的操作 -> bak
        v2 = m2.Role.objects.using('bak').all()
        print(v2)
        return HttpResponse("返回")
  • router配置,判断具体使用哪个数据库

15.5.3 分库(单app)

100张表,50表-A数据库;50表-B数据库。

python 复制代码
from django.shortcuts import render, HttpResponse

from app01 import models as m1


def index(request):
    # app01中的操作 -> default
    v1 = m1.UserInfo.objects.all()
    print(v1)

    # app01中的操作 -> bak
    v2 = m1.Role.objects.using('bak').all()
    print(v2)

    return HttpResponse("返回")
15.5.4 注意事项
  • 分库,表拆分到不用数据库。

    复制代码
    一定不要跨数据库做关联  -> django不支持
    
    怎么办?
    尽可能的将有关联的表放在一个库中。
  • 为什么表拆分到不同的库?

15.6 表关系

  • 单表

    python 复制代码
    class Role(models.Model):
        title = models.CharField(verbose_name="标题", max_length=32)
  • 一对多

  • 多对多

    如果关系表中只有3列。

    python 复制代码
    class Boy(models.Model):
        """
        1   杰森斯坦森
        2   汤普森
        """
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
        b = models.ManyToManyField(to="Girl")
    
    class Girl(models.Model):
        """
        1   ax
        2   yu
        """
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
    python 复制代码
    class Boy(models.Model):
        """
        1   杰森斯坦森
        2   汤普森
        """
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
        
    class Girl(models.Model):
        """
        1   ax
        2   yu
        """
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
        b = models.ManyToManyField(to="Boy")
    python 复制代码
    class Boy(models.Model):
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
    
    
    class Girl(models.Model):
        name = models.CharField(verbose_name="标题", max_length=32, unique=True)
    
    
    class B2G(models.Model):
        bid = models.ForeignKey(to="Boy", on_delete=models.CASCADE)
        gid = models.ForeignKey(to="Girl", on_delete=models.CASCADE)
        address = models.CharField(verbose_name="地点", max_length=32)
  • 一对一

    复制代码
    表,100列     ->  50A表      50B表
    
    博客园为例:
    	- 注册,用户名、密码,无法创建博客
    	- 开通博客  地址/
python 复制代码
models.OneToOneField()  # 实际上是使用外键Foreign Key+唯一索引Unique实现的

15.7 数据操作

15.7.1 单表
python 复制代码
class Role(models.Model):
    title = models.CharField(verbose_name="标题", max_length=32)
python 复制代码
# obj1 = models.Role.objects.create(title="管理员", od=1)
# obj2 = models.Role.objects.create(**{"title": "管理员", "od": 1})

# 内存 -> save
# obj = models.Role(title="客户", od=1)
# obj.od = 100
# obj.save()

# obj = models.Role(**{"title": "管理员", "od": 1})
# obj.od = 100
# obj.save()
python 复制代码
# models.Role.objects.all().delete()
models.Role.objects.filter(title="管理员").delete()
python 复制代码
models.Role.objects.all().update(od=99)
models.Role.objects.filter(id=7).update(od=99, title="管理员")
models.Role.objects.filter(id=7).update(**{"od": 99, "title": "管理员"})
python 复制代码
# QuerySet = [obj, obj]
v1 = models.Role.objects.all()
for obj in v1:
    print(obj, obj.id, obj.title, obj.od)

# QuerySet = []
# v2 = models.Role.objects.filter(od=99, id=99)
v2 = models.Role.objects.filter(**{"od": 99, "id": 99})
for obj in v2:
    print(obj, obj.id, obj.title, obj.od)
    

v3 = models.Role.objects.filter(id=99)
print(v3.query)

v3 = models.Role.objects.filter(id__gt=2)
print(v3.query)

v3 = models.Role.objects.filter(id__gte=2)
print(v3.query)

v3 = models.Role.objects.filter(id__lt=2)
print(v3.query)

v3 = models.Role.objects.filter(id__in=[11, 22, 33])
print(v3.query)

v3 = models.Role.objects.filter(title__contains="户")
print(v3.query)

v3 = models.Role.objects.filter(title__startswith="户")
print(v3.query)

v3 = models.Role.objects.filter(title__isnull=True)
print(v3.query)
python 复制代码
v3 = models.Role.objects.filter(id=99)
print(v3.query)
# 不等于
v3 = models.Role.objects.exclude(id=99).filter(od=88)
print(v3.query)
python 复制代码
# queryset=[obj,obj]
v3 = models.Role.objects.filter(id=99)

# queryset=[{'id': 6, 'title': '客户'}, {'id': 7, 'title': '客户'}]
v4 = models.Role.objects.filter(id__gt=0).values("id", 'title')

# QuerySet = [(6, '客户'), (7, '客户')]
v5 = models.Role.objects.filter(id__gt=0).values_list("id", 'title')
print(v5[0])
python 复制代码
v6 = models.Role.objects.filter(id__gt=0).first()
# print(v6)  # 对象

v7 = models.Role.objects.filter(id__gt=10).exists()
print(v7)  # True/False
python 复制代码
# asc
v8 = models.Role.objects.filter(id__gt=0).order_by("id")

# id desc  od asc
v9 = models.Role.objects.filter(id__gt=0).order_by("-id", 'od')
15.7.2 一对多
python 复制代码
class Depart(models.Model):
    """ 部门 """
    title = models.CharField(verbose_name="标题", max_length=32)


class Admin(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32)
    pwd = models.CharField(verbose_name="密码", max_length=32)

    depart = models.ForeignKey(verbose_name="部门", to="Depart", on_delete=models.CASCADE)
python 复制代码
models.Admin.objects.create(name='武沛齐1', pwd='123123123', depart_id=2)
# models.Admin.objects.create(**{..})

obj = models.Depart.objects.filter(id=2).first()
models.Admin.objects.create(name='武沛齐2', pwd='123123123', depart=obj)
models.Admin.objects.create(name='武沛齐2', pwd='123123123', depart_id=obj.id)
python 复制代码
# filter()   # 当前表的字段 + depart__字段    -> 连表和条件

# 找到部门id=3的所有的员工,删除
# models.Admin.objects.filter(depart_id=3).delete()

# 删除销售部的所有员工
# obj = models.Depart.objects.filter(title="销售部").first()
# models.Admin.objects.filter(depart_id=obj.id).delete()

# models.Admin.objects.filter(depart__title="销售部", name='武沛齐').delete()
python 复制代码
# 1. select * from admin    					queryset=[obj,obj,]
v1 = models.Admin.objects.filter(id__gt=0)
for obj in v1:
    print(obj.name, obj.pwd, obj.id, obj.depart_id)

# 2. select * from admin inner join depart      queryset=[obj,obj,]
v2 = models.Admin.objects.filter(id__gt=0).select_related("depart")  # select_related中放模型中定义的外键字段名
for obj in v2:
    print(obj.name, obj.pwd, obj.id, obj.depart_id, obj.depart.title)

# 3. select id,name.. from admin inner join depart      queryset=[{},{}]
v3 = models.Admin.objects.filter(id__gt=0).values("id", 'name', 'pwd', "depart__title")
print(v3)

# 4. select id,name.. from admin inner join depart      queryset=[(),()]
v4 = models.Admin.objects.filter(id__gt=0).values_list("id", 'name', 'pwd', "depart__title")
print(v4)
python 复制代码
# 查询
# models.Admin.objects.filter(id=2).update(name='xxx', pwd='xxxx')
# models.Admin.objects.filter(name="武沛齐").update(depart_id=2)

# models.Admin.objects.filter(id=2).update(depart__title="技术部")  -> 只能更新自己表字段
15.7.3 多对多
python 复制代码
from django.db import models


class Boy(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32, db_index=True)


class Girl(models.Model):
    name = models.CharField(verbose_name="姓名", max_length=32, db_index=True)


class B2G(models.Model):
    bid = models.ForeignKey(to="Boy", on_delete=models.CASCADE)
    gid = models.ForeignKey(to="Girl", on_delete=models.CASCADE)
    address = models.CharField(verbose_name="地点", max_length=32)
python 复制代码
def index(request):
    # models.Boy.objects.create(name="宝强")
    # models.Boy.objects.create(name="羽凡")
    # models.Boy.objects.create(name="乃亮")
    #
    # models.Girl.objects.bulk_create(
    #     objs=[models.Girl(name="小路"), models.Girl(name="百合"), models.Girl(name="马蓉")],
    #     batch_size=3
    # )

    # 创建关系
    # models.B2G.objects.create(bid_id=1, gid_id=3, address="北京")
    # models.B2G.objects.create(bid_id=1, gid_id=2, address="北京")
    # models.B2G.objects.create(bid_id=2, gid_id=2, address="北京")
    # models.B2G.objects.create(bid_id=2, gid_id=1, address="北京")

    # b_obj = models.Boy.objects.filter(name='宝强').first()
    # g_object = models.Girl.objects.filter(name="小路").first()
    # models.B2G.objects.create(bid=b_obj, gid=g_object, address="北京")

    # 1.宝强都与谁约会。
    # queyset=[obj,obj,obj]
    # q = models.B2G.objects.filter(bid__name='宝强').select_related("gid")
    # for item in q:
    #     print(item.id, item.address, item.bid.name, item.gid.name)

    # q = models.B2G.objects.filter(bid__name='宝强').values("id", 'bid__name', 'gid__name')
    # for item in q:
    #     print(item['id'], item['bid__name'], item['gid__name'])

    # 2.百合 都与谁约会。
    # q = models.B2G.objects.filter(gid__name='百合').values("id", 'bid__name', 'gid__name')
    # for item in q:
    #     print(item['id'], item['bid__name'], item['gid__name'])

    # 3.删除
    # models.B2G.objects.filter(id=1).delete()
    # models.Boy.objects.filter(id=1).delete()

    return HttpResponse("返回")
15.7.4 一对一

16. cookie和session

python 复制代码
127.0.0.1       v1.david.com
127.0.0.1       v2.david.com
# set_cookie中的参数domain只对域名生效,对ip无效

16.2 配置session

16.2.1 文件版
python 复制代码
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',
]


# session
SESSION_ENGINE = 'django.contrib.sessions.backends.file'
SESSION_FILE_PATH = 'xxxx'  # 在项目目录下生成xxxx目录即可

SESSION_COOKIE_NAME = "sid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"  # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False  # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600  # Session的cookie失效日期(2周)

SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = True  # 是否每次请求都保存Session,默认修改之后才保存
16.2.2 数据库存储
python 复制代码
INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    'django.contrib.sessions',  # 将这个app取消注释,并且通过migrate命令生成对应表
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01.apps.App01Config",
]

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',
]


# session
SESSION_ENGINE = 'django.contrib.sessions.backends.db'

SESSION_COOKIE_NAME = "sid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"  # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False  # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600  # Session的cookie失效日期(2周)

SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = True  # 是否每次请求都保存Session,默认修改之后才保存
16.2.3 redis缓存
python 复制代码
INSTALLED_APPS = [
    # 'django.contrib.admin',
    # 'django.contrib.auth',
    # 'django.contrib.contenttypes',
    # 'django.contrib.sessions',
    # 'django.contrib.messages',
    'django.contrib.staticfiles',
    "app01.apps.App01Config",
]

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',
]


# session
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
SESSION_CACHE_ALIAS = 'default'  # 指定settings.py中CACHES配置具体使用哪个

SESSION_COOKIE_NAME = "sid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串
SESSION_COOKIE_PATH = "/"  # Session的cookie保存的路径
SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名
SESSION_COOKIE_SECURE = False  # 是否Https传输cookie
SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输
SESSION_COOKIE_AGE = 1209600  # Session的cookie失效日期(2周)

SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否关闭浏览器使得Session过期
SESSION_SAVE_EVERY_REQUEST = True  # 是否每次请求都保存Session,默认修改之后才保存

17. 缓存

  • 服务器 + redis安装启动

  • django

    • 安装连接redis包

      shell 复制代码
      pip install django-redis==5.4.0  # 最新版django-redis-6.0.0 仅支持Django4.2+
    • settings.py

      python 复制代码
      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": "密码",
              }
          }
      }
    • 手动操作redis

      python 复制代码
      from django_redis import get_redis_connection
      
      conn = get_redis_connection("default")
      conn.set("xx","123123")
      conn.get("xx")

18. 通过orm创建数据

18.1 离线脚本

想使用ORM插入一些数据,伪造启动django。一般在项目根目录下创建scripts目录下放一个py文件

python 复制代码
# 伪造django启动
import os
import sys
import django

base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 项目根目录
sys.path.append(base_dir)

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'model6_order.settings')
django.setup() 

# 离线脚本运行完之后防止反复运行,就给他注释掉
from web import models
from utils.encrypt import md5

models.Administrator.objects.create(
    username='admin',
    password=md5('admin'),
    mobile='13829102910'
)

18.2 shell命令行

shell 复制代码
python manage.py shell
# 临时交互命令行, 一次性输入也可以通过orm创建一条数据
>>> from web import models
>>> from utils.encrypt import md5
>>> models.Administrator.objects.create(username='root',password=md5('root'),mobile='13829102911')

19. 项目知识点

19.1 Ajax请求

本质,利用浏览器上XMLhttpRequest。

利用jQuery类库,内部封装,使用简单。

html 复制代码
<html>
	...
    <body>
        
        <script src='jquery.js'></script>
        <script>
        	$.ajax({
                url:"....",
                type:"post",
                data:{n1:123,n2:456},
                success:function(res){
                    console.log(res);
                }
            })
        </script>
    </body>
</html>
19.1.1 应用场景
  • 提交数据,页面可以刷新 -> form表单
  • 提交数据,不想刷新,ajax形式提交。

19.2. CSRF Token

19.2.1 form表单形式
html 复制代码
<form>
    {% csrf_token %}   <input type='hidden' value='xxxx' />
    <input type='submit' />
</form>
19.2.2 ajax方式
html 复制代码
<form>
    {% csrf_token %}   <input type='hidden' value='xxxx' />
    <input type='submit' />
</form>
复制代码
浏览器打开网址时,django在cookie中也给我们返回了一段值
html 复制代码
$.ajax({
	url:"...",
	type:"get",
	data:{user:"david", pwd:"xxx"},
	header:{
		"X-CSRFToken":"cookie中的值"
	}
	success:function(arg){
		
	}
})

19.3. Form组件

19.3.1 form表单提交
  • 生成HTML标签 + 携带数据。
    • 保留原来提交的数据,不再担心form表单提交时页面刷新。

    • 显示默认值,做编辑页面显示默认值。

html 复制代码
<h2 style="text-align: center">用户登录</h2>
<form action="/login/" method="post" novalidate>
    {% csrf_token %}

    {% for field in form %}
    <div class="form-group" style="position: relative; margin-bottom: 25px">
        <label>{{ field.label }}</label>
        {{ field }}
        <span style="color:red;position: absolute;">{{ field.errors.0 }}</span>
    </div>
    {% endfor %}

    {{ form.non_field_errors.0 }}

    <button type="submit" class="btn btn-primary">登 录</button>
    <span style="color:red">{{ error }}</span>

    <a href="/sms/login" style="float: right">短信登录</a>
</form>
  • 数据校验,对用户提交的数据格式校验
python 复制代码
### views.py
from django.shortcuts import HttpResponse, render, redirect
from web import models
from utils.encrypt import md5
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator


class LoginForm(forms.Form):
    role = forms.ChoiceField(
        label="角色",  # 用于模板渲染时可以使用for-endfor生成
        choices=(("1", "管理员"), ("2", "客户")),
        widget=forms.Select(attrs={"class": "form-control"})
    )
    username = forms.CharField(
        label="用户名",
        widget=forms.TextInput(attrs={"class": "form-control", "placeholder": "用户名"})
    )
    password = forms.CharField(
        label="密码",
        min_length=6,
        max_length=10,
        validators=[RegexValidator(r'^[0-9]+$', '密码必须是数字'), ],
        widget=forms.PasswordInput(attrs={"class": "form-control", "placeholder": "密码"}, render_value=True),
        # 密码字段提交后默认不保留,如果想要保留需要添加render_value=True
    )

    # 自定义方法(钩子)
    def clean_username(self):  # 函数名规定为 clean_字段名
        username = self.cleaned_data['username']
        # 校验规则
        # 如果校验失败
        if len(username) < 3:
            from django.core.exceptions import ValidationError
            raise ValidationError("用户名格式错误")
        # 如果校验成功
        return username  # 只要返回就是通过

    def clean(self):
        # 在每个字段的校验通过后,对所有值进行校验,无论前面字段校验成功与否
        username = self.cleaned_data.get('username')
        pwd = self.cleaned_data.get('password')

        # 1.不返回值,默认self.cleaned_data
        # 2.返回值,self.cleaned_data = 返回的值
        # 3.报错,ValidationError -> self.add_error(None, e)
        # raise ValidationError("整体错误")

    def _post_clean(self):
        pass


def login(request):
    if request.method == "GET":
        form = LoginForm()
        return render(request, 'login.html', {"form": form})

    # 1.接受并获取数据(数据格式或是否为空验证-Form组件 & ModelForm组件)
    form = LoginForm(data=request.POST)
    if not form.is_valid():  # 开始校验
        # form.cleaned_data
        # form.errors  {"username":[...], "password":[...]}
        # form.errors['__all__']  这是clean()方法的结果
        # form.non_field_errors().0
        return render(request, 'login.html', {"form": form})

    print(form.cleaned_data)  # {"username":'xx', 'password':'xx', 'role':'xx'}

    role = form.cleaned_data.get('role')  # get获取第一个值 getlist获取列表中所有值。例如多选框
    username = form.cleaned_data.get('username')
    password = form.cleaned_data.get('password')  # get获取第一个值 getlist获取列表中所有值。例如多选框
    password = md5(password)

    # 2.去数据库进行校验 1管理员 2客户
    role_mapping = {"1": "ADMIN", "2": "CUSTOMER"}
    if role not in role_mapping:
        return render(request, 'login.html', {'form': form, 'error': "角色不存在"})

    if role == "1":
        user_obj = models.Administrator.objects.filter(active=1, username=username, password=password).first()
    else:
        user_obj = models.Customer.objects.filter(active=1, username=username, password=password).first()

    # 2.1 校验失败
    if not user_obj:
        return render(request, 'login.html', {'form': form, 'error': "用户名或密码错误"})
    # 2.2 校验成功,用户信息写入session+进入项目后台
    request.session['user_info'] = {'role': role_mapping[role], 'name': user_obj.username, 'id': user_obj.id}
    return redirect("/home/")
19.3.2 form的校验流程
  • 每个字段的内部:required + validators + min_length=6,max_length=10

  • 字段的钩子方法

    python 复制代码
    def clean_username(self):
        user = self.cleaned_data['username']
        # 校验规则
        # 校验失败
        if len(user) < 3:
            from django.core.exceptions import ValidationError
            raise ValidationError("用户名格式错误")
    	return user
    
    print(form.cleaned_data)
  • clean(用的少)

    python 复制代码
    def clean(self):
        # 在每个字段的校验通过后,对所有值进行校验,无论前面字段校验成功与否
        username = self.cleaned_data.get('username')
        pwd = self.cleaned_data.get('password')
    
        # 1.不返回值,默认self.cleaned_data
        # 2.返回值,self.cleaned_data = 返回的值
        # 3.报错,ValidationError -> self.add_error(None, e)
        # raise ValidationError("整体错误")
  • _post_clean(用的少)

    python 复制代码
    def _post_clean(self):
        pass
19.3.3 Form目的
  • 生成标签

    • 循环 + 单独某个字段
    • label页面显示文本信息
    • 自定义错误信息位置,position
  • 校验

    • 定义

      • 每个字段校验

        • 字段上定义校验规则 正则、空、长度

        • clean_方法名

          校验成功:
          self.cleaned_data["字段"] = 值
          校验失败
          self.errors["字段"] = 错误

      • 所有字段校验

        • clean

          python 复制代码
          try:
              cleaned_data = self.clean()
          except ValidationError as e:
              self.add_error(None, e)
          else:
              if cleaned_data is not None:
                  self.cleaned_data = cleaned_data
                  
          self.errors["__all__"] = 错误
        • _post_clean

    • 校验源码

      • form.is_valid
      • form.errors
      • form.full_clean()
    • 业务校验

      python 复制代码
      form = 对象
      
      if not form.is_valid():
          print(form.errors)
          form.errors.username.0
          form.errors.__call__.0   ->  模板语言中 form.errors.__ 不支持
          form.non_field_errors()  <==> form.errors.__call__
          在页面上去适合的位置显示错误信息
      else:
          print(form.cleaned_data)
          ...
    • 扩展

      复制代码
      如果想要让某个错误信息,展示在特定的字段旁,就可以使用:
      form.add_error("password", "用户名或密码错误")

19.4 短信登录

19.4.1 展示数据
19.4.2 发送短信
复制代码
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>

$.ajax({
	url:"/xxx/",
	type:"GET",
	data:{mobile:"1888888"},
	success:function(res){
		...
	}
})
javascript 复制代码
function getCookie(name) {
    let cookieValue = null;
    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();
            // 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;
}

向后端发送请求:

  • form

    复制代码
    - GET/POST
    html 复制代码
    <form method='GET'>
        
    </form>
    html 复制代码
    <form method='POST'>
        {% csrf_token %}
        ...
    </form>
  • ajax

    javascript 复制代码
    $.ajax({
        url: "/xxx/xx",
        type: "GET",
        data: {mobile: "1888888"},
        success: function (res) {
            console.log(res);
        }
    })
    javascript 复制代码
    $.ajax({
        url: "/xxx/xx",
        type: "POST",
        data: {mobile: "1888888"},
        headers:{
            "X-CSRFTOKEN":"...."
        },
        success: function (res) {
            console.log(res);
        }
    })
    javascript 复制代码
    $.ajax({
        url: "/xxx/xx",
        type: "PUT",
        data: {mobile: "1888888"},
        headers:{
            "X-CSRFTOKEN":"...."
        },
        success: function (res) {
            console.log(res);
        }
    })
    javascript 复制代码
    $.ajax({
        url: "/xxx/xx",
        type: "DELETE",
        data: {mobile: "1888888"},
        headers:{
            "X-CSRFTOKEN":"...."
        },
        success: function (res) {
            console.log(res);
        }
    })
19.4.3 登录
  • Ajax提交,展示效果好。
  • Form提交。
html 复制代码
<form id='f1'>
    <input ...
           
</form>
javascript 复制代码
$.ajax({
    url:"/sms/login/",
    type:"POST",
    data:{
        mobile:'xxx',
        code:'xxx'
    }
    
})
javascript 复制代码
$.ajax({
    url:"/sms/login/",
    type:"POST",
    data:$("#f1").serialize()
})

# 好处,不需要加csrf header

19.5 总结

  • 发送Ajax

    html 复制代码
    $.ajax({
    	url:"...",
    	type:"GET",
    	data:{},
    	dataType:"JSON",
    	success:function(res){
    	}
    })
  • csrf认证

    • 请求体中

      复制代码
      - 传统的form   添加一行{% csrf_token %}
      - jQuery    $("#smsForm").serialize()  + Ajax
    • 请求头中

      复制代码
      集成在了csrf.js中,在html中导入csrf.js即可
      $.ajaxSetup({
      	beforeSend...
      })
      
      $.ajax({
      	url:"...",
      	type:"GET",
      	data:{},
      	dataType:"JSON",
      	headers:{
      		...
      	},
      	success:function(res){
      	}
      })
  • Django的自定义请求头

    复制代码
    - 自动化添加 HTTP_ 前缀
    - 自动化添加 HTTP_ 前缀,前端的-,转换成后端的_
  • Form组件 & 源码执行流程 & 钩子执行顺序

    • 未使用钩子
    • 使用钩子流程 & 结合源码
  • jQuery + location.href + 错误信息展示

  • 保留代码,便于后续开发。(model6_order)

相关推荐
q***49862 小时前
在Django中安装、配置、使用CKEditor5,并将CKEditor5录入的文章展现出来,实现一个简单博客网站的功能
数据库·django·sqlite
IT北辰2 小时前
Python实现居民供暖中暖气能耗数据可视化分析(文中含源码)
开发语言·python·信息可视化
FreeCode2 小时前
LangChain1.0智能体开发:长期记忆
python·langchain·agent
Predestination王瀞潞3 小时前
Python __name__ 与 __main__
开发语言·python
萧曵 丶3 小时前
Python 字符串、列表、元组、字典、集合常用函数
开发语言·前端·python
梦想的初衷~3 小时前
Plaxis自动化建模与Python应用全解:从环境搭建到高级案例实战
python·自动化·工程设计·工程软件
Q_Q5110082853 小时前
python+uniapp基于微信小程序的垃圾分类信息系统
spring boot·python·微信小程序·django·flask·uni-app·node.js
HackerTom3 小时前
vs code jupyter连gpu结点kernel
python·jupyter·gpu·vs code·远程
weixin_468466854 小时前
遗传算法求解TSP旅行商问题python代码实战
python·算法·算法优化·遗传算法·旅行商问题·智能优化·np问题