Django

Django 官网: https://www.djangoproject.com/

概念: Django 是:

  • Python web 框架
  • 服务端框架
  • MVT 设计模式

MVT 设计模式:

mvt.jpg


开始

  1. 安装虚拟环境, 避免第三方库的兼容性冲突
# 安装虚拟环境
pip install virtualenv
  1. 创建虚拟环境

环境部署

# 名为 env 的虚拟环境
virtualenv env

这个命令会创建一个名为 env 的文件夹

  1. 激活虚拟环境

到 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 应用:

djangoApp.jpg

创建应用:

# 格式 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)

django_model.jpg

通过创建一个类来创建对应表

具体的类型约束参考官方文档

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

一个关系表的例子

三个基础关系:

  • 一对一
  • 一对多
  • 多对多

查询函数

query_function_django.jpg

# 进入项目内的 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 的路径, 然后还需安装一个第三方库 whitenoise1

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/

Signals.jpg

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 %}

  1. whitenoise 并不管理用户上传的文件 ↩
最后修改:2024 年 02 月 21 日
如果觉得我的文章对你有用,请随意赞赏