Django model 项目配置详解

setting.py 配置文件

在 Django 的 settings.py 文件中,LANGUAGE_CODE 和 TIME_ZONE 是重要的配置选项,它们分别用于设置语言代码和时区。这两个设置可以根据您的需求进行自定义,但有一些规范和建议需要遵循。

LANGUAGE_CODE

LANGUAGE_CODE 用于指定您应用的默认语言。默认为:

LANGUAGE_CODE = 'en-us'  # 英文(美国)
  • 可用值
    • 该值应该是有效的语言代码,通常使用 ISO 639-1 标准(两个字母的语言代码)和国家/地区的 ISO 3166-1 标准(两个字母的国家代码),以连字符(-)连接。
    • 常见值:
      • 英文(美国):'en-us'
      • 中文(简体):'zh-hans'
      • 中文(繁体):'zh-hant'
      • 西班牙语:'es'
      • 法语:'fr'

TIME_ZONE

定义TIME_ZONE 用于设置系统的时区。默认为:

TIME_ZONE = 'UTC'  # 设置为协调世界时
  • 可用值
    • TIME_ZONE 应该是 Django 支持的有效时区,通常可以从 IANA 时区数据库 中查找。
    • 常见值:
      • 协调世界时:'UTC'
      • 北京时间:'Asia/Shanghai'
      • 纽约时间:'America/New_York'
      • 伦敦时间:'Europe/London'
ALLOWED_HOSTS
定义:安全设置项,用来防止 HTTP Host 头攻击(Host header attack)。它是你项目中的一个列表,用于声明 Django 允许接收请求的主机名(域名或IP)。

settings.py 中的配置示例

ALLOWED_HOSTS = ['localhost', '127.0.0.1', 'www.example.com']

注意:runserver 要监听在正确地址。

默认运行:python manage.py runserver 等价于 python manage.py runserver 127.0.0.1:8000,只能本机访问,其他设备访问不到。所以设置正确监听所有地址方式:python manage.py runserver 0.0.0.0:8000或简写为python manage.py runserver 0:8000

静态文件添加

在 Django 项目中,静态文件(如 CSS、JavaScript 和图像文件)的管理和组织是至关重要的。合理地配置静态文件目录可以确保项目的可维护性和性能。以下是关于 Django 项目中静态文件的组织、获取逻辑以及与其他框架的不同之处的详细解释。

项目级别的静态目录结构

将静态文件放在项目的根目录下,通常有一个专门的 static 目录。

mysite/
├── static/
│   ├── css/
│   │   └── style.css
│   ├── js/
│   │   └── script.js
│   └── images/
│       └── my_image.jpg
├── manage.py
└── mysite/
    └── settings.py

多个 Django 应用,可以在每个应用中创建一个 static 目录,通常结构如下:

blog/
│
├── static/
│   └── blog/
│       ├── css/
│       │   └── blog.css
│       ├── js/
│       │   └── blog.js
│       └── images/
│           └── blog_logo.png
│
├── templates/
│   └── blog/
│       └── index.html
├── models.py
├── views.py
└── ...

在 settings.py 中,添加以下配置,告诉 Django 如何处理静态文件:

import os

# BASE_DIR 表示项目根目录
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

# 静态文件的 URL (即在浏览器中访问时的前缀)
STATIC_URL = '/static/'

# 指定静态文件的文件夹
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static'),  # 项目级别的静态文件目录
]

# 你可以使用这个选项来定义不在 STATICFILES_DIRS 里的应用的静态文件的位置,通常不需要手动设置

使用静态文件

首先在模板文件顶部加载静态文件的模板标签库:

{% load static %}

然后可以使用 {% static %} 标签引用静态文件。例如,引用 CSS 和 JavaScript 文件:

<link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">
<script type="text/javascript" src="{% static 'js/script.js' %}"></script>

基于项目https://www.ljh.cool/wp-admin/post.php?post=43305&action=edit

继续完善静态文件内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Blog Index</title>
    
    <!-- 加载静态文件 -->
    {% load static %}
    <link rel="stylesheet" type="text/css" href="{% static 'css/style.css' %}">  <!-- 引用 CSS 文件 -->
    <script type="text/javascript" src="{% static 'js/script.js' %}"></script>  <!-- 引用 JS 文件 -->
</head>
<body>
    <h1>Welcome to the Blog Index!</h1>
    <p>This is where you can find the latest posts.</p>

    <!-- 添加静态图片 -->
    <h2>My Image:</h2>
    <img src="{% static 'images/my_image.jpg' %}" alt="My Image" style="max-width: 100%; height: auto;">
    
    <h2>Posts:</h2>
    <ul>
        {% for post in posts %}
            <li>
                <strong>{{ post.title }}</strong><br>
                {{ post.content }}<br>
                <em>Published on: {{ post.created_at }}</em>
            </li>
        {% empty %}
            <li>No posts available.</li>
        {% endfor %}
    </ul>
</body>
</html>

打开浏览器,访问静态资源,如 http://127.0.0.1:8000/static/images/my_image.jpg

Django model 项目配置详解

通过访问 http://127.0.0.1:8000/blog/ 加载静态资源

Django model 项目配置详解

apps.py 配置

在 Django 中,每个应用(app)都有一个 apps.py 文件,里面定义了一个应用的配置类。默认情况下,当您创建一个新应用时,Django 会自动为您生成这个文件。

在 apps.py 中,您将定义一个继承自 django.apps.AppConfig 的类。这个类主要包含以下内容:

  • 名称 (name):指定应用的名称,通常是该应用的模块名。
  • 标签 (label):用于唯一标识该应用的标签,可以是与名不同(可选)。
  • 初始化 (ready):这是用于初始化日志、信号等的地方。

使用 AppConfig

信号(Signals):Django 信号框架用来处理在特定事件发生时自动执行的逻辑。例如,您可以在创建模型实例时发送通知或记录日志。

应用信号:如果您使用了 Django 的信号机制(例如 post_save 或 pre_save),通常会在 ready 方法中进行连接。示例如下:

from django.apps import AppConfig
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Post

class BlogConfig(AppConfig):
    name = 'blog'
    verbose_name = "Blog Application"

    def ready(self):
        # 在这里连接信号
        from . import signals

信号的工作原理

  • 信号 是 Django 的一种机制,允许应用程序在某些事件发生时执行特定的操作。例如,当新的 Post 模型实例被保存时,您可能希望自动执行某些操作(如更新其他模型、发送通知等)。
  • post_save 信号 是在模型实例保存后自动发送的信号。您可以定义一个接收器(receiver)函数来响应这个信号:
@receiver(post_save, sender=Post)
def post_save_handler(sender, instance, created, **kwargs):
    if created:
        print(f"New post created: {instance.title}")
    else:
        print(f"Post updated: {instance.title}")

以下是一个示例 apps.py 文件的结构,以 blog 应用为例:

from django.apps import AppConfig


class BlogConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'blog'
    verbose_name = "Blog Application"  # 应用的可读名称
default_auto_field = 'django.db.models.BigAutoField'
  • 从 Django 3.2 开始,引入了 default_auto_field 选项来指定生成模型主键的默认类型。BigAutoField 是一个 64 位整数,可以自动递增,适用于希望支持更大数量记录的应用。
  • 使用 BigAutoField 比较适合大型应用,因为它允许比默认的 AutoField(32 位整数)支持更多的记录。

这个配置类有什么用?

  • 全局配置AppConfig 可以用于管理 Django 应用的元数据,配置如自动字段、显示名称等。
  • 信号连接:在将来使用信号(如保存、删除模型时)时,您可以在类的 ready 方法中设置信号的连接。
  • 更容易的管理和扩展:通过使用应用程序配置,您能够灵活地管理应用程序的行为。例如,您可以根据环境变量来设置特定于环境的行为。

model.py 模型概述

Django 模型是用于定义数据结构的 Python 类,通常继承自 django.db.models.Model。模型允许开发者通过 Python 代码描述数据库中的表和字段,Django 的 ORM(对象关系映射)自动将模型转换为数据库表。

为什么使用模型?

  1. 数据抽象:通过模型,您无需直接编写 SQL 语句,Django 会处理与数据库的交互。
  2. 数据验证:模型字段提供数据类型和约束,Django 在处理数据前进行验证。
  3. 易于维护:模型使代码更清晰,便于集中管理与数据相关的逻辑。

Django 模型组成部分

1、类定义
模型定义为一个 Python 类,例如:

from django.db import models

class Post(models.Model):
    ...

2、字段定义:

模型属性定义为字段,设置数据类型和约束。常用字段类型包括:

  • CharField:用于存储短文本。需要设置最大长度。
  • TextField:用于存储长文本,没有长度限制。
  • IntegerField:用于存储整数。
  • FloatField:用于存储浮点数。
  • DecimalField:用于存储固定精度的十进制数,适合金融计算。
  • DateField:用于存储日期。
  • DateTimeField:用于存储日期和时间。
  • BooleanField:用于布尔值(True 或 False)。
  • EmailField:用于存储电子邮件地址,自动验证格式。
  • URLField:用于存储 URL 地址。
  • ImageField/FileField:用于处理文件上传。

3、方法定义:

可以定义方法增加业务逻辑,例如重载 __str__ 方法,使其返回可读字符串表示。

def __str__(self):
    return self.title

Django 模型字段选项

null:指示数据库是否可以存储 NULL 值。

title = models.CharField(max_length=200, null=True)

blank:控制该字段在表单中是否可留空。

title = models.CharField(max_length=200, blank=True)

unique:指示字段在整个表中必须保持唯一性。

email = models.EmailField(unique=True)

default:设置字段的默认值。

status = models.CharField(max_length=20, default='draft')

完整的模型示例

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200, null=False, blank=False, unique=True)
    content = models.TextField(null=True, blank=True)
    author_email = models.EmailField(unique=True)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)
    created_at = models.DateTimeField(auto_now_add=True)
    is_published = models.BooleanField(default=False)
    
    def __str__(self):
        return self.title

在修改模型后,需要运行以下命令生成并应用迁移,以更新数据库结构:

python manage.py makemigrations  # 生成迁移文件
python manage.py migrate         # 更新数据库

利用 Django ORM 进行操作

使用 Django ORM,通过创建、查询、更新和删除记录进行数据操作:

# 创建记录
new_post = Post(title="My First Post", content="This is the body of my post.")
new_post.save()  # 保存到数据库

# 查询记录
all_posts = Post.objects.all()  # 获取所有文章
specific_post = Post.objects.get(id=1)  # 获取特定文章

# 更新记录
post_to_update = Post.objects.get(id=1)
post_to_update.title = "Updated Title"
post_to_update.save()  # 保存更改

# 删除记录
post_to_delete = Post.objects.get(id=1)
post_to_delete.delete()  # 删除该文章

模型外键定义

在 Django 中,您可以使用 ForeignKey 字段在模型中建立外键关系。下面是如何实现的详细示例。

假设我们要创建一个博客应用,其中的 Post 模型可以有许多评论(Comment),而每个评论是对应某个帖子的。您可以在 Comment 模型中定义一个外键来指向 Post 模型。

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200, null=False, blank=False, unique=True)
    content = models.TextField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.title

class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE)  # 外键字段,用于链接到 Post
    content = models.TextField(null=False, blank=False)       # 评论内容
    created_at = models.DateTimeField(auto_now_add=True)      # 评论创建时间

    def __str__(self):
        return f'Comment on {self.post.title}'  # 显示评论相关的帖子标题

重要参数说明

在 ForeignKey 中,您可以设置一些重要的参数:

on_delete:指示当关联的对象被删除时,外键字段的行为。

  • models.CASCADE:级联删除,如果关联的帖子被删除,则相关的评论也会被删除。
  • models.SET_NULL:设置为 NULL,前提是外键字段允许 NULL
  • models.PROTECT:防止删除关联对象,如果尝试删除将抛出错误。
  • models.SET_DEFAULT:将外键设置为字段的默认值。

related_name:用于指定反向关系的名称。在 Comment 模型中可以定义反向关系。例如:

post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')

这样可以通过 post_instance.comments.all() 获取与该 post_instance 相关的所有评论。

Django ORM 和外键的使用

有了外键字段后,您可以用 Django 的 ORM 轻松查询和管理这两个模型之间的关系。

创建记录

# 创建一个 Post
new_post = Post(title="My First Post", content="This is the content of my first post.")
new_post.save()

# 创建与 Post 关联的 Comment
comment1 = Comment(post=new_post, content="Great post!")
comment1.save()

查询记录

你可以从 Post 模型中获取所有相关的评论:

post = Post.objects.get(id=1)  # 获取 ID 为 1 的帖子
comments = post.comments.all()   # 获取与该帖子关联的所有评论
for comment in comments:
    print(comment.content)

修改表名

以下是如何在 Django 中修改表名的示例:

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200, null=False, blank=False, unique=True)
    content = models.TextField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        db_table = 'custom_post_table'  # 指定自定义表名

在这个例子中,db_table 属性被设置为了 'custom_post_table',这意味着即使 Django 的默认行为下表名为 blog_post(即应用名称加下划线和模型名称),实际在数据库中将会创建一个名为 custom_post_table 的表。

注意事项:如果已经迁移过数据库并且希望修改表名,您需要执行以下步骤:

  • 修改模型的 Meta 类以包含 db_table。(添加class Meta: db_table ='xxx')
  • 执行 makemigrations 命令生成迁移文件。(python manage.py makemigrations)
  • 执行 migrate 命令更新数据库。(python manage.py migrate

枚举类型

枚举(enum)是一种特殊的类型,可以让你定义一组固定和有限的选项。比如,你可以用它来表示文章的状态,比如 "草稿"(draft)、"已发布"(published)和 "归档"(archived)。使用枚举可以让你的代码更清晰,避免使用字符串。

为什么使用枚举?

  1. 限制选项:它只允许你选择特定的值。比如,对于文章的状态,你只能选择 "草稿"、"已发布" 或 "归档"。
  2. 易于维护:如果你想改变或添加状态,只需要在一处修改,不需要到处找。

在 Django 中,你可以通过模型的 choices 属性来使用枚举。这里有一个简单的例子:

from django.db import models

class Post(models.Model):  # 定义一个 Post 类(文章)
    
    # 定义文章状态的三个选项
    class Status(models.TextChoices):
        DRAFT = 'draft', '草稿'
        PUBLISHED = 'published', '已发布'
        ARCHIVED = 'archived', '归档'

    # 定义标题字段
    title = models.CharField(max_length=200, unique=True)  # 限制最多200个字符,且名称必须唯一
    # 定义内容字段
    content = models.TextField()  # 可以存储任意长的文本
    # 定义状态字段,用于选取枚举值
    status = models.CharField(max_length=10,
                              choices=Status.choices,  # 允许的选项
                              default=Status.DRAFT)  # 默认状态是草稿
    # 创建时间,自动设置为当前时间
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title  # 显示文章的标题
  1. 状态定义:我们创建了一个内部类 Status,通过 TextChoices 定义了三个状态:
    • DRAFT 值为 'draft',中文显示为 '草稿'
    • PUBLISHED 值为 'published',中文显示为 '已发布'
    • ARCHIVED 值为 'archived',中文显示为 '归档'
  2. 标题和内容title 字段是用来存文章的标题,而 content 字段是用来存文章的内容。
  3. 状态字段
    • status 字段定义了可以选择的状态,它只能是 'draft''published' 或 'archived' 中的一个。
    • 如果没有手动设置状态,默认值是 'draft'

修改数据库引擎

我这里在 macOS 上操作,对于 Ubuntu 或 windows 用户自行搜索文档:

步骤 1: 安装 MySQL

brew install mysql

步骤 2: 安装 mysqlclient 库

安装相关的开发工具

安装 mysqlpkg-config 依赖

brew install mysql pkg-config
  • pkg-config 是编译期间用来寻找头文件和库路径的工具
  • mysql 安装后会带有 mysql_config

您可能需要安装 pkg-config 及一些开发库。

brew install pkg-config
brew install openssl
brew install mariadb-connector-c

设置环境变量(在虚拟环境中配置)

export PATH="/usr/local/opt/openssl/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"

安装 mysqlclient

pip install mysqlclient
Django model 项目配置详解

步骤 3: 修改 settings.py 配置

打开 Django 项目中的 settings.py 文件,找到 DATABASES 部分。您需要将其更改为 MySQL 数据库的配置。以下是一个使用 MySQL 的示例配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 设置为 MySQL
        'NAME': 'your_database_name',          # 您的数据库名称
        'USER': 'your_username',               # 您的用户名
        'PASSWORD': 'your_password',           # 您的密码
        'HOST': 'localhost',                   # 数据库主机
        'PORT': '3306',                        # MySQL 的默认端口
    }
}

步骤 4: 创建数据库

创建您在 settings.py 中指定的数据库:

CREATE DATABASE your_database_name CHARACTER SET UTF8mb4 COLLATE utf8mb4_unicode_ci;

步骤 5: 迁移数据

在修改数据库引擎并创建数据库后,您需要运行以下命令来生成迁移文件并应用迁移,以在新的 MySQL 数据库上创建所有表:

python manage.py makemigrations
python manage.py migrate

步骤 6: 测试连接

python manage.py runserver ,然后在浏览器中访问 http://127.0.0.1:8000/,查看应用是否正常工作。

例如:我们将https://www.ljh.cool/43305.html的数据库从 sqlite3 迁移到 mysql 过程:

# 创建数据库:
CREATE DATABASE blog_db CHARACTER SET UTF8mb4 COLLATE utf8mb4_unicode_ci;

# 修改 settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 设置为 MySQL
        'NAME': 'blog_db',           # 您的数据库名称
        'USER': 'root',              # 您的用户名
        'PASSWORD': 'XXXXXXXX',      # 您的密码
        'HOST': 'localhost',         # 数据库主机
        'PORT': '3306',              # MySQL 的默认端口
    }
}

# 迁移数据
python manage.py makemigrations
python manage.py migrate

查看迁移结果:

Django model 项目配置详解

后台登录插入一些内容,查看数据库是否成功插入

Django model 项目配置详解
Django model 项目配置详解

额外补充:假如使用pymysql

如果您使用 mysqlclient 并且成功地连接了 MySQL 数据库,那么您无需进行额外的配置。默认情况下,Django 会自动识别 mysqlclient。但是,如果您决定使用 pymysql 作为 MySQL 的数据库驱动程序,您需要在 Django 项目的 __init__.py 文件中添加以下代码:

在 mysite/__init__.py 中添加:

import pymysql
pymysql.install_as_MySQLdb()

这段代码的作用是让 pymysql 作为 MySQLdb 库来使用,因为 Django 的 MySQL 数据库后端默认是 MySQLdb。这就可以确保 Django 可以使用 pymysql 来与 MySQL 数据库进行交互。如果不在工程包中导入pymysql,会在执行迁移时报错:No module named 'MySQLdb'

Python 的 shell 调试 Django 应用程序

使用 Python 的 shell(REPL 环境)来调试 Django 应用程序可以帮助您理解 ORM(对象关系映射)如何工作,以及如何与数据库交互。您可以在 Django 的 shell 中执行操作,查询数据库等。

使用 Django Shell

在 Django 项目的根目录下(与 manage.py 位于同一目录)打开命令行或终端,然后输入以下命令进入 Django shell:

python manage.py shell

这将启动一个 Python 交互式 shell,您可以在其中直接使用 Django 的模型和功能。

一些基本操作

导入模型
首先,您需要导入 Post 模型:

from blog.models import Post

这里的 blog 是您的 Django 应用名称,如果您的应用名称不同,请相应地更改。

创建新文章
例如,您可以使用以下代码创建一个新的 Post 实例:

实例方法创建

new_post = Post(title="My First Post", content="This is the content of my first post.")
new_post.save()  # 保存到数据库

查询集方法创建

在需要更多灵活性和控制的情况下,使用 .save() 方法会更合适。如果您仅仅是创建新对象并保存,无需复杂逻辑,Post.objects.create() 可能是更为优雅和简洁的选择。

Post.objects.create(title="My Second Post", content="This is the content of my second post.")

批量创建

使用 bulk_create() 方法可以一次性创建多个对象。例如,假设我们要创建多个新的 Post 对象,可以这样做:

from yourapp.models import Post  # 替换为你的实际应用和模型导入

# 创建多个 Post 对象
posts_to_create = [
    Post(title="博客文章 1", content="内容 1", status="已发布"),
    Post(title="博客文章 2", content="内容 2", status="草稿"),
    Post(title="博客文章 3", content="内容 3", status="已发布"),
]

# 批量创建
Post.objects.bulk_create(posts_to_create)
  • 实例方法适合于你需要获取具体的对象实例时,便于后续对该实例的其他操作。
  • 查询集方法适合于批量操作,且更为高效和简洁,特别是在处理大量数据时。

更新文章
您可以更新某个文章的标题或内容:

实例方法更新

post = Post.objects.get(id=1)  # 获取 ID 为 1 的 Post
post.title = "修改标题后的博客文章 1"  # 更新标题
post.save()  # 保存更改

查询集方法更新

当你要更新多条记录时,可以使用 update() 方法借助 filter() 来实现 。filter() 返回的结果是多个对象的列表

Post.objects.filter(id=1).update(title="修改标题后的博客文章 1")

删除文章

使用 get() 方法获取对象并调用 delete() 方法来删除数据

post_to_delete = Post.objects.get(id=1)  # 获取要删除的 Post
post_to_delete.delete()  # 删除该 Post

使用 filter() 方法结合 delete() 方法来删除数据

Post.objects.filter(id=1).delete()

Django Shell 查询操作(重点)

目前数据库内容:

Django model 项目配置详解

1. 查询所有文章
获取所有的帖子并输出它们:

posts = Post.objects.all()  # 获取所有 Post 数据,返回结果为列表
for post in posts:
    print(post.title)  # 打印每个 Post 的标题

2. 获取特定文章
如果您知道某个帖子的 ID,可以轻松获取该帖子的内容:

post = Post.objects.get(id=1)  # 根据 ID 获取 Post,使用 get() 返回结果为单个对象
print(post.content)  # 打印该 Post 的内容

3. 使用特定字段查询

如果不知道帖子的 ID,可以通过其他属性进行查询。例如,通过标题或作者:

例如通过标题查询(如果查询的结果不存在,建议进行异常捕获)

# 假设要查找标题为 "博客文章 1" 的 Post
try:
    post = Post.objects.get(title="博客文章 1")  # 根据标题获取 Post
    print(post.content)  # 打印该 Post 的内容
except Post.DoesNotExist:
    print("没有找到该标题的帖子")

4. 使用 filter() 方法

filter() 方法可以用来根据某些条件获取多个对象。可以链式使用多个过滤条件:

# 获取状态为 "已发布" 的所有文章
published_posts = Post.objects.filter(status='已发布')
for post in published_posts:
    print(post.title)

你还可以使用 Q 对象组合查询条件,支持 OR 查询:

假设你想查找标题包含 "博客" 或者内容中包含 "first" 的文章。可以使用 Q 对象:

from django.db.models import Q

# 查询标题包含 "博客" 或内容中包含 "first" 的帖子
posts = Post.objects.filter(Q(title__icontains='博客') | Q(content__icontains='first'))
for post in posts:
    print(f'Title: {post.title}, Content: {post.content}')

5. 排序和限制结果

你可以使用 order_by() 方法对查询结果进行排序,使用 first() 或 last() 获取第一条或最后一条记录。

# 获取按发布日期降序排列的所有文章
# 按创建时间降序获取所有文章
posts = Post.objects.order_by('-created_at')
for post in posts:
    print(f'Title: {post.title}, Created At: {post.created_at}')

# 获取创建时间最新的文章
latest_post = Post.objects.order_by('-created_at').first()
if latest_post:
    print(f'最新文章: Title: {latest_post.title}, Created At: {latest_post.created_at}')

6. 使用切片

你可以使用切片来限制返回的结果数,例如获取前十条记录:

# 获取前两篇文章
top_two_posts = Post.objects.all()[:2]
for post in top_two_posts:
    print(f'Title: {post.title}, Created At: {post.created_at}')

7. 计数

你可以使用 count() 方法获取符合某个条件的记录数:

# 获取文章总数
total_posts = Post.objects.count()
print(f'总文章数: {total_posts}')

Django ORM 构建新表

我们要创建两个新的表:Author 表和 Comment 表

在 models.py 文件中定义两个新表对象:

from django.db import models  # 导入 Django 的模型包,模型定义替代了数据库表的设计

# 定义作者模型,表示每个博客的作者
class Author(models.Model):
    name = models.CharField(max_length=100)  # 作者的名字,使用 CharField 字段类型,最大长度为 100
    bio = models.TextField(blank=True, null=True)  # 作者介绍,使用 TextField 字段,允许空值和 blank(不强制填写)

    def __str__(self):
        return self.name  # 返回作者名称,用于表示该对象,比如在 Django Admin 中方便查看
        

# 定义帖子模型,表示每篇博客文章
class Post(models.Model):
    title = models.CharField(max_length=200)  # 帖子的标题,使用 CharField,最大长度为 200
    content = models.TextField()  # 帖子的内容,使用 TextField,可以容纳长文本
    created_at = models.DateTimeField(auto_now_add=True)  # 帖子创建时间,使用 DateTimeField,会在创建时自动设置为当前时间
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='posts')  # 与 Author 模型之间的一对多关系,关联作者

    def __str__(self):
        return self.title  # 返回帖子的标题,方便在代码中的显示


# 定义评论模型,表示对帖子进行的评论
class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')  # 与 Post 模型之间的外键关系,一个评论属于一个帖子
    author = models.ForeignKey(Author, on_delete=models.CASCADE)  # 与 Author 之间的外键关系,一个评论由一个作者创建
    content = models.TextField()  # 评论内容,使用 TextField,允许长文本
    created_at = models.DateTimeField(auto_now_add=True)  # 评论创建时间,自动设置为当前时间

    def __str__(self):
        # 返回格式化的字符串,显示评论作者及其评论的帖子标题
        return f'Comment by {self.author.name} on {self.post.title}'

创建迁移文件并应用

# 创建迁移文件
python manage.py makemigrations

# 应用迁移到数据库
python manage.py migrate

注意:刷新迁移

在已迁移的数据库中重置数据库需要先删除 migrations/0001_initial.py 文件

如果遇到下面返回结果:
> python manage.py makemigrations

Migrations for 'blog':
  blog/migrations/0001_initial.py
    + Create model Author
    + Create model Post
    + Create model Comment
(venv) 

但是再执行迁移时没有进行迁移:
> python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, blog, contenttypes, sessions
Running migrations:
  No migrations to apply.

下面我们需要进行操作:
python manage.py migrate blog zero --fake  # 让 Django 模拟重置状态而不实际删除任何东西
python manage.py migrate blog        # 重新应用 blog 应用的迁移

测试数据的插入

python manage.py shell

在 shell 中执行:

from blog.models import Author, Post, Comment  # blog 修改为你的应用名

# 创建作者
author1 = Author.objects.create(name="用户A", bio="技术爱好者")
author2 = Author.objects.create(name="用户B", bio="博客写手")

# 创建帖子
post1 = Post.objects.create(title="Django 入门", content="这是关于Django的基本知识.", author=author1)
post2 = Post.objects.create(title="Python 进阶", content="深入理解Python的特性.", author=author2)

# 创建评论
Comment.objects.create(post=post1, author=author2, content="非常棒的文章!")
Comment.objects.create(post=post1, author=author1, content="谢谢!")
Comment.objects.create(post=post2, author=author2, content="图文并茂,学习到了很多!")

插入结果:

Django model 项目配置详解

ORM 常见数据操作

1. F 对象

用途:
F 对象允许你在查询中引用模型字段的值,以便进行比较、算术运算等。这在需要根据同一模型的不同字段进行操作时非常有用。

示例:

blog_comment表中找出 author_id > post_id 的评论

Comment.objects.filter(author_id__gt=F('post_id'))


假设你有一个 Post 模型,包含 likes 字段。你想找到所有点赞数大于 10 的帖子,并增加它们的点赞数。

from django.db.models import F

# 增加点赞数
Post.objects.filter(likes__gt=10).update(likes=F('likes') + 1)  # 增加点赞数

使用 F 对象优势:

  • 避免数据的冗余处理:通过使用 F 对象,您可以在数据库层面直接进行操作,减少了在应用程序中有效处理数据的需要,减轻了内存占用。
  • 提高性能:F 对象允许 Django 直接与数据库交互,从而减少了网络流量,比起在内存中取出记录后再进行处理,能够提高性能。

2. Q 对象

用途:
Q 对象用于构建复杂查询,支持逻辑运算符(如 AND、OR)(使用 &(与)和 |(或)来组合条件)。它使得组合多个查询条件变得更加灵活。

示例:
查找所有由“用户A”创作的帖子,或者评论内容中包含“图文并茂”的帖子:

from django.db.models import Q

posts = Post.objects.filter(Q(author__name='用户A') | Q(comments__content__icontains='图文并茂')).distinct()
for post in posts:
    print(f'Title: {post.title}, Author: {post.author.name}')
  • 或:Q() | Q()
  • 且:Q() & Q()
  • 非:~Q()

3. 聚合函数

用途:
Django 提供了一些内置的聚合函数(如 SumCountAvgMax 和 Min),用于在查询中获取统计信息。

示例:
获取每个作者所创作的帖子数量:

from django.db.models import Count

# 使用 annotate() 方法与 Count() 函数获取每个作者的帖子数量
authors_with_post_count = Author.objects.annotate(post_count=Count('posts'))

for author in authors_with_post_count:
    print(f'Author: {author.name}, Post Count: {author.post_count}')

4. 关联查询

用途:
通过外键关系在查询中涉及多个表。Django ORM 允许通过模型属性轻松进行多表查询。

示例:
获取所有评论以及对应的帖子标题和作者:

comments = Comment.objects.select_related('post', 'author')  # 使用 select_related 来优化查询
for comment in comments:
    print(f'Comment by {comment.author.name} on post "{comment.post.title}": {comment.content}')

5. 使用 annotate() 进行复杂查询

用途:
annotate() 方法用于在查询集上添加计算字段,以汇总数据或计算汇总值。

示例:
获取每个帖子的评论数量:

from django.db.models import Count

posts_with_comment_count = Post.objects.annotate(comment_count=Count('comments'))
for post in posts_with_comment_count:
    print(f'Post: {post.title}, Comment Count: {post.comment_count}')

6. 复杂的筛选条件使用 exclude()

用途:
exclude() 方法用于从查询结果中排除满足特定条件的记录。

示例:
获取所有不是由“用户A”创作的帖子:

posts_not_by_author_a = Post.objects.exclude(author__name='用户A')
for post in posts_not_by_author_a:
    print(f'Title: {post.title}, Author: {post.author.name}')

filter()查询函数过滤条件

基本过滤

field_name=value: 精确匹配字段值。

Post.objects.filter(title="Django 入门")

不等于

field_name!=value 或 field_name__ne=value(后者在 Django 中不常用,可以直接使用 exclude())。

Post.objects.filter(title__ne="Django 入门")  # 或者直接使用
Post.objects.exclude(title="Django 入门")

大小比较

  • field_name__gt=value: 大于
  • field_name__gte=value: 大于等于
  • field_name__lt=value: 小于
  • field_name__lte=value: 小于等于
Post.objects.filter(created_at__lt="2025-01-01")

包含和开头匹配

  • field_name__icontains=value: 不区分大小写的包含。
  • field_name__contains=value: 区分大小写的包含。
  • field_name__startswith=value: 以指定值开头的匹配。
  • field_name__endswith=value: 以指定值结尾的匹配。
Post.objects.filter(title__icontains="Django")
Post.objects.filter(title__startswith="D")

范围查找

  • field_name__in=[val1, val2, ...]: 包含于指定的列表中。
Post.objects.filter(id__in=[1, 2, 3])

日期和时间过滤

  • field_name__date=value: 只获取日期部分。
  • field_name__year=value: 只获取年份。
  • field_name__month=value: 只获取月份。
  • field_name__day=value: 只获取天数。
Post.objects.filter(created_at__year=2025)

布尔值过滤

  • field_name=True 或 field_name=False:过滤出布尔字段为 True 或 False 的记录。
Post.objects.filter(is_published=True)

正则表达式

  • field_name__regex=r'正则': 匹配正则表达式。
  • field_name__iregex=r'正则': 不区分大小写的正则匹配。
Post.objects.filter(title__regex=r'^Django')  # 匹配以 Django 开头的标题

链式过滤

  • 多条件过滤可以通过链式调用 filter() 进行:
Post.objects.filter(title='Django 入门').filter(created_at__gt="2025-01-01")

使用 Q 对象

  • 可以使用 &(与)和 |(或)来组合条件。
from django.db.models import Q
Post.objects.filter(Q(author__name="用户A") | Q(title__icontains="Django"))

当你调用 filter() 方法,并传入这样的条件时,Django ORM 会为你的查询自动构建适当的 SQL 查询,包含相应的 JOIN 操作。

SELECT * FROM blog_post
JOIN blog_author ON blog_post.author_id = blog_author.id
WHERE blog_author.name = '用户A' OR blog_post.title LIKE '%Django%'

Django 会执行该 SQL 查询,从数据库中获取符合条件的 Post 对象。

聚合函数详解

使用 Django 的内置聚合函数(如 CountSumAvgMax 和 Min)来获取一些统计信息。

# 示例 1: 计算每个作者的帖子数量
from django.db.models import Count
from blog.models import Author

# 获取每个作者的帖子数量
authors_with_post_count = Author.objects.annotate(post_count=Count('posts'))

for author in authors_with_post_count:
    print(f'作者: {author.name}, 帖子数量: {author.post_count}')

# 示例 2: 计算每个帖子的评论数量
from django.db.models import Count
from blog.models import Post

# 获取每个帖子的评论数量
posts_with_comment_count = Post.objects.annotate(comment_count=Count('comments'))

for post in posts_with_comment_count:
    print(f'帖子: {post.title}, 评论数量: {post.comment_count}')


# 示例 3: 计算整个博客的评论总数
from django.db.models import Count

# 获取所有评论的数量
total_comments = Comment.objects.count()
print(f'总评论数量: {total_comments}')

# 示例 4: 计算每位作者的平均评论数
from django.db.models import Count, Avg
from blog.models import Author

# 计算每个作者的平均评论数
authors_with_avg_comments = Author.objects.annotate(avg_comments=Avg('posts__comments'))

for author in authors_with_avg_comments:
    print(f'作者: {author.name}, 平均评论数: {author.avg_comments}')

# 示例 5: 找出帖子中评论的最大和最小数量
from django.db.models import Count, Max, Min
from blog.models import Post

# 获取每个帖子的评论数量并找出最大和最小值
post_comment_counts = Post.objects.annotate(comment_count=Count('comments'))

max_comment_post = post_comment_counts.aggregate(Max('comment_count'))
min_comment_post = post_comment_counts.aggregate(Min('comment_count'))

print(f'最大评论数: {max_comment_post}')
print(f'最小评论数: {min_comment_post}')

Django ORM 排序的核心概念

核心概念

Django 提供的排序功能可以通过 order_by() 方法实现。这个方法用于对查询集结果进行升序或降序排序。

  • 升序排序: 使用一个字段的名称。
  • 降序排序: 在字段名称前加负号(-)。
  • 多重排序: 可以在 order_by() 中传入多个字段,以此进行分层排序。

1. 按照创建时间降序排序帖子

from blog.models import Post

# 按创建时间降序排序
posts_sorted = Post.objects.order_by('-created_at')

for post in posts_sorted:
    print(f'标题: {post.title}, 创建时间: {post.created_at}')

2. 按照帖子标题升序排序

# 按标题升序排序
posts_sorted_by_title = Post.objects.order_by('title')

for post in posts_sorted_by_title:
    print(f'标题: {post.title}')

3. 按作者名字升序排序并按创建时间降序排序

# 同时按作者名字升序和创建时间降序排序
posts_sorted_by_author_and_time = Post.objects.select_related('author').order_by('author__name', '-created_at')

for post in posts_sorted_by_author_and_time:
    print(f'标题: {post.title}, 作者: {post.author.name}, 创建时间: {post.created_at}')

4. 获取评论数最多的前 5 个帖子

from django.db.models import Count

# 获取评论数最多的前 5 个帖子
top_5_posts = Post.objects.annotate(comment_count=Count('comments')).order_by('-comment_count')[:5]

for post in top_5_posts:
    print(f'标题: {post.title}, 评论数量: {post.comment_count}')

Django ORM 中关联查询的核心概念

Django model 项目配置详解

核心概念
Django ORM 支持模型之间的一对一、一对多和多对多关系。通过使用关联查询,你可以轻松地从相关联的模型中获取数据:

  • select_related(): 用于一对多或多对一关系的前向查询,使用 SQL 的 JOIN 操作,可以在查询时获取相关对象数据,从而减少查询次数。
  • prefetch_related(): 用于多对多或一对多关系的后向查询,执行两次查询(一次查询主对象,一次查询相关对象),适用于可能返回大量结果的查询。

1. 使用 select_related() 查询

获取所有帖子及其作者的信息:

from blog.models import Post

# 获取所有帖子及其作者信息
posts_with_authors = Post.objects.select_related('author').all()

for post in posts_with_authors:
    print(f'帖子: {post.title}, 作者: {post.author.name}')

解释: 在这个例子中,使用 select_related('author') 来避免对 author 模型的多次查询,这样可以提高效率。

2. 使用 prefetch_related() 查询

获取所有作者及其发布的帖子:

from blog.models import Author

# 获取所有作者及其关联的帖子
authors_with_posts = Author.objects.prefetch_related('posts')

for author in authors_with_posts:
    print(f'作者: {author.name}')
    for post in author.posts.all():
        print(f'  帖子: {post.title}')

解释: 这里使用 prefetch_related('posts') 来预加载每个 Author 的 posts,这样每个作者及其帖子都会在单独的查询中加载,从而避免 N+1 查询问题。

3. 使用反向关联查询

找到所有评论以及关联的帖子标题和作者名称:

from blog.models import Comment

# 获取所有评论及其关联的帖子标题和作者名称
comments_with_posts = Comment.objects.select_related('post__author')

for comment in comments_with_posts:
    print(f'评论: {comment.content}, 帖子: {comment.post.title}, 作者: {comment.post.author.name}')

解释: 在这个例子中,通过 select_related('post__author') 可以直接获取评论相关的帖子及其作者的信息。

related_name 在关联查询中的意义

核心概念

  1. 正向获取:
    • 正向查询是从子对象获取父对象的信息。通过外键(ForeignKey)字段,可以直接访问关联的父对象。
  2. 反向获取:
    • 反向查询是从父对象获取所有相关的子对象。这通常通过指定的 related_name 进行。

我们有下模型结构:

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    author = models.ForeignKey(Author, related_name='posts', on_delete=models.CASCADE)
    
    def __str__(self):
        return self.title

class Comment(models.Model):
    post = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE)
    content = models.TextField()

    def __str__(self):
        return self.content

正向获取示例
从 Post 获取 Author

# 获取某个帖子
post = Post.objects.get(title='Django 入门')

# 获取帖子的作者
author = post.author

print(f'帖子标题: {post.title}, 作者: {author.name}')

反向获取示例
从 Author 获取所有 Post

通过 related_name 获取与该作者相关的所有帖子:

# 获取某个作者
author = Author.objects.get(name='用户A')

# 通过 related_name 获取该作者的所有帖子
posts = author.posts.all()  # 使用 'posts' 这个 related_name

for post in posts:
    print(f'帖子标题: {post.title}')

从 Post 获取所有 Comment
通过 related_name 获取与该帖子相关的所有评论:

# 获取某个帖子
post = Post.objects.get(title='Django 入门')

# 通过 related_name 获取该帖子的所有评论
comments = post.comments.all()  # 使用 'comments' 这个 related_name

for comment in comments:
    print(f'评论内容: {comment.content}')
  • 正向查询: 通过外键直接访问关联的父对象(如 post.author)。
  • 反向查询: 通过 related_name 从父对象获取其关联的所有子对象(如 author.posts.all())。

使用默认名称和自定义 related_name

1. 默认名称 (_set)

当在定义 ForeignKey 或 ManyToManyField 时,如果没有设置自定义的 related_name,Django 会自动生成一个默认的名称,以小写的模型名称加上 _set 后缀。这样你就可以通过这个默认名称访问与父对象关联的所有子对象。

示例模型

class Author(models.Model):
    name = models.CharField(max_length=100)

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

在这个例子中,Post 对象与 Author 对象之间存在一对多关系。

使用默认名称获取数据

如果你没有为 author 字段设置 related_name,获取与作者关联的所有帖子时,你需要使用默认名称访问:

# 假设获取某个作者的所有帖子
author = Author.objects.get(name='用户A')

# 使用默认的 _set 名称进行访问
posts = author.post_set.all()  # 这里的 post_set 是 Django 生成的默认名称,格式为:主表模型(实例对象).关联模型类名小写_set.all()

自定义 related_name: 你可以在外键或多对多字段中自定义 related_name 以便使得访问和查询更为直观和易于理解。如果不设置,Django 会自动生成一个默认值,通常是小写的模型名称加上 _set

# 没有设置 related_name,使用默认名称
post_set = author.post_set.all()  # 使用默认的 '_set' 访问

# 假如自定义了 related_name
posts = author.posts.all()  # 使用自定义的相关名称

这里,post_set 是 Django 自动生成的查询集名称,它允许你从 Author 对象获取所有相关的 Post 对象。

自定义 related_name

为了使代码更具可读性,您可以在外键字段中定义自定义的 related_name
修改模型以添加 related_name

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, related_name='posts', on_delete=models.CASCADE)

在这个例子中,我们为 author 字段定义了一个自定义的 related_name 为 posts

现在就可以通过自定义的 related_name 来访问与作者关联的所有帖子,你使用的 posts 是你在 ForeignKey 中设置的 related_name,它使得访问更直观,而不是用 _set 这种自动生成的名称。

# 获取某个作者的所有帖子
author = Author.objects.get(name='用户A')

# 使用自定义的 related_name 进行访问
posts = author.posts.all()  # 使用自定义的名称 'posts'

结合使用模型之间的关系(前向和反向)以及筛选条件来获取特定的数据

1. 基本概念

  • 正向查询:通过子对象获取父对象的信息。
  • 反向查询:通过父对象获取所有相关的子对象。
  • 筛选条件:可以使用 .filter() 方法来过滤结果集,使用双下划线(__)语法访问关联的字段进行条件筛选。

找到所有由特定作者(如“用户A”)撰写的帖子

# 获取作者"用户A"
author_a = Author.objects.get(name='用户A')

# 获取该作者的所有帖子
posts_by_author_a = author_a.posts.all()  # 使用 related_name

for post in posts_by_author_a:
    print(f'帖子标题: {post.title}')

根据作者的名字来筛选所有帖子,可以使用 .filter() 结合双下划线(__):

# 筛选所有由"用户A"创作的帖子
posts_by_author_a = Post.objects.filter(author__name='用户A')

for post in posts_by_author_a:
    print(f'帖子标题: {post.title}')

筛选由"用户A"创建的帖子,并且这些帖子必须有评论

# 筛选由"用户A"创建的帖子,并且这些帖子必须有评论
posts_with_comments = Post.objects.filter(
    author__name='用户A',  # 查找作者为"用户A"的帖子
    comments__content__isnull=False  # 确保这些帖子有评论
).distinct()  # 使用 distinct() 防止重复的帖子记录

# 输出结果
for post in posts_with_comments:
    print(f'标题: {post.title},作者: {post.author.name}')  # 输出帖子的标题和作者
    for comment in post.comments.all():  # 获取该帖子的所有评论
        print(f'  评论内容: {comment.content}')  # 输出每条评论的内容

QuerySet 结果集

查询结果集在 Django ORM 中是通过 QuerySet 表示的,用于管理和操作从数据库中检索的对象。它支持链式查询、切片、计数以及迭代等操作。

QuerySet 的基本概念

  • 定义: QuerySet 是通过 Model.objects.all()Model.objects.filter()Model.objects.exclude()Model.objects.order_by() 方法返回的一系列数据库记录,这些记录可以是一个或多个。
  • 延迟加载:QuerySet 是懒加载的,这意味着它不会立即从数据库中获取数据,直到需要的时候(例如,当你迭代 QuerySet 或在模板中渲染数据时)。
  • 可链式调用: QuerySet 可以通过方法链组合。您可以在一个 QuerySet 上进行多个筛选条件,而每个筛选都会返回新 QuerySet。
  • 假设我们使用先前定义的 AuthorPost 和 Comment 模型进行查询。
# 获取所有帖子
all_posts = Post.objects.all()

# 获取特定条件的帖子
posts_by_author_a = Post.objects.filter(author__name='用户A')

查询结果集的操作:

# 迭代: 你可以遍历 QuerySet,例如:
for post in all_posts:
    print(post.title)

# 切片: 和列表相似,你可以对 QuerySet 进行切片,获取其中的一部分记录:
first_three_posts = Post.objects.all()[:3]  # 获取前3个帖子的 QuerySet

# 计数: 可以使用 .count() 方法获取 QuerySet 中的记录数量:
number_of_posts = Post.objects.all().count()  # 计算所有帖子的数量
Django model 项目配置详解

结果集的特性

  • 可序列化: QuerySet 可以被序列化为 JSON,可以直接用于 API 响应。
  • 从 QuerySet 到列表: 如果您需要结果集的列表,可以通过转换:
posts_list = list(Post.objects.all())  # 变为列表

ORM 分页操作

什么是分页(Pagination)

分页的本质是:
当数据量太大时,一次性返回所有数据太重,我们希望"一页只返回一部分数据"。

比如你有 100 篇博客,一次只显示 10 篇,那么你就需要分页来控制每次返回的数据范围(例如第1页10条,第2页10条……)。

Django 提供的分页工具

from django.core.paginator import Paginator

它可以把一个QuerySet(如 Post.objects.all())分页成一页一页的数据,非常方便地控制每页几条、查看第几页。

示例:分页展示博客文章

from django.core.paginator import Paginator
from blog.models import Post  # 替换为你的 app 名

# 第一步:获取所有博客文章(QuerySet)
all_posts = Post.objects.all().order_by('-created_at')  # 按创建时间倒序排列

# 第二步:创建分页器,每页2条(你可以改为任意数)
paginator = Paginator(all_posts, 2)

# 第三步:获取某一页的数据,比如获取第1页
page_number = 1  # 假设我们想获取第1页
page_obj = paginator.get_page(page_number)

# 第四步:打印这一页的帖子
for post in page_obj:
    print(post.title, post.created_at, post.author.name)

分页对象 page_obj 有什么用?

page_obj 是当前页的对象,它不仅包含当前页的数据,还包含一些分页相关的属性,例如:

page_obj.has_next()        # 是否还有下一页
page_obj.has_previous()    # 是否有上一页
page_obj.number            # 当前页码
page_obj.paginator.num_pages   # 总页数
page_obj.paginator.count       # 总记录数

发布者:LJH,转发请注明出处:https://www.ljh.cool/43387.html

Like (0)
LJH的头像LJH
Previous 2025年7月25日 下午5:53
Next 2025年8月1日 下午5:26

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注