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_ROOTMEDIA_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 自带一个名叫 UserGroup 的数据库对象, 用于管理员面板的使用

通过继承 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 日
如果觉得我的文章对你有用,请随意赞赏