Django学习笔记

Django框架学习笔记

1.Django框架简介

Django是一个开放源代码的Web应用框架,采用了MTV的框架模式,即模型M,视图V和模版T

  • 重量级的Python Web框架,Django 配备了常用的大部分组件
  • 用途一:网站/微信公众号/小程序后端开发
  • 用途二:人工智能平台融合
  • Django官网 http://www.djangoproject.com
  • Django中文文档参考网站 https://yiyibooks.cn/

组件

  • 基本配置文件/路由系统
  • 模型层(M)/模板层(T)/视图层(V)
  • Cookies和Session
  • 分页及发邮件
  • Admin管理后台
  • Django项目创建

2.Django项目搭建

项目安装

在线安装

  • Linux虚拟机执行 sudo pip3 install django–2.2.12
  • 检查是否成功 sudo pip3 freeze|grep -i ‘Django’

离线安装- 官网下载离线安装包

  • 将安装包复制到虚拟机
  • 解压缩 tar -xvf Django-2.2.12.tar.gz
  • 进入目录 cd Django-2.2.12
  • 执行安装 sudo python3 setup.py install
  • 检查是否成功 sudo pip3 freeze|grep -i ‘Django’

创建项目

  • 创建相应项目文件夹 执行 django-admin startproject mysite1
  • 需要在配置项中 禁止掉 csrf【POST提交403问题】

启动服务

  • 终端 cd 进入到项目文件夹,如 cd mysite1
  • 启动 django 服务,执行 python3 manage.py runserver
  • 浏览器访问 http://127.0.0.1:8000 可看到django的启动页面
  • 若想更换端口,执行 python3 manage.py runserver 端口号

若要局域网其他主机访问此主机的Django服务,启动方式如下:

  • python manage.py runserver 0.0.0.0:5000
  • 指定网络设备内网环境下其他主机正常访问该站点,需加 ALLOWED_HOSTS=[‘内网IP’]

关闭服务

方式1:在runserver终端下

  • 执行Ctrl + C 可关闭Django服务

方式2:在其他终端下

  • 执行 sudo lsof -i:8000 查询出 Django的进程id
  • 执行 kill -9 Django进程id

项目结构

Django学习笔记_第1张图片

  1. db.sqlite3 - 项目默认创建的数据库文件
  2. python manage.py 包含项目管理的子命令
  3. 项目同名文件夹 - mysite1/mysite1
  • ——init—— : Python包的初始化文件
  • wsgi.py : WEB服务网关的配置文件 - Django正式启动时,需要用到
  • urls.py : 项目的主路由配置 - HTTP请求进入Django时,优先调用该文件
  • settings.py : 项目的配置文件 - 包含项目启动时所需配置

1.Settings.py配置

  • 配置项分为 公有配置 和 自定义配置
  • 配置项格式: BASE_DIR = ‘xxxx’
  • 公有配置 - Django官方提供 https://docs.djangoproject.com/en/4.0/ref/settings/
from pathlib import Path

# 创建内部路径: BASE_DIR / 'subdir'.
# 绑定当前项目的绝对路径(动态),所有文件夹依赖此路径
BASE_DIR = Path(__file__).resolve().parent.parent

DEBUG = True
"""
配置Django项目的启动模式
True - 调试模式
1,检测代码改动后,立刻重启服务
2,报错页面
False - 正式启动模式 / 上线模式 / 生产模式
"""

# 公有配置 请求头Host头,过滤无效请求头,比如域名 www.tendu.com,127.0.0.1 等等
ALLOWED_HOSTS = []
"""
设置允许访问到本项目的 host 头值
- []空列表,表示只有请求头中host为127.0.0.1,localhost能访问,DEBUG=True时有效
- ['*'],表示任何请求头的host都能访问
- ['192.168.124.20','127.0.0.1']表示只有当前两个host头的值能访问
"""

# 配置Django应用 -注册应用 指定当前项目中安装的应用列表
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'user',
    'index',
    'note',
]
# 中间件配置,用于注册中间件
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',
    'middleware.mymiddleware.MyMW',
    'middleware.mymiddleware.MyMW2',
]
# 标识主路由的位置
ROOT_URLCONF = 'tedu_note.urls'

# 指定模板的配置信息
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [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',
            ],
        },
    },
]

WSGI_APPLICATION = 'tedu_note.wsgi.application'


# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
# 数据库连接配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'tedu_note',
        'USER': 'root',
        'PASSWORD':'root',
        'HOST':'127.0.0.1',
        'PORT' : '3306'
    }
}

# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

# 语言设置
LANGUAGE_CODE = 'zh-Hans'

# 时区设置
TIME_ZONE = 'Asia/Shanghai'

USE_I18N = True

USE_TZ = False


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

# 指定seesionid在cookies中的保存时长(默认是2周)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2

#设置只要浏览器关闭时,session就失效(默认为False)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False

# 数据库缓存配置
CACHES = {
    'default':{
        'BACKEND':'django.core.cache.backends.db.DatabaseCache',
        'LOCATION':'my_cache_table',
        'TIMEOUT':300, # 缓存保存时间 单位秒, 默认值为 300
        'OPTIONS':{
            'MAX_ENTRIES':300,# 缓存最大数据条数
            'CULL_FREQUENCY':2,# 缓存条数达到最大值时 删除 1/X 的缓存数据
        }
    }
}
# 服务器内存的缓存配置
# CACHES = {
#     'default':{
#         'BACKEND':'django.core.cache.backends.locmem.LocMemCache',
#         'LOCATION':'unique-snowflake'
#     }
# }
# 文件系统的缓存配置
# CACHES = {
#     'default':{
#         'BACKEND':'django.core.cache.backends.filebased.FileBasedCache',
#         'LOCATION':'/var/tmp/django_cache', # 缓存文件夹的路径
#         'LOCATION':'c:\test\cache', # windows下示例,缓存文件夹的路径
#
#     }
# }

3.设计模式

MVC与MTV的区别

备注:格式化快捷键 ctr+alt+L

MVCC(控制层) 相当于 MTV 中的 主路由配置(urls.py) 简化

urlpatterns = [
    path('admin/', admin.site.urls),
    #视图函数
    path('test_request',views.test_request),
    path('test_get_post',views.test_get_post),
    path('test_html',views.test_html)]

4.模板层

模板层配置(setting.py),并创建 templates文件夹

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / 'templates'],
        # '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',
            ],
        },
    },
]

MVCV(视图)功能相当于 MTV 中的 T(模板)+V(视图)

其中:T 保存 html ,V 控制逻辑,与模板层进行交互(可传变量 **{{ 变量键名 }} **)

当视图中需要生成的字典内容过多时,可以使用 locals() 自动生成含有当前所有内容的字典

def test_html(request):
    # 方案1:
    from django.template import loader
    t=loader.get_template('test_html.html')
    html=t.render()
    return HttpResponse(html)

    # 方案2:简化代码,使用render()直接加载并响应模板,dic传值必须是字典
    from django.shortcuts import render
    dic={'uname':'yuyupeng','age':22}
    return render(request,'test_html.html',dic)

在模板中使用变量方法

{{ 变量名 }} {{ 变量名.index }} {{ 变量名.key }} {{ 函数名 }} {{ 对象.方法 }}

<h3> int : {{ int }}h3>
<h3> str : {{ str }}h3>
<h3> lst : {{ lst }}h3>
<h3> lst[1] : {{ lst.1 }}h3>
<h3> dic : {{ dic }}h3>
<h3> dic['a'] : {{ dic.a }}h3>
<h3> fuc : {{ fuc }}h3>
<h3> obj : {{ obj.speak }}h3>

模板中标签用法

  • {% 标签 %} {% 结束标签 %} / {% if 变量名 %} 内容 {% endif %}
  • if - 参考文档:https://docs.djangoproject.com/zh-hans/4.0/ref/templates/builtins/#if

模板过滤器

通过使用 过滤器来改变 变量的输出显示 ,如 lower | upper | safe | add:“n” | truncatechars":'n’

语法:{{ 变量 | 过滤器1:‘参数值1’ |过滤器2:‘参数值2’ |… }}

**safe:**变量输出时不需要转义

官方文档:https://docs.djangoproject.com/en/4.0/ref/templates/builtins/

模板的继承

子模板可以直接继承父模板,并覆盖相应的块

继承:{% extends ‘base.html’ %} (动态变量无法继承)

重写:{% block block_name %} {% endblock [block_name] %}

5.URL

代码中的url书写规范

1.绝对地址

2.相对地址

(1)’**/**page/1’ (带有 / 开头的),相对地址为 127.0.0.1:8000/page/1

(2)‘page/1’(不带 / 开头的),相对地址会截取最后一位 / ,比如

127.0.0.1:8000/page**/**3 截取后相对地址为 127.0.0.1:8000/page/page/1

url反向解析

Django学习笔记_第2张图片

在视图函数中 -> 可调用django中的reverse 方法进行反向解析

from django.urls import reverse
reverse('别名',args=[],kwargs={})
ex:
print(reverse('pagen',args=[300]))
print(reverse('person',kwargs={'name':'xixi','age':18}))

6.静态文件访问

1.静态文件配置(setting.py),并创建 static文件夹

STATIC_URL = 'static/'
STATICFILES_DIRS=( BASE_DIR/'static',)

方案1:绝对路径/相对路径

方案2:动态访问,更加灵活

加载static - {% load static %}

使用静态资源 - {% static ‘静态资源路径’ %}

#方案1
<img src="http://127.0.0.1:8000/static/image/django.png" width="200px" height="200px">
<img src="/static/image/django.png" width="200px" height="200px">
#方案2
{% load static %}
<img src="{% static 'image/django.png' %}" width="200px" height="200px">

7.应用

添加 app 命令:python manage.py startapp music

配置(setting.py) 注册app(一定要注册应用

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'music',
    'sport',
    'news',
]

8.模型层

模型层 — 负责跟数据库之间进行通信
Django学习笔记_第3张图片

Django 配置Mysql

ubuntu 中 安装 python3-dev 以及 default-libmysqlclient-dev

检查命令: sudo apt list --installed|grep -E ‘libmysqlclient-dev|python3-dev’

安装命令: sudo apt-get install python3-dev default-libmysqlclient-dev

Mysql - 安装命令: pip install mysqlclient

创建数据库命令 create database mysite1 default charset utf8;

配置(setting.py)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mysite3',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST' : '127.0.0.1',
        "PORT" : '3306'
    }
}

其他数据库配置如下:

  • ENGINE - 指定数据库存储引擎
'django.db.backends.mysql'
'django.db.backends.sqllite3'
'django.db.backends.oracle'
'django.db.backends.postgresql'
  • NAME - 指定要连接的数据库的名称
  • USER - 指定登录到数据库的用户名
  • PASSWORD - 数据库的密码
  • HOST/PORT - 链接具体数据库的 IP 和 端口

模型:是数据交互的接口,表示和操作数据库的方法和方式

  • python中 django.db.models.Model 的派生子类
  • 模型类代表 数据库中的一张数据表
  • 每个类属性代表一个字段

ORM框架

ORM(Object Relational Mapping) 即 对象关系映射

  • 1.建立模型类和表之间的对应关系,通过面向对象方式来操作数据库
  • 2.根据设计的模型类生成数据库中的表格
  • 3.通过简单的配置就可以进行数据库的切换

优点:

1.只需要面向对象编程,不需要面向数据库编写代码

2.实现了数据模型与数据库的解耦,屏蔽了不同数据库操作上的差异

Django学习笔记_第4张图片

python建立模型类

新建app (python manage.py startapp bookstore)

配置 bookstore-> models.py

字段类型文档:https://docs.djangoproject.com/en/4.0/ref/models/fields/#field-types

from django.db import models
# class 模型类名(models.Model):
#   字段名 = models.字段类型(字段选项)
  
class Book(models.Model):
    title= models.CharField('书名',max_length=50,default='')
    price = models.DecimalField('价格',max_digits=7,decimal_places=2)

数据库迁移同步(只要修改过,必须要执行 makemigrations和migrate)

迁移是Django同步您对模型所作更改(添加字段、删除模型等)到数据库模式的方式

1.生成迁移文件,将应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中

  • 执行 python manage.py makemigrations

2.执行迁移脚本程序 ,将每个应用下的migrations目录中的中间文件同步会数据库

  • 执行 python manage.py migrate

执行数据库交互式操作Django Shell

  • 启动方式:python manage.py
  • 项目代码发生变化时,重新进入Django shell
from bookstore.models import Book
#方式一:(推荐)
b1 = Book.objects.create(title='Python',pub='清华大学出版社',price=20,market=25)
#方式二:
b2 = Book(title='Django',pub='清华大学出版社',price=70,market=75)
b2.save()

ORM操作方法

1.ORM查询方法

( all / values / values_list / order_by / filter / exclude / get)

其中 get 必须 和 (try - except Exception as e) 配套使用,必须查询到唯一object,否则报错

from bookstore.models import Book
#a ll() 存放Book实例
all_book = Book.objects.all()
for book in all_book:
  print('书名',book.title,'出版社:',book.pub)

#values() 存放字典
values = Book.objects.values('title','pub')
for book in values:
  print('书名',book['title'],'出版社:',book['pub'])

#values_list() 存放元组
values = Book.objects.values_list('title','pub')
for book in values:
  print('书名',book[0],'出版社:',book[1])

#order_by() 排序,默认为升序,降序需增加 '-',如 '-price'
order = Book.objects.all().order_by('-price')
for book in order:
  print('书名',book.title,'出版社:',book.pub)
  
#显示Sql查询语句
print(order.query)
2.查询谓词
  • 类属性+ ‘__’ + 谓词
  • __exact :等值匹配, is null
  • __contains :包含指定值
  • __startswith :以XXX开始
  • __endswith :以XXX结束
  • __gt :大于
  • __gte :大于等于
  • __lt :小于
  • __lte :小于等于
  • __in :指定范围内
  • __range : 指定区间内

官方文档:https://docs.djangoproject.com/en/4.0/ref/models/querysets/#field-lookups

3.ORM更新方法

单个数据更新

#查 get()
try:
    book = Book.objects.get(id=book_id)
except Exception as e:
    print('--update book error is %s' % (e))
    return HttpResponse('The book is not existed')
# 改
book.price = price
book.market = market_price
# 保存
book.save()

批量数据更新

#查 
books = Book.objects.filter(id__gt=3)
#QuerySet中 update()
books.update(price=10)
4.ORM删除方法

单个/批量数据删除

try:
  #单个
  auth = Author.objects.get(id=1)
  auth.delete()
  #批量
  auth1 = Author.objects.filter(age__gt=65)
  auth1.delete()
  
except Exception as e:
  print('删除失败%s'%(e))

伪删除

mysql表中增加 布尔型字段(is_active)字段,默认为True

注意:显示数据时,均要加 is_active = True 的过滤查询,否则删除失效

5.F对象和Q对象

F对象

  • 定义:一个F对象代表数据库中某条记录的字段信息
  • 作用:不获取就可以对数据库中的字段值进行操作,用于类属性(字段)之间的比较
  • 语法:F(‘列名’)
  • 可以保证在高并发时,数据脏读问题
#示例1 更新Book实例中所有价格涨10元
from bookstore.models import Book
from django.db.models import F
#方案一:使用F对象
Book.objects.all().update(price=F('price')+10)
#等同于执行 mysql语句 'UPDATE book SET book.price = book.price+10'

#方案二:常规
books = Book.objects.all()
for book in books:
  book.price = book.price+10
  book.save()

#示例2 对数据库中两字段值进行比较,列出零售价高于定价的书籍
books = Book.objects.filter(market__gt=F('price') )
for book in books:
  print(book.title,'定价:',book.price,'现价:',book.market)

Q对象

  • 获取查询结果集 可以借助 Q对象 使用复杂的逻辑判断
#  & 与操作    |或操作      ~非操作
   Q('条件1')&Q('条件2')  #同时成立
   Q('条件1')|Q('条件2')  #满足其一
   Q('条件1')&~Q('条件2') #条件1成立且条件2不成立 
from bookstore.models import Book
from django.db.models import Q
Book.objects.filter(Q(price__lt=20)|Q(pub='清华大学出版社'))

聚合查询

1.整表聚合

  • 语法:MyModel.objects.aggregate(结果变量名 = 聚合函数(‘列’))
  • 返回结果为字典 {“结果变量名”:值 }
from django.db.models import *
# Sum, Avg, Count, Max, Min
Book.objects.aggregate(result=Count('id'))

2.分组聚合

  • 为查询集的每一项生成聚合
  • 语法: QuerySet.annotate(结果变量名 = 聚合函数(‘列’))
  • 返回值:QuerySet
from bookstore.models import Book
from django.db.models import Count
bs = Book.objects.values('pub')
bs.annotate(res=Count('pub'))

原生数据库操作

Django支持 直接用Sql语句通信数据库

查询:MyModel.objects.raw(sql语句,拼接参数)

返回值:RawQuerySet集合对象【支持基础操作,如循环等】

books = models.book.objects.raw('select * from book')

SQL注入

  • 案例1:用户在搜索好友的表单中 输入 ‘1 or 1=1’
  • 攻击结果:可查询出所有用户数据
#错误
s1 = Book.objects.raw('select * from book where id=%s'%('1 or 1=1'))
#正确的 sql注入防范 (sql语句,拼接参数)
s2 = Book.objects.raw('select * from book where id=%s',['1 or 1=1'])

Cursor

完全跨过模型类操作数据库 - 查询/更新/删除

from django.db import connection
#通常使用with语句进行创建cursor对象,保证异常时能释放 cursor资源
with connection.cursor() as cur:
  cur.execute('执行SQL语句','拼接参数')

#将id为 10 的书的出版社调整
with connection.cursor() as cur:
  cur.execute('update book set pub="光明出版社" where id=10;')

#删除id为 1的一条记录  
with connection.cursor() as cur:
  cur.execute('delete from book where id=10;')

9.admin管理后台

配置

  • 创建后台管理账号-即最高权限账号 – python manage.py createsuperuser
  • 配置管理后台
  • 管理后台用户和组配置
  • 进入 http://127.0.0.1:8000/admin

注册自定义模型类

配置mysite1/admin.py

from django.contrib import admin
from .models import Book
# Register your models here.
admin.site.register(Book)

模型管理器类

配置mysite1/admin.py

from django.contrib import admin
from .models import Book,Author
# Register your models here.

class BookManager(admin.ModelAdmin):
    #列表页显示哪些字段的列
    list_display = ['id','title','pub','price','market']
    #控制list_display 中的字段 哪些可以链接到修改页
    list_display_links = ['title']
    #添加过滤器
    list_filter = ['pub','price']
    #添加搜索框[模糊查询]
    search_fields = ['title']
    #添加可在列表页编辑的字段
    list_editable = ['pub','price','market']
  
admin.site.register(Book,BookManager)

Meta类

class Book(models.model):
  title = CharField(....)
  class Meta:
    # 1.该模型所用的数据表名称-(设置后需立马更新同步数据库)
    db_table = 'book'
    # 2.该模型的显示名称(单数),用于显示在/admin管理界面中
    verbose_name = '图书'
    # 3.该模型的显示名称(复数),用于显示在/admin管理界面中
    verbose_name_plural = verbose_name

10.关系映射

级联删除 - on_delete

  • models.CASCADE 模拟SQL约束的行为,并删除包含ForeighKey的对象
  • models.PROTECT 抛出ProtectedError 以阻止删除(等同mysql默认的RESTRICT)
  • SET_NULL 设置ForeighKey null ,需要指定null=True
  • SET_DEFAULT 将ForeignKey设置为其默认值,必须设置ForeignKey的默认值
  • 官方文档:https://docs.djangoproject.com/en/4.0/ref/models/fields/#foreignkey

一对一

1.建立应用 oto,并注册

2.建立models中数据表,并同步迁移

from django.db import models

# Create your models here.
class Author(models.Model):
    name = models.CharField('姓名',max_length=11)

class Wife(models.Model):
    name = models.CharField('姓名',max_length=11)
    #增加一对一属性:属性 =OneToOneField(类名,on_delete=xxx)
    author = models.OneToOneField(Author,on_delete=models.CASCADE)

3.创建数据

from oto.models import Author,Wife
#无外键的模型类[Author]
a1 = Author.objects.create(name='王老师')
a2 = Author.objects.create(name='吕老师')
#有外键的模型类[Wife] 
wife1 = Wife.objects.create(name='王夫人',author=a1) #关联王老师的obj
wife1 = Wife.objects.create(name='吕夫人',author_id=a2.id) #关联吕老师的id

4.查询数据

#正向查询
print(wife1.author.name)
#反向查询 '实例对象.引用类名(小写)',若反向引用不存在,则出现异常
print(a2.wife.name)

一对多

1.建立应用 otm,并注册

2.建立models中数据表,并同步迁移

from django.db import models

# Create your models here.
class Publisher(models.Model):
    title = models.CharField('出版社名称',max_length=11)

class Book(models.Model):
    title = models.CharField('书名',max_length=11)
    #增加一对多属性:属性 =ForeignKey(类名,on_delete=xxx)
    publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE)

3.创建数据

from otm.models import Publisher,Book
#无外键的模型类[Author]
pub1 = Publisher.objects.create(title='清华大学出版社')

#有外键的模型类[Wife] 
book1 = Book.objects.create(title='C++',publisher=pub1) #关联pub的obj
book1 = Book.objects.create(title='Java',publisher_id=pub1.id) #关联pub的id

4.查询数据

#正向查询 '实例对象.引用类名(小写)'
print(book1.publisher.title)

#反向查询 通过 book_set 获取pub1 对应的多个Book数据对象
books=pub1.book_set.all() 
books1=Book.objects.filter(publisher=pub1) #也可以

for book in books:
    print(book.title)

多对多

1.建立应用 oto,并注册

2.建立models中数据表,并同步迁移

from django.db import models

# Create your models here.
class Author(models.Model):
    name = models.CharField('姓名',max_length=11)

class Book(models.Model):
    title = models.CharField('书名',max_length=11)
    #增加多对多属性:属性 =ManyToManyField(类名)
    authors = models.ManyToManyField(Author)

3.创建数据

from mtm.models import Author,Book
# 方案1 先创建 author 再关联 book
author1 = Author.objects.create(name='王老师')
author2 = Author.objects.create(name='吕老师')
# 王老师 和 吕老师 同时写了一本 Python
book1 = author1.book_set.create(title='Python')
author2.book_set.add(book1)

# 方案2 先创建 book 再关联 author
book1 = Book.objects.create(title='Java')
#赵老师 和 王老师 都参与了 Java 的创作
author3 = book1.authors.create(name='赵老师')
book1.authors.add(author1)

4.查询数据

#正向查询 '实例对象.引用类名(小写)'
authors = book1.authors.all()
for author in authors:
    print(author.name)
#反向查询 通过 book_set 获取pub1 对应的多个Book数据对象
books=author1.book_set.all() 
for book in books:
    print(book.title)

11.cookies 和 session

  • 保持会话状态的两个存储技术

Cookies【浏览器,相对不安全】

  • 以键-值对 的形式进行存储(ASCII字符串)
  • 存储的数据带有生命周期
  • cookies中的数据是按域存储隔离,不同的域之间无法访问
  • cookies内部数据会在每次访问网址时带到服务器端,若cookies过大会降低响应速度
# 设置cookies
def set_cookies(request):
    resp=HttpResponse('set cookies is ok')
    resp.set_cookie('uname','yyp',500)
    return resp

# 获取cookies
def get_cookies(request):
    value = request.COOKIES.get('uname','空值')
    return HttpResponse('value is %s'%(value))

# 删除cookies
def delete_cookies(request):
  	resp = HttpResponse('已删除 uname')
    resp.delete_cookie('uname')
    return resp

Session【服务器,相对安全】

session是在服务器上开辟一段空间,用于保留浏览器和服务器交互时的重要数据。

  • 使用session需要在浏览器客户点启动cookie,且在cookie中存储sessionid
  • 每个客户端在服务器端都有独立的Session,不会共享数据
  • Django中session数据存储在数据库中,所以使用session前需确保已经执行过migrate

相关配置项 setting.py

# 启用 session 应用
INSTALLED_APPS = [
    'django.contrib.messages',   
]
# 启用 session 中间件
MIDDLEWARE = [
    'django.contrib.sessions.middleware.SessionMiddleware',
]

# 指定seesionid在cookies中的保存时长(默认是2周)
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 2

#设置只要浏览器关闭时,session就失效(默认为False)
SESSION_EXPIRE_AT_BROWSER_CLOSE = False

session用法

# 保存 session 的值到服务器
def set_session(request):

    value = request.session['uname'] = 'wwc'
    return HttpResponse('set session is ok')

# 获取 session 的值
def get_session(request):

    value = request.session.get('uname')
    return HttpResponse('session value is %s'%(value))

# 删除 session
def delete_session(request):

    del request.session['uname']
    return HttpResponse('delete session is ok')

# 修改session存储时间为 1 天
    request.session.set_expiry(60*60*24)
# 你可以传递四种不同的值给它:

# *如果value是个整数,session会在些秒数后失效(适用于整个Django框架,即这个数值时效时整个页面都会session失效)。

# *如果value是个datatime或timedelta,session就会在这个时间后失效。
 
# *如果value是0, 用户关闭浏览器session就会失效。
 
# *如果value是None, session会依赖全局session失效策略。

session的问题

  • 1.django_session表是 单表设计,且该表数据量持续增持【浏览器故意删掉seesionid&过期数据未删除】
  • 2.可每晚执行 python manage.py clearsessions【该命令可删除已过期的session数据】

12.云笔记项目

项目简介

​ 用户可在该系统中 记录 自己的 日常学习/旅游 笔记,用户的数据将被安全的存储在 云笔记平台;用户与用户之间数据为隔离存储(用户只有在登陆后才能使用相关笔记功能,且之恩能够查阅自己的笔记内容)

部分页面展示如下:

Django学习笔记_第5张图片

项目组成员角色

  • 产品/运营经理:负责产品功能细节的把控
  • 开发:
    • 前端 - 负责显示部分内容的开发 【多】
    • 后端 - 负责服务器部分的功能开发 【少】
  • 运维 - 管理linux服务器,组件化配置,安全问题
  • 测试 - 负责找出产品功能的问题【BUG】
  • 美术 - 负责产品素材方面的绘制

13.项目问题

1.密码如何处理【明文】


哈希算法 : 给定明文,计算出一段定长的 ,不可逆的值; md5, sha-256

应用场景:1. 密码处理 2. 文件完整性校验(如电影下载)

特点

  • 定长输出: 不管明文输入长度多少,哈希值都是定长,md5 - 32位 16进制
  • 不可逆: 无法反向计算出 对应的 明文
  • 雪崩效应:输入改变,输出必然变
import hashlib

# 哈希算法 - 给定明文,计算出一段定长的 ,不可逆的值; md5, sha-256
h = hashlib.md5()
# 字符串需解码 -> 2进制
h.update(password1.encode())
passwordm = h.hexdigest()

2.重复插入问题

需使用 try - except 异常处理

# 有可能报错 - 重复插入 [唯一索引注意并发写入问题]
try:
    user = User.objects.create(username=username,password=passwordm)
except Exception as e:
    print('--create user error %s'%(e))
    error = '用户名重复,请重新填写!'
    return render(request, 'user/register.html', locals())

3.验证登陆状态

使用装饰器,降低代码重复率,解耦

# 在每个视图前引用装饰器即可
@check_login
def check_login(fn):

    def wrap(request,*args, **kwargs):
        # 检查 Session
        if 'username' not in request.session or 'uid' not in request.session:
            # 检查Cookies
            c_username = request.COOKIES.get('username')
            c_uid = request.COOKIES.get('uid')
            if not c_username or not c_uid:
                return HttpResponseRedirect('/user/login')
            else:
                # 回写session
                request.session['username'] = c_username
                request.session['uid'] = c_uid
        return fn(request, *args, **kwargs)
    return wrap

4.注册后免登录一天,会话有效期

session记录登陆用户,并设置过期时间

Django学习笔记_第6张图片

# 用户登录视图
def login_view(request):

    if request.method == 'GET':
        # 获取登录页面
        # 检查登录状态 ,如果登录了,显示 ’已登录'
        if request.session.get('username') and request.session.get('uid'):
            return HttpResponse('已登录')

        # 检查Cookies
        c_username = request.COOKIES.get('username')
        c_uid = request.COOKIES.get('uid')
        if c_uid and c_username:
            # 回写session
            request.session['username'] = c_username
            request.session['uid'] = c_uid
            return HttpResponse('已登录')

        return render(request,'user/login.html')

    elif request.method == 'POST':
        error = ''
        username = request.POST.get('username')
        password = request.POST.get('password')
        remember = request.POST.get('remember')

        try:
            user = User.objects.get(username=username,is_active=True)
        except Exception as e:
            print('--login user error %s'%(e))
            return render(request, 'user/login.html', locals())

        # 记录会话状态- 默认存session 为1天
        request.session['username'] = username
        request.session['uid'] = user.id
        # 设置过期时间 为一天,默认 settings.py 中为 14天
        request.session.set_expiry(60 * 60 * 24)

        resp = HttpResponse('登录成功')
        # 判断用户是否 点选了 ‘记住用户名‘
        if remember:

            resp.set_cookie('username',username,60 * 60 * 24 * 3)
            resp.set_cookie('uid',user.id,60 * 60 * 24 * 3)
        # 点选了 -> Cookies 存储 username,uid 时间3天
        return resp

14.缓存

  • **定义:**加快数据读取的存储方式,一般存储临时数据
  • **意义:**使用缓存技术,可以减少页面的实际渲染次数,降低用户的响应时间
  • **场景:**博客列表页 | 电商商品详情页 , 缓存的地方,数据变动频率较少
  • 官网优化思想:
given a URL, try finding that page in the cache
if the page is in the cache:
  return the cached page
else:
  generate the page
  save the generated page in the cache (for next tiem)
  return the generated page

Django中设置缓存

1.将缓存的数据存储在数据库中(mysql等)

需要手动执行 创建表 的命令,即 执行 python3 manage.py createcachetable

# 数据库缓存配置 (settings.py中添加)
CACHES = {
    'default':{
        'BACKEND':'django.core.cache.backends.db.DatabaseCache',
      	'LOCATION':'my_cache_table',
        'TIMEOUT':300, # 缓存保存时间 单位秒, 默认值为 300
        'OPTIONS':{
            'MAX_ENTRIES':300,# 缓存最大数据条数
            'CULL_FREQUENCY':2,# 缓存条数达到最大值时 删除 1/X 的缓存数据
        }
    }
}

2.将数据缓存到服务器内存中(测试使用)

# 服务器内存的缓存配置 (settings.py中添加)
CACHES = {
    'default':{
        'BACKEND':'django.core.cache.backends.locmem.LocMemCache',
        'LOCATION':'unique-snowflake'
    }
}

3.将缓存的数据存储到本地文件中

# 文件系统的缓存配置
CACHES = {
    'default':{
        'BACKEND':'django.core.cache.backends.filebased.FileBasedCache',
        'LOCATION':'/var/tmp/django_cache', # 缓存文件夹的路径
      # 'LOCATION':'c:\test\cache', # windows下示例,缓存文件夹的路径

    }
}

1.整体缓存策略

  • 采用数据库缓存策略
  • 视图函数使用缓存(即装饰器使用)
# 1.视图函数使用缓存(即装饰器使用)
from django.views.decorators.cache import cache_page
@cache_page(30)  # 单位(s)
def my_view(request):
  pass

# 2.路由中使用缓存(urls.py)
from django.views.decorators.cache import cache_page
from note import views
urlpatterns = [
    path('note/all',cache_page(30)(views.my_view))
]

2.局部缓存策略

  • 缓存Api的引入
# 方式1:引入cache对象,使用 caches['CACHE配置 KEY'] 导入具体对象
from django.core.cache import caches
cache = caches['default'] 
cache1 = caches['myalias']
cache2 = caches['myalias_2']

# 方式2:相当于直接引入 CACHES配置项中的 'default'项
from django.core.cache import cache
# 
  • 缓存Api的使用
# 1.存储缓存
cache.set(key,value,timeout)
# key:缓存的key,字符串类型
# value:Python对象
# timeout:缓存存储时间(s),默认为CACHES的TIMEOUT值
# 返回值:None

# 2.获取缓存
cache.get(key)
# key:缓存的key,字符串类型
# 返回值:key的具体指,若无,则为None

# 3.储存缓存,只在 key 不存在时生效
cache.add(key,value)
# 返回值:True[存储成功] or False[存储失败]

# 4.若为获取到数据,则执行 set操作
cache.get_or_set(key,value,timeout)
# 返回值:value

# 5.批量存储缓存
cache.set_many(dict,timeout)
# 返回值:插入不成功的key的数组

# 6.批量获取缓存数据
cache.get_many(key_list)
# 返回值:取到的key和value的字典

# 7.删除key的缓存数据
cache.delete(key)
cache.delete_many(key_list)
# 返回值:None

浏览器缓存策略

Django学习笔记_第7张图片

1.强缓存

不会向服务器发送请求,直接从缓存中读取资源

响应头 - Expires

  • 缓存过期时间
  • 样例 Expires:Thu,02 Apr 2030 05:14:09 GMT

响应头 - Casche-Control

  • 在HTTP/1.1中,主要用于控制网页缓存,max-age=120,表示120s后缓存失效
  • 目前服务器都会带着这两个头同时相应给浏览器,浏览器优先使用Cache-Control

2.协商缓存

强缓存中数据过期后,浏览器会和服务器协商,当前缓存是否可用,可用则无需返回,否则返回最新数据

1.Last-Modeified响应头 If-Modified-Since 请求头

  • 表示文件最新修改时间,若第一次请求 静态文件时,服务器返回该响应头,则表示该资源为需协商的缓存
  • 若缓存到期后,浏览器将 Last-Modified值作为请求头 的值进行协商,服务器返回 **304 **响应码【响应体为空】,代表缓存继续使用,**200 **响应码代表缓存不可用【响应体为最新资源】

2.ETag 响应头 和 If-None-Match请求头

Etag是服务器返回当前资源文件的一个唯一标识(如哈希值),资源变化,Etag重新生成

缓存到期后,浏览器将ETag值 作为 If-None-Match请求头的值 进行协商,服务器比对文件标识,一致则返回 304 响应码,不一致则返回 200 响应码【响应体为最新资源】

15.中间件

Django请求流程图

Django学习笔记_第8张图片

  • Django 中间件是是轻量级的请求/响应处理的钩子
  • 介于 HttpRequest 与 HttpResponse 处理之间的一道处理过程
  • 中间件以类的形式负责特定的功能

Django学习笔记_第9张图片

  • 中间件类需继承 django.utils.deprecation.MiddlewareMixin类

中间件配置

# 中间件配置
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',
    'middleware.mymiddleware.MyMW',
]

中间件类实现方法

中间件中大多数方法在返回None时表示忽略当前操作进入下一项事件,返回HttpResponse对象时表示此请求结束,直接返回给客户端。

class MyMW(MiddlewareMixin):
	# 执行路由前被调用,在每个请求上调用,返回None 或 HttpResponse对象
    def process_request(self,request):
        
        print('MyMW process_request do ---')
	# 调用视图前被调用,返回None 或 HttpResponse对象
    def process_view(self,request,callback,callback_args,callback_kwargs):

        print('MyMW process_views do --')
	# 所有响应返回浏览器 被调用,返回HttpResponse对象
    def process_response(self,request,response):
      
        print('MyMW process_response do ---')
        return response
    # 当处理过程中抛出异常时调用,返回一个HttpResponse对象 
    def process_exception(self,request,exception):
    	print('MyMW process_exception do ----')
      
    # 在视图函数执行完毕 且 试图返回的对象中包含 render 方法时被调用,
    # 该方法需要返回实现了 render 方法的相应对象
    def process_template_response(self,request,response):
        print('MyMW process_template_response')
        return response

中间件编写练习

  • 用中间件实现限制某个IP地址只能向/test开头的地址发送5次请求
  • 远程客户端的IP地址: request.META[‘REMOTE_ADDR’]
  • 客户端访问的请求路由信息:request.path_info
class VisitLimit(MiddlewareMixin):

    visit_times = {}
    def process_request(self,request):
        ip = request.META['REMOTE_ADDR']
        path_url = request.path_info
        if not re.match('^/note',path_url):
            return
        times = self.visit_times.get(ip,0)
        print('ip', ip,'已经访问',times)
        self.visit_times[ip] =times+1
        if times < 6:
            return
        return HttpResponse('您已经访问过'+str(times)+'次,访问被禁止')

CSRF

1.CSRF攻击

  • 跨站请求伪造(CSRF,即Cross-Site Request Forgey)
  • 某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作。

Django学习笔记_第10张图片

2.CSRF防范

  • settings.py中确认 MIDDLEWARE 是否已注册
  • 模板中,form表单中 添加 标签 { % csrf_token %}
# 中间件配置
MIDDLEWARE = [
    'django.middleware.csrf.CsrfViewMiddleware', 
]

3.CSRF局部关闭

若某个视图不需要django进行csrf保护,可以用装饰器 (csrf_exempt) 关闭对此视图的检查

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def my_view(request):
  return HttpResponse('Hello world')

16.分页

  • 在web页面上有大量数据需要显示,为了阅读方便在每个分页中只显示部分数据
  • 减少数据提取量,减轻服务器压力
  • Django提供了 Paginator类可以方便的实现分页功能

Paginator对象

paginator负责分页数据整体的管理

Paginator属性

  • count :需要分页数据的对象总数
  • num_pages : 分页后的页面总数
  • page_range :从1开始的range对象,用于记录当前页码数
  • per_page :每页数据的个数
from django.core.paginator import Paginator 
 # /test_page/3
def test_page(request,page_num):
  
  object_list= ['1','2','3','4','5']  # 需要分页数据的对象列表
  per_page = 3 # 每页数据个数
  
  # 初始化paginator
  paginator = Paginator(object_list,per_page)
  
  # 初始化 具体页码的page对象
  c_page = paginator(int(page_num))
  return render(request,'test_page.html', locals())

Page对象

负责具体某一页的数据的管理 ( page = paginator.page(number) )

若提供页码不存在,则抛出InvalidPage异常

InvalidPage 总的异常基类

  • PageNotAnInteger: 当向page() 传入一个不是整数的值 时抛出
  • EmptyPage: 当向page() 提供一个有效值,但是那个页面上没有任何对象时抛出

Page对象属性

  • object_list : 当前页上所有数据对象的列表
  • number : 当前页的序号,从1开始
  • paginator :当前page对象相关的Paginator对象
  • has_next() :如果有下一页返回True
  • has_previous() :如果有上一页返回True
  • has_other_pages() :如果有上一页或下一页返回True
  • next_page_number() :返回下一页的页码,如果不存在,抛出InvalidPage异常
  • previous_page_number() :返回上一页的页码,如果不存在,抛出InvalidPage异常

17.CSV、文件上传、邮件

1.CSV文件生成

  • 逗号分隔值(Comma-Separated Values, CSV,或称字符分隔值)
  • 以纯文本形式存储的表格数据(数字或文本)
  • 可被常见制表工具,如excel等直接进行读取

CSV文件下载注意

  • 响应Content-Type类型需修改为 text/csv,告诉浏览器该文档是CSV文件,不是HTML
  • 响应会获得一个额外的 Content-Disposition ,包含CSV文件名称,开启”另存为… “ 对话框
def csvload_note(request,page_num):

    # 获取当前页的数据
    uid = request.session.get('uid')
    notes = Note.objects.filter(user_id=uid, is_active=True)
    # 初始化 分页对象 paginator
    paginator = Paginator(notes, 5)
    # 初始化 具体页码的page对象
    c_page = paginator.page(page_num)
    
    # 修改响应头符合浏览器 csv 保存模式
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment;filename="note-%s.csv"' % (page_num)
    
    # 调用 csv 的写方法,并写入数据
    writer = csv.writer(response)
    writer.writerow(['id','titel','content','created_time','updated_time','user_id'])
    for note in c_page.object_list:
        writer.writerow([note.id,note.title,note.content,note.created_time,note.updated_time,note.user_id])
    return response

2.文件上传

  • 用户可以通过浏览器将图片等文件传至网站
  • 用户上传头像、上传流程性文档[pdf,txt等]

上传规范-前端【HTML】

  • 文件上传必须为POST提交方式
  • 表单’form’ 中文件上传时必须 带有 enctype=“multipart/form-data” 时才会包含文件内容数据
  • 表单中用 input type = “file” name=“xxx” 标签上传文件

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>上传文件title>
head>
<body>
<form action="/note/test_upload" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="text" name="title">
    <input type="file" name="myfile">
    <input type="submit" value="上传">

form>
body>
html>

上传规范-后端【Django】

视图函数中,用 file=request.FILES[‘xxxx’] 取 文件框的内容

  • 1.FILES 的 key 对应页面中 file框的name值
  • 2.file绑定文件刘对象
  • 3.file.name 文件名
  • 4.file.file文件的字节流数据

配置文件访问路径和存储路径

  • Django把用户上传的文件,统称为media资源
  • MEDIA_URL 和 MEDIA_ROOT需要手动绑定
# file:settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR/'media'

# 在主路由中 添加路由,
# 等价于 做了MEDIA_URL开头的路由,Django接到该特征请求后去MEDIA_ROOT路径查找资源
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
]
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

文件写入

# 方案一:传统的open方式 (不推荐),会出现文件重名判断并修改问题
@csrf_exempt
def upload_view(request):
    if request.method == 'GET':
        return render(request,'test_upload.html')
    elif request.method == 'POST':
        a_file = request.FILES['myfile']
        print('上传文件名:',a_file.name)
        filename = settings.MEDIA_ROOT/a_file.name
        with open(filename,'wb') as f:
            data = a_file.file.read()
            f.write(data)
        return HttpResponse("接收文件:"+a_file.name+"成功")

# 方案二:借助ORM 

# 新增模型类,(如Content),包含字段: FileField(upload='子目录名')
class Content(models.Model):
    desc = models.CharField('描述',max_length=11)
    myfile = models.FileField(upload_to='myfile')
# 视图操作    
def test_load(request):

    if request.method == 'GET':
        return render(request,'note/test_upload.html')
    elif request.method == 'POST':
        title = request.POST.get('title')
        a_file = request.FILES['myfile']
        Content.objects.create(desc=title,myfile=a_file)
        return HttpResponse('---上传文件成功---')

3.django发送邮件

  • 业务场景:1.业务告警 2.邮件验证 3. 密码找回

SMTP

  • (Simple Mail Transfer Protocol),即简单邮件传输协议(25号端口)
  • 是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转
  • 属于”推送”协议

IMAP

  • Internet Mail Access Protocol,即交互式邮件访问协议,是一个应用层协议(端口143)
  • 用来从本地邮件客户端(Outlook Express、Foxmail、MozillaThunderbird)访问远程服务器上的文件
  • 属于“拉取”协议

18.内建用户系统

  • Django带有一个用户认证系统,处理用户账号、组、权限以及基于cookies的用户会话
  • 用户可以直接使用Django自带的用户表
  • 官方文档:https://docs.djangoproject.com/en/4.0/topics/auth

User模型类结构

from django.contrib.auth.models import User

  • username - 用户名
  • password - 密码
  • email - 邮箱
  • first_name - 名
  • last_name - 姓
  • is_superuser - 是否是管理员账号(/admin)
  • is_staff - 是否可以访问admin管理界面
  • is_active - 是否是活跃用户,默认为True,伪删除用户
  • last_login - 上一次的登录时间
  • date_joined - 用户创建时间

Django学习笔记_第11张图片

用户系统操作

settings.py配置

# 未登录情况下的跳转地址
LOGIN_URL='/login'

以下含7种内置操作:

# 1.创建普通用户 create_user
from django.contrib.auth.models import User
user = User.objects.create_user(username='用户名',password='密码',...)
# 注意,此时密码由该方法 哈希加密过(不用自己重新加密)

# 2.创建超级用户 create_superuser
user = User.objects.create_superuser(username='用户名',password='密码',...)

# 3.校验密码 authenticate
from django.contrib.auth import authenticate
user = authenticate(username=username,password=password)
# 如果用户名密码检验成功,则返回对应的 User对象,否则返回 None

# 4.修改密码 set_password
try:
  user = User.objects.get(username='test')
  user.set_password('123456')
  user.save()
  return HttpResponse("修改密码成功!")
except:
  return HttpResponse("修改密码失败!")

# 5.登录状态保持 login
from django.contrib.auth import login
def login_view(request):
  user = authenticate(username=username,password=password)
  login(request,user)

# 6.登陆状态校验(login_required 装饰器 等同于 自建的check_login)
from django.contrib.auth decorators import login_required
@login_required
def index_view(request):
  # 该视图必须为用户登陆状态下才可访问,未登录跳转至 settings.LOGIN_URL
  # 当前登录用户可通过request.user 获取
  login_user = request.user   ....

# 7.登录状态取消 logout
from django.contrib.auth import logout
def logout_view(request):
  logout(request)

内建用户表-扩展字段

  • 方案1:通过建立新表,跟内建表做1对1(不推荐)
  • 方案2:继承 内奸的抽象User模型类

继承内建抽象类

  • 1.添加新的应用 - python manage.py startapp user
  • 2.定义模型类 继承 AbstractUser
  • 3.settings.py 中 指明 AUTH_USER_MODEL = ‘应用名.类名’
  • 4.必须在 第一次Migrate之前 进行,即未生成任何表之前,否则无效
# 1.添加user应用 python manage.py startapp user
# 2.user/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class UserInfo(AbstractUser):
  # 新增手机号,其余字段继承了 User表
  phone = models.CharField(max_length=11,default='')

# 3.settings.py添加配置
AUTH_USER_MODEL = 'user.UserInfo'

# 4.用户系统操作 和内建表 使用相同,如添加用户
from user.models import UserInfo
UserInfo.objects.create_user(username='test1',password='123456',phone='13211112131')

19.项目部署

在软件开发完毕后,将开发机器上运行的软件实际安装到服务器上进行长期运行

  • 1.在安装机器上安装和配置同版本的环境【py,数据库等】
  • 2.django项目迁移
  • sudo scp /home/pp/django/mysite1 [email protected]:/home/root/xxx
  • 3.用 uWSGI 替代 python manage.py runserver 方法启动服务器
  • 4.配置nginx 反向代理服务器
  • 5.用 nginx 配置静态文件路径,解决静态路径问题

1.uwsgi

WSGI (Web Server Gateway Interface) Web服务器网关接口

  • 是Python应用程序或框架 和 Web服务器之间的一种接口
  • python manage.py runserver 通常只在开发和测试环境中使用,开发结束后
  • 使用WSGI,保障完善的项目代码在一个高效稳定的环境中运行

uWSGI简介

  • 是WSGI的一种,实现了HTTP协议、WSGI协议以及uwsgi协议(2进制)
  • 功能完善,支持协议众多,在python web热度极高
  • uWSGI主要以学习配置为主

Ubuntu安装配置

  • 执行 sudo pip3 install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/
  • 检查是否安装成功 sudo pip3 freeze|grep -i ‘uwsgi’ ,成功则输出 uWSGI==2.0.18

配置uWSGI

  • 添加配置文件 项目同名文件夹/uwsgi.ini(mysite1/mysite1/uwsgi.ini)
  • Django的settings.py
  • 1.DEBUG =True 改为 False
  • 2.ALLOWED_HOSTS = [] 改为 网站域名 或 服务监听的ip地址
[uwsgi]
# 注意 socket 和 http 二选一 
# socket = 127.0.0.1:8000 # 套接字方式的IP地址:端口号【此模式需要有nginx】
http = 127.0.0.1:8000 # Http通信方式的IP地址:端口号
chdir = C:\Users\Administrator\tedu_note # 项目当前工作目录(绝对路径)
wsgi-file = tedu_note/wsgi.py # 项目中wsgi.py文件目录,当前工作目录(相对路径)
process = 4 # 进程个数, CPU核心数量最小值
theads = 2 # 每个进程的线程个数
pidfile = uwsgi.pid # 服务的pid记录文件
daemonize = uwsgi.log # 服务的日志文件位置
master = True # 开启主进程管理模式

启动/停止uwsgi

  • cd 到 uWSGI配置文件所在目录
  • uwsgi --ini uwsgi.ini
  • uwsgi --stop uwsgi.pid
  • 查看uwsgi状态 ps aux|grep ‘uwsgi’

uWSGI的运行说明

1.无论是启动还是关闭,都需执行 ps aux|grep ‘uwsgi’ 确认是否符合预期

2.启动成功后,进程在后台执行,所以日志输出在配置文件所在目录 uwsgi.log中

3.Django中代码有任何修改,需要重新启动uwsgi

常见问题:

1.启动失败:端口被占用(被其他进程占用uWSGI启动端口)

解决:可执行 sudo lsof -i:端口号 查询出具体进程;kill 进程后 sudo kill -9 PID,重启即可

2.停止失败:stop无法关闭uWSGI(重复启动uWSGI,导致pid文件中进程号失准)

解决:ps出WSGI进程,手动kill即可 sudo kill -9 PID

2.nginx

  • Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡等

  • C语言编写,执行效率高

  • nginx作用 :

    ​ -负载均衡、多台服务器轮流处理请求

    ​ -反向代理

  • 原理:

    ​ -客户端请求nginx,再由nginx将请求转发uWSGI进行的django

nginx安装

  • sudo apt install nginx 如果下载慢,考虑更换为国内源

    -vim /etc /apt/sources.list

    -sudo apt-get update

  • 安装后,ubuntu终端输入 nginx -v 查看nginx version 是否成功即可

  • 修改nginx的配置文件

    -sudo vim /etc/nginx/sites-enabled/default

    -关闭 vim,输入 :wq

    -检查配置文件语法 sudo nginx -t

# 在server节点下添加新的location项,指向uwsgi的ip与端口

server{
  location / {
    # try_files $uri/ =404  #需要将其禁用,对转发而言无用
    uwsgi_pass 127.0.0.1:8000;   # 重定向到127.0.0.1的8000端口
    include /etc/nginx/uwsgi_params; # 将所有的参数转到uwsgi下
  }
}

nginx启动/停止

# 方式1 :
sudo /etc/init.d/nginx start|stop|restart|status
# 方式2:
sudo service nginx start|stop|restart|status
# 注意:nginx配置只要修改,就需要进行重启,否则配置不生效

修改 uWSGI配置

说明nginx负责接收请求,并把请求转发给后面的uWSGI

此模式下,uWSGI需要以 socket 模式启动

[uwsgi]
# 套接字方式的IP地址:端口号【此模式需要有nginx】
socket = 127.0.0.1:8000
# 去掉http
# http = 127.0.0.1:8000

nginx 日志位置

  • 异常信息 /var/log/nginx/error.log
  • 正常访问信息 /var/log/nginx/access.log

uwsgi日志位置

  • 项目同名目录下/uwsgi.log

静态文件配置

  • 1创建新路径-主要存放Django所有静态文件 如:/home/pp/项目名_static
  • 2.在Django settings.py中添加新配置
STATIC_ROOT = '/home/pp/项目名_static/static'
#注意: 此配置路径为 存放正式环境中需要的静态文件
  • 3.进入项目 执行 python3 manage.py collectstatic,Django 将项目中所有静态文件 复制到 STATIC_ROOT 中,包括Django内建的静态文件
  • 4.在Nginx配置中添加新配置
# 进入file:sudo vim /etc/nginx/sites-enabled/default
# 新添加 location /static 路由配置,重定向到 指定文件路径

server{
  location /static {
    # root 第一步创建文件夹的绝对路径,如:
    root /home/pp/项目名_static;
  }
}

常见问题排查

1.访问127.0.0.1:80地址,502响应

502响应 代表nginx反向代理配置成功,但是对应的uWSGI未启动

2.访问127.0.0.1:80/url 404响应

1)路由的确不在django配置中

2)nginx配置错误,未禁止掉 try_files

你可能感兴趣的:(django,学习,python)