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:
FormView mewarisi
TemplateResponseMixin
jaditemplate_name
dapat digunakan disini.Penerapan awalan untuk
form_valid()
cukup mengalihkan kesuccess_url
.
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:
CreateView
danUpdateView
menggunakanmyapp/author_form.html
DeleteView
menggunakanmyapp/author_confirm_delete.html
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"]
The above example assumes that if the client supports text/html
, that they
would prefer it. However, this may not always be true. When requesting a
.css
file, many browsers will send the header
Accept: text/css,*/*;q=0.1
, indicating that they would prefer CSS, but
anything else is fine. This means request.accepts("text/html")
will be
True
.
To determine the correct format, taking into consideration the client's
preference, use django.http.HttpRequest.get_preferred_type()
:
class JsonableResponseMixin:
"""
Mixin to add JSON support to a form.
Must be used with an object-based FormView (e.g. CreateView).
"""
accepted_media_types = ["text/html", "application/json"]
def dispatch(self, request, *args, **kwargs):
if request.get_preferred_type(self.accepted_media_types) is None:
# No format in common.
return HttpResponse(
status_code=406, headers={"Accept": ",".join(self.accepted_media_types)}
)
return super().dispatch(request, *args, **kwargs)
def form_invalid(self, form):
response = super().form_invalid(form)
accepted_type = request.get_preferred_type(self.accepted_media_types)
if accepted_type == "text/html":
return response
elif accepted_type == "application/json":
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)
accepted_type = request.get_preferred_type(self.accepted_media_types)
if accepted_type == "text/html":
return response
elif accepted_type == "application/json":
data = {
"pk": self.object.pk,
}
return JsonResponse(data)
The HttpRequest.get_preferred_type()
method was added.