当前位置:首页 > 技术 > 正文内容

Django ORM 多表关联操作实战

访客 技术 2026年6月4日 1

项目路由配置

在 Django 项目中,URL 路由是连接视图函数与 HTTP 请求的桥梁。下面是一个完整的多表操作路由配置示例:

from django.urls import path, re_path
from myapp import views

urlpatterns = [
    path("create/", views.publication_create),
    path("init/", views.data_initialization),
    path("list/", views.publication_list),
    re_path("modify/(\d+)/$", views.publication_update),
    re_path("remove/(\d+)/$", views.publication_delete)
]

视图层实现

视图函数负责处理业务逻辑,包括数据的增删改查操作。以下是各个功能的详细实现:

创建出版物

def publication_create(request):
    """处理新增出版物的业务逻辑"""
    if request.method == "POST":
        # 从POST请求中提取表单数据
        title = request.POST.get("title")
        price = request.POST.get("price")
        publish_date = request.POST.get("publish_date")
        publisher_id = request.POST.get("publisher_id")
        # 作者字段支持多选,使用getlist获取列表
        writers = request.POST.getlist("writer_ids")

        # 创建出版物记录
        book = models.Publication.objects.create(
            name=title,
            price=price,
            date=publish_date,
            publisher_id=publisher_id
        )
        # 通过多对多关系添加作者
        book.writers.add(*writers)
        
        return redirect("/myapp/create/")
    
    # GET请求时加载表单页面
    publishers = models.Publisher.objects.all()
    authors = models.Author.objects.all()
    return render(request, "publication_form.html", {
        "publishers": publishers, 
        "authors": authors
    })

数据初始化

def data_initialization(request):
    """批量初始化基础数据"""
    
    # 创建出版社记录
    publisher_names = [
        "上海出版社", "广州出版社", "深圳出版社", 
        "杭州出版社", "武汉出版社", "成都出版社", "西安出版社"
    ]
    for name in publisher_names:
        models.Publisher.objects.create(title=name)
    
    # 创建作者详情记录
    cities = ["上海", "广州", "深圳", "杭州", "武汉", "成都", "西安"]
    addresses = ["浦东新区", "天河区", "南山区", "西湖区", "江汉区", "锦江区", "雁塔区"]
    
    for city, addr in zip(cities, addresses):
        models.AuthorDetail.objects.create(location=addr, city=city)
    
    # 创建作者记录并关联详情表
    author_names = ["张三", "李四", "王五", "赵六", "钱七", "孙八", "周九"]
    for idx, city in enumerate(cities):
        detail = models.AuthorDetail.objects.get(city=city)
        models.Author.objects.create(
            username=author_names[idx],
            age=20 + idx,
            detail=detail
        )
    
    return HttpResponse("数据初始化完成")

出版物列表

def publication_list(request):
    """查询所有出版物信息"""
    books = models.Publication.objects.all()
    return render(request, "publication_list.html", {"books": books})

更新出版物

def publication_update(request, book_id):
    """编辑已存在的出版物"""
    book = models.Publication.objects.get(pk=book_id)
    publishers = models.Publisher.objects.all()
    authors = models.Author.objects.all()
    
    if request.method == "POST":
        title = request.POST.get("title")
        price = request.POST.get("price")
        publish_date = request.POST.get("publish_date")
        publisher_id = request.POST.get("publisher_id")
        writers = request.POST.getlist("writer_ids")
        
        # 更新出版物基本信息
        models.Publication.objects.filter(pk=book_id).update(
            name=title,
            price=price,
            date=publish_date,
            publisher_id=publisher_id
        )
        # 使用set方法更新多对多关系(先清除旧关系再添加新关系)
        book.writers.set(writers)
        
        return redirect("/myapp/list/")
    
    return render(request, "publication_edit.html", locals())

删除出版物

def publication_delete(request, book_id):
    """删除指定出版物"""
    models.Publication.objects.filter(pk=book_id).delete()
    return redirect("/myapp/list/")

数据模型定义

模型层定义了数据库表结构及其相互关系。Django ORM 提供了强大的字段类型来描述表之间的关系:

from django.db import models

class Publication(models.Model):
    """出版物模型(核心表)"""
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=64)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    date = models.DateField()
    # 外键关联出版社(一对多)
    publisher = models.ForeignKey(
        to="Publisher", 
        to_field="pid", 
        on_delete=models.CASCADE
    )
    # 多对多关联作者
    writers = models.ManyToManyField(to="Author")

    def __str__(self):
        return self.name


class Publisher(models.Model):
    """出版社模型"""
    pid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)


class Author(models.Model):
    """作者模型"""
    aid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=64)
    age = models.IntegerField()
    # 一对一关联作者详情
    detail = models.OneToOneField(
        to="AuthorDetail", 
        to_field="aid", 
        on_delete=models.CASCADE
    )


class AuthorDetail(models.Model):
    """作者详情模型"""
    aid = models.AutoField(primary_key=True)
    location = models.CharField(max_length=64)
    city = models.CharField(max_length=64)

模板文件实现

新增出版物表单

<html>
<head>
    <meta charset="UTF-8">
    <title>添加出版物</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
</head>
<body>
<div class="container">
    <h2 class="text-center">新增出版物</h2>
    <div class="row">
        <div class="col-md-6 col-md-offset-3">
            <a href="/myapp/list" class="btn btn-info">返回列表</a>
            <form method="post" class="form-horizontal">
                {% csrf_token %}
                <div class="form-group">
                    <label>出版物名称</label>
                    <input type="text" name="title" class="form-control">
                </div>
                <div class="form-group">
                    <label>价格</label>
                    <input type="number" step="0.01" name="price" class="form-control">
                </div>
                <div class="form-group">
                    <label>出版日期</label>
                    <input type="date" name="publish_date" class="form-control">
                </div>
                <div class="form-group">
                    <label>出版社</label>
                    <select name="publisher_id" class="form-control">
                        {% for pub in publishers %}
                        <option value="{{ pub.pk }}">{{ pub.title }}</option>
                        {% endfor %}
                    </select>
                </div>
                <div class="form-group">
                    <label>作者(可多选)</label>
                    <select name="writer_ids" multiple class="form-control">
                        {% for author in authors %}
                        <option value="{{ author.pk }}">{{ author.username }}</option>
                        {% endfor %}
                    </select>
                </div>
                <button type="submit" class="btn btn-primary">提交</button>
            </form>
        </div>
    </div>
</div>
</body>
</html>

出版物列表页

<html>
<head>
    <meta charset="UTF-8">
    <title>出版物列表</title>
    <link rel="stylesheet" href="/static/css/bootstrap.css">
</head>
<body>
<div class="container">
    <h2 class="text-center">出版物查询</h2>
    <a href="/myapp/create" class="btn btn-success">新增出版物</a>
    <table class="table table-hover table-bordered">
        <thead>
            <tr class="info">
                <th class="text-center">编号</th>
                <th class="text-center">名称</th>
                <th class="text-center">出版日期</th>
                <th class="text-center">价格</th>
                <th class="text-center">出版社</th>
                <th class="text-center">作者</th>
                <th class="text-center">操作</th>
            </tr>
        </thead>
        <tbody>
            {% for item in books %}
            <tr>
                <td>{{ forloop.counter }}</td>
                <td>{{ item.name }}</td>
                <td>{{ item.date|date:"Y-m-d" }}</td>
                <td>{{ item.price }}</td>
                <td>{{ item.publisher.title }}</td>
                <td>
                    {# 遍历多对多关系获取所有作者 #}
                    {% for writer in item.writers.all %}
                        {% if forloop.last %}
                        <span>{{ writer.username }}</span>
                        {% else %}
                        <span>{{ writer.username }}, </span>
                        {% endif %}
                    {% endfor %}
                </td>
                <td>
                    <a href="/myapp/modify/{{ item.pk }}/" class="btn btn-warning btn-sm">编辑</a>
                    <a href="/myapp/remove/{{ item.pk }}/" class="btn btn-danger btn-sm">删除</a>
                </td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
</div>
</body>
</html>

关键知识点总结

  • ForeignKey(一对多):在"多"的一方定义外键,如 Publication.publisher
  • ManyToManyField(多对多):在任意一方定义,Django 会自动创建中间表
  • OneToOneField(一对一):常用于将详细信息分离出主表
  • 正向查询:通过对象.关联字段.属性获取,如 book.publisher.title
  • 反向查询:通过外键所在模型查询,如 publisher.publication_set.all()
  • 多对多操作:add()添加、remove()删除、set()重置关系
标签: djangoormPython

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。