0%

django基于orm实践

说明

  • 这篇文章介绍了django中简单的类视图单表操作,本次开始进行基于orm的一对多,一对一,多对多的实践
  • 关于orm的具体介绍,可以查看这篇文章的笔记

一对多

model

1
2
3
4
5
6
7
8
9
10
11
12
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
# 关联出版社一对多,意思就是一个出版社可以有印刷多本书
publish = models.ForeignKey("Publish", on_delete=models.CASCADE, default=None)

# 出版商
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField()
  • 生效
1
2
3
python manage.py makemigrations TestModel# 让 Django 知道我们在我们的模型有一些变更
python manage.py migrate TestModel # 创建表

image-20250921164906417

view&html

  • 这里的配置和book的代码一模一样
  • 运行代码新增出版商的数据

image-20250921151545436

改造book

  • forms/book,加入了publish
1
2
3
4
5
6
7
8
9
10
11
class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'price', 'publish']

# 你可以在这里添加额外的验证逻辑
def clean_title(self):
title = self.cleaned_data['title']
if not title:
raise forms.ValidationError("Title is required.")
return title
  • 书籍的index.html 引用出版商
1
{{ book.publish.name }}-
  • 后续的增删改查,不用改动任何代码

多对多&一对一

model

一对一

  • 新建作者和作者详情表,也就是一个作者一个作者详情
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
# 一对一,意思为一个作者对应一个作者详情
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE)

class AuthorDetail(models.Model):
gender_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()
  • 创建表单类来处理 froms.py
1
2
3
4
5
6
7
8
9
10
11
12
13
from django import forms
from ..models import Author, AuthorDetail

class AuthorForm(forms.ModelForm):
class Meta:
model = Author
fields = ['name', 'age']

class AuthorDetailForm(forms.ModelForm):
class Meta:
model = AuthorDetail
fields = ['gender', 'tel', 'addr', 'birthday']

  • 新增个人信息views.py,注意get_context_data的用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# Author Views
class AuthorList(LoginRequiredMixin, ListView):
model = Author
template_name = 'author_list.html'
context_object_name = 'authors'

class AuthorCreate(LoginRequiredMixin, CreateView):
model = Author
form_class = AuthorForm
template_name = 'author_form.html'
success_url = reverse_lazy('author_list')

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.method == 'POST':
context['detail_form'] = AuthorDetailForm(self.request.POST)
else:
context['detail_form'] = AuthorDetailForm()
return context

def form_valid(self, form):
context = self.get_context_data()
detail_form = context['detail_form']
if detail_form.is_valid():
author_detail = detail_form.save(commit=False)
author_detail.save() # 先保存个人明细页面
author = form.save(commit=False)
author.au_detail = author_detail
author.save() # 再保存个人信息
return super().form_valid(form)
else:
return self.render_to_response(context)
  • 个人列表author_list.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Author List</title>
</head>
<body>
<h1>Author List</h1>
<a href="{% url 'author_new' %}">Add New Author</a>
<ul>
{% for author in authors %}
<li>
{{ author.id }}--
<a href="{% url 'author_detail' author.id %}">{{ author.name }}</a>--
<a href="{% url 'author_edit' author.id %}">Edit</a>
<form action="{% url 'author_delete' author.id %}" method="post" style="display:inline;">
{% csrf_token %}
<button type="submit" onclick="return confirm('确定要删除这个作者吗?')">Delete</button>
</form>
</li>
{% endfor %}
</ul>
</body>
</html>

  • 新增个人信息author_form.html,注意这里和编辑共用的同一个html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% if object %}Edit{% else %}Add{% endif %} Author</title>
</head>
<body>
<h1>{% if object %}Edit{% else %}Add{% endif %} Author</h1>
<form method="post">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.as_p }}
{{ detail_form.non_field_errors }}
{{ detail_form.as_p }}
<button type="submit">{% if object %}Save changes{% else %}Save{% endif %}</button>
</form>
<a href="{% url 'author_list' %}">Back to list</a>
</body>
</html>
  • veiw加入明细、修改、删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class AuthorDetail(LoginRequiredMixin, DetailView):
model = Author
template_name = 'author_detail.html'
context_object_name = 'author'

class AuthorUpdate(LoginRequiredMixin, UpdateView):
model = Author
form_class = AuthorForm
template_name = 'author_form.html'
success_url = reverse_lazy('author_list')

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.method == 'POST':
context['detail_form'] = AuthorDetailForm(self.request.POST, instance=self.object.au_detail)
else:
context['detail_form'] = AuthorDetailForm(instance=self.object.au_detail)
return context

def form_valid(self, form):
context = self.get_context_data()
detail_form = context['detail_form']
if detail_form.is_valid():
author_detail = detail_form.save(commit=False)
author_detail.save() # Save AuthorDetail first to update it
author = form.save(commit=False)
author.au_detail = author_detail
author.save()
return super().form_valid(form)
else:
return self.render_to_response(context)

class AuthorDelete(LoginRequiredMixin, DeleteView):
model = Author
template_name = 'author_confirm_delete.html'
success_url = reverse_lazy('author_list')
  • 最终的url加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from django.urls import path

from . import views
from .views import BookAdd, BookList, BookDetail, BookEdit, BookDelete,PublishList,PublishAdd

urlpatterns = [
path('book/', BookList.as_view(), name='book_list'),
# path("event_add/", views.event_add, name="event_add"),
path('book/add/', BookAdd.as_view(), name='book_add'),
path('book/edit/<int:pk>/', BookEdit.as_view(), name='book_edit'),
path('book/<int:pk>/', BookDetail.as_view(), name='book_detail'),
path('book/delete/<int:pk>/', BookDelete.as_view(), name='book_delete'), # 新增删除路径
path("publish_add/",PublishAdd.as_view() , name="publish_add"),
path("publish/", PublishList.as_view(), name="publish_list"),


# Author URLs
path('authors/', views.AuthorList.as_view(), name='author_list'),
path('author/<int:pk>/', views.AuthorDetail.as_view(), name='author_detail'),
path('author/new/', views.AuthorCreate.as_view(), name='author_new'),
path('author/<int:pk>/edit/', views.AuthorUpdate.as_view(), name='author_edit'),
path('author/<int:pk>/delete/', views.AuthorDelete.as_view(), name='author_delete'),


]
  • author_detail.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Author Detail</title>
</head>
<body>
<h1>{{ author.name }} Details</h1>
<p>Name: {{ author.name }}</p>
<p>Age: {{ author.age }}</p>
<p>Gender: {{ author.au_detail.get_gender_display }}</p>
<p>Tel: {{ author.au_detail.tel }}</p>
<p>Address: {{ author.au_detail.addr }}</p>
<p>Birthday: {{ author.au_detail.birthday }}</p>
<a href="{% url 'author_list' %}">Back to list</a>
</body>
</html>

多对多

  • book表再次优化
1
2
3
4
5
6
7
8
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
# 关联出版社一对多,意思就是一个出版社可以有印刷多本书
publish = models.ForeignKey("Publish", on_delete=models.CASCADE)
# 多对多,意思就是多个作者可以编写多本书
authors = models.ManyToManyField("Author")
  • 生成表结构
1
2
python manage.py makemigrations TestModel# 让 Django 知道我们在我们的模型有一些变更
python manage.py migrate TestModel

image-20250921190310674

  • 注意这里多对多自动生成了一个中间表

  • 然后在from/book.py fields加入authors就可以了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from django import forms

from TestModel.models import Book


class BookForm(forms.ModelForm):
class Meta:
model = Book
fields = ['title', 'price', 'pub_date', 'authors']
# 你可以在这里添加额外的验证逻辑
def clean_title(self):
title = self.cleaned_data['title']
if not title:
raise forms.ValidationError("Title is required.")
return title
  • 在书籍详情页html,加了个循环读取作者信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>详情页面</title>
</head>
<body>
<div>
<p>id:{{ book.id }}</p>
<p>标题:{{ book.title }}</p>
<p>价格:{{ book.price }}</p>
<p>日期:{{ book.pub_date }}</p>
<p>Authors:
{% for author in book.authors.all %}
<a href="{% url 'author_detail' author.id %}">{{ author.name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
</div>
<a href="{% url 'book_list' %}">Back to List</a>

</body>
</html>