使用【Django】的TemplateView来创建Form页
当我试图使用Templateview创建Form页面时,我发现遇到了比想象中更多的困难,所以我总结了使用Templateview创建List页面的方法。
总结
使用TemplateView来创建From页面的方法。
想在这篇文章中传达的内容是
- Templateviewを使ったFormページの作り方
結論就是
继承ModelFormMixin
from django.views.generic.edit import ModelFormMixin
from django.views.generic import TemplateView, ListView
from django.urls import reverse_lazy
from .forms import TestDataModelForm
from .models import TestData
class IndexView(TemplateView, ModelFormMixin):
template_name: str = "app1/index.html"
form_class = TestDataModelForm
success_url = reverse_lazy('app1:data_list')
model = TestData
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = None
self.object_list = self.get_queryset()
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
源代码
我已经在GitHub上公开了源代码
https://github.com/ANKM0/django_sample_make_form_with_templateview.git
环境
- django4.1.3
文件夾架構
文件夹的结构是像这样的
项目名称是config,应用程序名称是app1。
DJANGO_SAMPLE(root)
│ db.sqlite3
│ gen_secrets.py
│ manage.py
│ secrets.json
│
├─app1
│ │ admin.py
│ │ apps.py
│ │ forms.py
│ │ models.py
│ │ tests.py
│ │ urls.py
│ │ views.py
│ │ __init__.py
│ │
│ ├─migrations
│ │ │ 0001_initial.py
│ │ │ __init__.py
│ │ │
│ │ └─__pycache__
│ │ 0001_initial.cpython-310.pyc
│ │ __init__.cpython-310.pyc
│ │
│ └─__pycache__
│ 略
│
├─config
│ │ asgi.py
│ │ settings.py
│ │ urls.py
│ │ wsgi.py
│ │ __init__.py
│ │
│ └─__pycache__
│ 略
│
└─templates
└─app1
base.html
index.html
准备好
创建模型和表单
我已经创建了用于创建Form的models和Forms。
from django.db import models
class TestData(models.Model):
number = models.PositiveIntegerField()
name = models.CharField(max_length=200, blank=False, null=False)
price = models.PositiveIntegerField()
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "テストデータ"
from django import forms
from .models import TestData
class TestDataModelForm(forms.ModelForm):
class Meta:
model = TestData
fields = ("number", "name", "price")
需要在Form中进行的处理
在Form中需要但在TemplateView中没有的功能
-
- form画面
- バリデーションの結果から遷移画面を判断する処理
以下是两个(可能还有其他的)选项。
通过添加这些处理,可以使表单在TemplateView中可用。
我将会解释如何在TemplateView中添加处理Form所需的步骤。
方案一:通过自己制作画面和逻辑
以自己制作界面和逻辑的方式来解决,如果没有的话就自行创作。
{% extends "app1/base.html" %}
{% block title %}index{% endblock %}
{% block content %}
<div class="container">
<h1>index page</h1>
<br>
<br>
{{ error_list }}
<form method="POST">
{% csrf_token %}
<span>
<p>Number:
<input type="number" name="number" min="0">
</p>
</span>
<span>
<p>Name:
<input type="text" name="name" maxlength="200">
</p>
</span>
<span>
<p>Price:
<input type="number" name="price" min="0">
</p>
</span>
<button type="submit" name="submit">送信</button>
</form>
</div>
{% endblock %}
使用input标签创建表单,并使用min和maxlength属性限制输入的值。
我們可以按照這樣的邏輯進行創建。
from django.views.generic import TemplateView, ListView
from django.http import HttpResponse
from django.shortcuts import render
from .models import TestData
from .forms import TestDataModelForm
class IndexView(TemplateView):
template_name: str = "app1/index.html"
form_class = TestDataModelForm
def post(self, request, *args, **kwargs) -> HttpResponse:
number = request.POST.get("number")
name = request.POST.get("name")
price = request.POST.get("price")
default_data = {
"number": number,
"name": name,
"price": price,
}
form = self.form_class(default_data)
if form.is_valid():
form.save()
else:
print(f"error:{form.errors}")
context = {
"error_list": form.errors,
}
return render(request, self.template_name, context)
通过请求对象的POST方法,可以获取被输入标签命名为”[input标签的name属性值]”的表单的值。
使用 self.form_class(default_data) 进行验证(值的检验)操作
使用 form.save() 将数据保存。
用django的表单的第二种方法
如果要自制图像,就需要根据input标签的数量编写相应的处理代码。
class IndexView(TemplateView):
template_name: str = "app1/index.html"
form_class = TestDataForm
def post(self, request, *args, **kwargs) -> HttpResponse:
number = request.POST.get("number")
name = request.POST.get("name")
price = request.POST.get("price")
# inputタグの数だけ処理を書く
# ︙
default_data = {
"number": number,
"name": name,
"price": price,
# inputタグの数だけ処理を書く
# ︙
}
因此,如果按照这种写法,代码会变得很乱(冗长)。
使用Django提供的表单。
from django.views.generic import TemplateView, ListView
from django.http import HttpResponse
from django.shortcuts import render, redirect
from .forms import TestDataModelForm
from .models import TestData
class IndexView(TemplateView):
template_name: str = "app1/index.html"
form_class = TestDataModelForm
def get(self, request, *args, **kwargs) -> HttpResponse:
form = self.form_class()
context = {
"form": form
}
return render(request, self.template_name, context)
def post(self, request, *args, **kwargs) -> HttpResponse:
form = self.form_class(request.POST)
if form.is_valid():
form.save()
return redirect("app1:index")
else:
context = {"form": form}
return render(request, self.template_name, context)
返回render(请求,self.template_name,上下文)将表单传递给模板。
需要注意的是,上下文必须是字典类型。
以下是模板参考样式:
{% extends "app1/base.html" %}
{% block title %}index{% endblock %}
{% block content %}
<div class="container">
<h1>index page</h1>
<br>
<br>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">送信</button>
</form>
</div>
{% endblock %}
用第三种方法,使用get_context_data函数。
在方法2中,对于get和post分别编写了处理代码。
由于get仅用于数据传递,为了避免副作用,最好不要使用。
因此,将get方法替换为get_context_data方法。
from django.views.generic import TemplateView, ListView
from django.http import HttpResponse
from django.shortcuts import redirect
from .forms import TestDataModelForm
from .models import TestData
class IndexView(TemplateView):
template_name: str = "app1/index.html"
form_class = TestDataModelForm
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["form"] = self.form_class()
return ctx
def post(self, request, *args, **kwargs) -> HttpResponse:
form = self.form_class(request.POST)
if form.is_valid():
form.save()
return redirect("app1:index")
ctx = super().get_context_data(**kwargs) 继承了get_context_data函数并将其内容赋值给ctx。
get_context_data函数的内容是以字典形式存在的,因此通过 ctx[“form”] = self.form_class() 代码将数据传递给它。
使用ModelFormMixin的第四种方法
实际上,在ModelFormMixin中提供了必要的属性和方法来显示表单。
过去我们通过重写(form)方法来添加功能。
为了编写更加优雅,我们使用Mixins来继承。
from django.views.generic.edit import ModelFormMixin
from django.views.generic import TemplateView, ListView
from django.urls import reverse_lazy
from .forms import TestDataModelForm
from .models import TestData
class IndexView(TemplateView, ModelFormMixin):
# https://docs.djangoproject.com/en/4.1/ref/class-based-views/mixins-single-object/
template_name: str = "app1/index.html"
form_class = TestDataModelForm
success_url = reverse_lazy('app1:data_list')
model = TestData
def get(self, request, *args, **kwargs):
self.object = None
return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
self.object = None
self.object_list = self.get_queryset() # modelからformを作成するために必要
form = self.get_form()
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
当使用FormView时,与之前使用方式相同。
template_name
使用するtemplateのパス
form_class
使用するForm
success_url
遷移先のurl
model
Formに使うmodel
我們分別指定了每個選項。
此外,在get/post方法中需要指定self.object = None。
由于ModelFormMixin的父类SingleObjectMixin具有self.object,所以即使在不使用self.object的View(TemplateView)中,也需要定义它。
当不使用时,需要将self.object设置为None。
总结
要在 TemplateView 中创建表单,可以使用 get_context_data 或 Mixin。
参考资料
-
- Djangoの 汎用クラスビューをまとめて、実装について言及する
-
- Djangoのクラスベースビューを完全に理解する
-
- みかん箱でプログラミング FormMixinクラス
-
- mixinの使い方
-
- django公式ドキュメント SingleObjectMixin self.object = Noneにする必要がある
- メモ〜djangoでのself.objectの注意点