Django ORM 多表关联操作实战
项目路由配置
在 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()重置关系