Django
Django 官网: https://www.djangoproject.com/
概念: Django 是:
- Python web 框架
- 服务端框架
- MVT 设计模式
MVT 设计模式:
开始
- 安装虚拟环境, 避免第三方库的兼容性冲突
# 安装虚拟环境
pip install virtualenv
- 创建虚拟环境
环境部署
# 名为 env 的虚拟环境
virtualenv env
这个命令会创建一个名为 env
的文件夹
- 激活虚拟环境
到 env/Scripts/
目录下, 运行 activate
env/Scripts/activate
# 进入虚拟环境后的结果:
(env) 当前工作目录
如果要退出虚拟环境, 运行 deactivate
即可
安装
安装 Django 框架
pip install django
# 查看 Django 是否安装完成
django-admin
创建与运行
startproject
创建一个 Django 项目
该命令会创建一个文件夹, 包含几个模板 .py
文件
django-admin startproject devsearch
启动服务器:
cd devsearch
# 启动服务器
python manage.py runserver
参考: https://docs.djangoproject.com/en/4.2/intro/tutorial01/
wsgi.py
Web Server Gateway Interface (Web 服务器网关接口)asgi.py
Asynchronous Server Gateway Interface (异步服务器网关接口)
创建应用
Django 应用:
创建应用:
# 格式 python manage.py startapp <应用名>
python manage.py startapp projects
创建完应用后, 需要在 setting.py
中注册应用
# apps 是模块名
# ProjectsConfig 是类名
INSTALLED_APPS = [
...,
'projects'
]
视图 (模板) 和 路由
(Views and URL)
路由
要将创建的应用路径引入到根应用中, 需要在 项目名/urls.py
中添加子应用的路径
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
# 导入子应用的自定义url
path("", include("projects.urls")),
]
然后在 devsearch\projects
文件夹中创建 urls.py
from django.urls import path
# 导入视图模块
# 该处为自建的包核模块
from .views import projects, product
urlpatterns = [
# 此处为自定义的路径
# <str:pk> url参数
path("projects/", projects, name="projects"),
path("project/<str:pk>", product, name="product"),
]
视图 (模板)
在 setting.py
中加入视图模板的位置
# 设置模板目录
TEMPLATES = [
...,
# 模板的目录
# 该代码表示模板存放于当前脚本运行目录下的 template 目录
"DIRS": [
os.path.join(BASE_DIR, "templates"),
],
...
]
views.py
是逻辑层, 一般用于存放函数
在渲染视图前, 需指定要渲染的模板
# 函数的定义
def function_name(请求对象, url参数)
return render()
from django.shortcuts import render
def projects(request):
msg = "Hello, your are on meassage page"
# render 函数用于渲染页面
# 最后一个参数用于向模板传入数据
return render(request, "projects/projects.html", {"messge": msg})
视图格式
{% %}
为标识符
main.html
:
<!-- 引入 navbar.html 模板 -->
{% include 'navbar.html'%}
<!-- 插入模板起始位置 -->
{% block content %}
<!-- 插入模板结束位置 -->
{% endblock content %}
content.html
:
<!-- 插入至定义好的位置 -->
{% extends 'main.html' %}
模板数据
访问数据:
{{ 变量名 }} <br />
{{对象.属性}}
流程控制:
{% if a > b%}
<p>判断为真, 执行的结果</p>
{% elif %}
<p>同上</p>
{% else %}
<p>判断为假, 执行的结果</p>
{% endif %}
循环:
{% for item in list %}
<p>your loop content here</p>
{% endfor %}
如果我们将一个路径命了名, 我们可以引入路径名
# 路径名为 project
path("projects/", projects, name="project"),
<a href="{% url '路径名'%}"></a> <br />
<a href="{% url '路径名' 路径参数 %}"></a>
消息
message.success('消息...')
会将数据传入到模板的 messages
变量中, 类型为列表
message.tags
消息类型message
消息
from django.contrib import messages
message.success('成功的消息')
# 其它类型的消息详见代码补全
render(request, )
{% for message in messages %}
<li {% if message.tags %} class="{{ message.tags }}" {% endif %}>{{ message }}</li>
{% endfor %}
常用变量
<!-- 判断用户是否验证 -->
{% if request.user.is_authenticated %} {% endif %}
模型 (Model) 与 管理员面板
管理员面板
在我们运行 python manage.py runserver
这个命令时, 会看到以下报错
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
首先是在 setting.py
中的数据库设置, 设置数据库并没有任何数据
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
如同报错所说, 运行 python manage.py migrate
来进行初始化操作
此时, 数据库将会完成表单的初始化, 但我们还需创建一个管理员账号
运行 python manage.py createsuperuser
来创建管理员账号
创建完成后就可以访问管理员面板了: http://127.0.0.1:8000/admin
模型 (Model)
通过创建一个类来创建对应表
具体的类型约束参考官方文档
import uuid
# 创建的表
class Project(models.Model):
title = models.CharField(max_length=200) # 设置最大直段
description = models.TextField(null=True, blank=True) # 允许字段为空
demo_link = models.CharField(max_length=2000, null=True, blank=True)
source_link = models.CharField(max_length=2000, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True)
运行 python manage.py makemigrations
来创建迁移文件
Django 会在 migrations
文件夹创建 0001_initial.py
文件
然后将利用迁移文件将表创建至数据库 python manage.py migrate
python manage.py makemigrations
python manage.py migrate
当表创建至数据库, 我们还不能通过管理员面板查看刚刚创建的表
还需要将创建的模型注册进管理员面板中
在 应用名/admin.py
中注册模型
from .models import 模型类名
# 注册模型
admin.site.register(模型类名)
关系性数据库
一个关系可视化的网站: https://www.drawsql.app
三个基础关系:
- 一对一
- 一对多
- 多对多
查询函数
# 进入项目内的 python shell
python manage.py shell
表单模型
新建一个文件 forms.py
from django.forms import ModelForm
from .models import Project
# 创建一个表单模型
class ProjectForm(ModelForm):
class Meta:
model = Project
# 这是设置要渲染的字段
# 我们还可以用一个列表指定要渲染的字段
fields = "__all__"
然后在 views.py
中传入表单模型的实例
def createProject(request):
form = ProjectForm()
context = {"form": form}
return render(request, "projects/project_form.html", context)
最后在模板 html 中加入
Django 会自动将表模型的对应字段添加进模板
<!-- csrf_token 要写在表单标签里面 -->
<form action="" method="POST">
{% csrf_token %} {{form}}
<input type="submit" />
</form>
<!-- 将 input标签按行渲染 -->
{{form.as_p}}
<!-- 渲染表单中的某个字段 -->
{{form.title}}
<!-- 渲染字段的名字-->
{{form.title.label}}
<!-- 循环渲染 -->
{% for field in form %} {{field.label}} {{field}} {%endfor%}
自定义表单
用于自定义在模板中的渲染, 通常是修改样式
from django.contrib.auth.forms import UserCreationForm
from django.forms.fields import CharField, EmailField
class ExampleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# super(ExampleForm, self).__init__(*args, **kwargs) # python 2.x
super().__init__(*args, **kwargs) # python 3.x
# 自定义特定字段
self.fields["title"].widget.attrs.update(
{"class": "input", "placeholder": "add title"}
)
# 将所有字段的class改为 'input'
for _, field in self.fields.items():
# 指定类型
current_field: CharField | EmailField = field
print(current_field.widget.attrs.update({'class': 'input'}))
self.fields
为字典, .items()
方法可将其转换为可迭代对象
field.widget
为 HTML 元素 (与fileds['字段名']
等价)field.widget.attrs
类型为字典, 为 HTML 属性
CRUD 操作
CRUD (Create Read Update Delete)
在 views.py
中
- CREATE 操作
from django.shortcuts import render, redirect
# 接受提交过来的表单, 并将其转换为字典
request.POST
def createProject(request: HttpRequest):
form = ProjectForm()
if request.method == "POST":
form = ProjectForm(request.POST)
# 校验表单是否合法
if form.is_valid():
form.save()
return redirect('projects')
context = {"form": form}
return render(request, "projects/project_form.html", context)
- UPDATE 操作
与创建有些许不同, 主要是要指明要更新的对象
def updateProject(request: HttpRequest, pk: str):
project = Project.objects.get(id=pk)
form = ProjectForm(instance=project)
if request.method == "POST":
form = ProjectForm(request.POST, instance=project)
if form.is_valid():
form.save()
return redirect("projects")
context = {"form": form}
return render(request, "projects/project_form.html", context)
静态文件
不难, 看官方文档
在 setting.py
文件下, 静态文件夹的设置可以这么写:
STATICFILES_DIRS = [BASE_DIR / "static"]
用户图片
在模型层 models.py
设置 models.ImageField()
字段, 可以通过管理页面上传图片
设置这个字段需要安装 Pillow
库
# 静态文件
feature_image = models.ImageField(null=True, blank=True, default="default.jpg")
如果没有在 setting.py
中设置 MEDIA_ROOT
, 上传的图片都会存储在项目根目录中
在 setting.py
中设置 MEDIA_ROOT
和 MEDIA_URL
MEDIA_URL = "images/"
MEDIA_ROOT = os.path.join(BASE_DIR, "static/images")
接着, 在项目的 urls.py
中, 添加相关路径
from django.conf import settings
from django.conf.urls.static import static
urlpartterns = [...]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
在模板中, 通过访问数据库来获取其图片路径, 要注意的是, 这里的 project
是从 View 层传入的对象
<img src="{{ project.feature_image.url }}" alt="head" />
上传的表单
enctype
指定提交的表单类型
<form action="" method="POST" enctype="multipart/form-data">
......
<input type="submit" />
</form>
要让 Django 接受表单传过来的文件, 需要在让 Model 对象接受 request.FILES
这样, 文件和数据库的信息将被一同存储下来
if request.method == "POST":
form = ProjectForm(request.POST, request.FILES)
生产环境下的静态托管
到目前为止, Django 的静态文件不是为生产环境准备的
事实上, Django 官方并不推荐用这样的方式管理静态文件
官方推荐采用托管的方式管理和存储静态文件
在 setting.py
中, 将 DEBUG
设为 False
, 设置的静态文件将不在生效
# 在生产环境下, 只有该静态文件设置有效
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# 将所有 DEBUG=True 下设置的静态文件打包指 STATIC_ROOT 中
python manage.py collectstatic
将 DEBUG
设为 True
并打包静态文件后, 以前的设置将不在生效
需要重新设置 urls.py
的路径, 然后还需安装一个第三方库 whitenoise
1
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
whitenoise
的安装, 查看 官方文档
pip install whitenoise
用户
Django 自带一个名叫 User
和 Group
的数据库对象, 用于管理员面板的使用
通过继承 User
对象来实现自建的用户表, 是依赖于 Django 的用户验证系统的, 如果在使用过程中对该
用户表有不当操作, 会导致整个 Django 的验证出现问题
因此, 建议再重新创建一个名为 Profile
的数据库对象, 来与 Django 自带的用户表做以分离,实以现自建的用户表
from django.contrib.auth.models import User, Group
信号 Signals
建议看看官方文档: https://docs.djangoproject.com/en/5.0/topics/signals/
Django 接受的信号:
post_save
post_delete
...
在 models.py
中, 编写代码:
from django.db.models.signals import post_save, post_delete
# 定义接受器
def pfunction_name(sender, instance, created: bool, **kwargs):
# instance 发送的实例
# created 是否创建
pass
def deleteUser(sender, instance, **kwargs):
print("Deleting User...")
# 设置接受器
# 参数 (接收器, 发送者)
post_save.connect(profileUpdated, sender=Profile)
post_delete.connect(deleteUser, sender=Profile)
装饰器写法:
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
# 定义接受器
@receiver(post_save, sender=Profile)
def pfunction_name(sender, instance, created: bool, **kwargs):
pass
如果想将信号相关的代码从 models.py
中分离出来, 除了将上面所有的代码放入 signals.py
中
还需在 apps.py
中注册分离出来的代码文件
class UsersConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "users"
# 将 "signals.py" 注册进该应用中
def ready(self) -> None:
import users.signals
用户认证 (Session)
当使用 Django 执行数据库初始迁移命令时
Django 会创建一个名叫 django_session 的表单
该表存储用户的 session(用户会话), 将用户的 session ID 存储进表
浏览器的 sessionid 值对应数据库中的 session_key
登录
在 views.py
中创建用户登录:
from django.contrib.auth import login, authenticate
from django.contrib.auth.models import User
from django.http import HttpRequest
def loginPage(request: HttpRequest):
if request.method == "POST":
print(request.POST)
username = request.POST["username"]
password = request.POST["password"]
try:
user = User.objects.get(username=username)
except:
# 这里可以改为 message.error(request, 'User not exists!')
# 官方文档: https://docs.djangoproject.com/en/4.2/ref/contrib/messages/#adding-a-message
print("User not exists!")
# 通过用户名和密码获取用户实例
user = authenticate(request, username=username, password=password)
if user is not None:
# 将 session 存储进数据库, 并让浏览器保存 session id
login(request, user)
return redirect("profiles")
else:
print("Username or password is incorrect")
return render(request, "users/login_register.html")
登出
让用户登出:
from django.contrib.auth import logout
def logoutUser(request: HttpRequest):
logout(request)
return redirect("profiles")
限制访问
如果是基于函数的 views.py
的代码
可以通过 login_required
修饰器来限制未授权访问
from django.contrib.auth.decorators import login_required
@login_required(login_url='login')
def createSomthing(request):
...
return render('somthing')
注册
使用内置的表单构建类
from django.contrib.auth.forms import UserCreationForm
def registerUser(request: HttpRequest):
page = "register"
form = UserCreationForm()
context = {"page": page, "form": form}
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
# commit = False 暂时不提交
user: User = form.save(commit=False)
user.username.lower()
user.save()
messages.success(request, "User account was created!")
login(request, user)
return('profiles')
return render(request, "users/login_register.html", context)
<!-- 渲染表单模板 -->
{{ form.as_p }}
自定义注册表单
可以通过 {{ for field in form }}
循环 Form
对象来取消掉 UserCreationForm
上的多余消息
但在模板中无法修改 Form
的 class 类名和后缀, 需要到数据模型那去修改
{% for field in form %}
<!-- 帮助文本 -->
{{ field.help_text }}
<!-- 错误列表, 使用时需循环 -->
{% field.errors %}
- whitenoise 并不管理用户上传的文件 ↩