Menangani formulir dengan tampilan berdasarkan-kelas

Pengolahan formulir umumnya mempunyai 3 jalur:

  • Inisial GET (kosong atau formulir diisi dimuka)
  • POST dengan data tidak sah (khususnya memperlihatkan kembali formulir dengan kesalahan)
  • POST dengan data sah (pengolahan data dan khususnya pengalihan)

Menerapkan ini anda sendiri sering menghasilkan banyak perulangan kode boilerplate (lihat Using a form in a view). Untuk menghindai ini, Django menyediakan kumpulan dari tampilan berdasarkan-kelas umum untuk pengolahan formulir.

Formulir dasar

Diberikan formulir kontak:

forms.py
from django import forms


class ContactForm(forms.Form):
    name = forms.CharField()
    message = forms.CharField(widget=forms.Textarea)

    def send_email(self):
        # send email using the self.cleaned_data dictionary
        pass

Tampilan dapat dibangun menggunakan FormView:

views.py
from myapp.forms import ContactForm
from django.views.generic.edit import FormView


class ContactFormView(FormView):
    template_name = "contact.html"
    form_class = ContactForm
    success_url = "/thanks/"

    def form_valid(self, form):
        # This method is called when valid form data has been POSTed.
        # It should return an HttpResponse.
        form.send_email()
        return super().form_valid(form)

Catatan:

Formulir model

Tampilan umum bersinar ketika bekerja dengan model. Tampilan umum ini akan secara otomatis membuat sebuah ModelForm, selama mereka dapat bekerya kelas model mana digunakan:

  • Jika atribut model diberikan, kelas model itu akan digunakan.
  • Jika get_object() mengembalikan sebuah obyek, kelas dari obyek akan digunakan.
  • Jika sebuah queryset diberikan, model untuk queryset itu akan digunakan.

Tampilan formulir model menyediakan sebuah penerapan form_valid() yang menyimpan model secara otomatis. Anda dapat menimpa ini jika anda mempunyai persyaratan khusus lainnya; lihat dibawah untuk contoh.

Anda tidak perlu menyediakan sebuah success_url untuk CreateView atau UpdateView - mereka akan menggunakan get_absolute_url() pada obyek model jika tersedia.

Jika anda ingin menggunakan penyesuaian ModelForm (misalnya untuk menambahkan validasi ekstra), atur form_class pada tampilan anda.

Catatan

Ketika menentukan sebuah penyesuaian kelas formulir, anda harus masih menentukan model, meskipun form_class mungkin berupa sebuah ModelForm.

Pertama kami butuh menambahkan get_absolute_url() ke kelas Author kami:

models.py
from django.db import models
from django.urls import reverse


class Author(models.Model):
    name = models.CharField(max_length=200)

    def get_absolute_url(self):
        return reverse("author-detail", kwargs={"pk": self.pk})

Kemudian kami dapat menggunakan CreateView dan teman-teman untuk melakukan pekerjaan sebenarnya. Perhatikan bagaimana kami hanya mengkonfigurasi tampilan berdasarkan-kelas umum disini; kami tidak harus menulis logika apapun kami sendiri:

views.py
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from myapp.models import Author


class AuthorCreateView(CreateView):
    model = Author
    fields = ["name"]


class AuthorUpdateView(UpdateView):
    model = Author
    fields = ["name"]


class AuthorDeleteView(DeleteView):
    model = Author
    success_url = reverse_lazy("author-list")

Catatan

Kami harus menggunakan reverse_lazy() daripada reverse(), ketika url tidak dimuat ketika berkas diimpor.

Atribut fields bekerja cara sama seperti atribut fields pada kelas Meta sebelah dalam pada ModelForm. Meskipun anda menentukan kelas formulir di cara lain, atribut dibutuhkan dan tampilan akan munculkan sebuah pengecualian ImproperlyConfigured jika itu tidak.

Jika anda menentukan kedua atribut fields dan form_class, sebuah pengecualian ImproperlyConfigured akan dimunculkan.

Akhirnya, kami mengaitkan tampilan baru ini kedalam URLconf:

urls.py
from django.urls import path
from myapp.views import AuthorCreateView, AuthorDeleteView, AuthorUpdateView

urlpatterns = [
    # ...
    path("author/add/", AuthorCreateView.as_view(), name="author-add"),
    path("author/<int:pk>/", AuthorUpdateView.as_view(), name="author-update"),
    path("author/<int:pk>/delete/", AuthorDeleteView.as_view(), name="author-delete"),
]

Catatan

Tampilan ini mewarisi class:~django.views.generic.detail.SingleObjectTemplateResponseMixin yang menggunakan template_name_suffix untuk membangun template_name berdasarkan pada model.

Di contoh ini:

Jika anda berharap untuk mempunyai cetakan terpisah untuk CreateView dan UpdateView, anda dapat menyetel antara template_name atau template_name_suffix pada kelas tampilan anda.

Models dan request.user

Untuk melacak pengguna yang membuat sebuah obyek menggunakan sebuah CreateView, anda dapat menggunakan sebuah penyesuaian ModelForm untuk melakukan ini. Pertama, tambah hubungan foreign key pada model:

models.py
from django.contrib.auth.models import User
from django.db import models


class Author(models.Model):
    name = models.CharField(max_length=200)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)

    # ...

Di tampilan, pastikan bahwa anda tidak menyertakan created_by di daftar dari bidang untuk menyunting, dan menimpa form_valid() untuk menambah pengguna:

views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic.edit import CreateView
from myapp.models import Author


class AuthorCreateView(LoginRequiredMixin, CreateView):
    model = Author
    fields = ["name"]

    def form_valid(self, form):
        form.instance.created_by = self.request.user
        return super().form_valid(form)

LoginRequiredMixin mencegah pengguna yang tidak masuk untuk mengakses formulir. Jika anda mengabaikannya, Anda harus menangani pengguna yang tidak sah di form_valid().

Contoh negosiasi isi

Here is an example showing how you might go about implementing a form that works with an API-based workflow as well as 'normal' form POSTs:

from django.http import JsonResponse
from django.views.generic.edit import CreateView
from myapp.models import Author


class JsonableResponseMixin:
    """
    Mixin to add JSON support to a form.
    Must be used with an object-based FormView (e.g. CreateView)
    """

    def form_invalid(self, form):
        response = super().form_invalid(form)
        if self.request.accepts("text/html"):
            return response
        else:
            return JsonResponse(form.errors, status=400)

    def form_valid(self, form):
        # We make sure to call the parent's form_valid() method because
        # it might do some processing (in the case of CreateView, it will
        # call form.save() for example).
        response = super().form_valid(form)
        if self.request.accepts("text/html"):
            return response
        else:
            data = {
                "pk": self.object.pk,
            }
            return JsonResponse(data)


class AuthorCreateView(JsonableResponseMixin, CreateView):
    model = Author
    fields = ["name"]
Back to Top