使用【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の注意点
广告
将在 10 秒后关闭
bannerAds