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
TemplateResponseMixinjaditemplate_namedapat digunakan disini.Penerapan awalan untuk
form_valid()cukup mengalihkan kesuccess_url.
Formulir model¶
Generic views really shine when working with models. These generic
views will automatically create a ModelForm, so long as
they can work out which model class to use:
Jika atribut
modeldiberikan, kelas model itu akan digunakan.If
get_object()returns an object, the class of that object will be used.Jika sebuah
querysetdiberikan, model untuk queryset itu akan digunakan.
Model form views provide a
form_valid() implementation
that saves the model automatically. You can override this if you have any
special requirements; see below for examples.
You don't even need to provide a success_url for
CreateView or
UpdateView - they will use
get_absolute_url() on the model object if
available.
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.
First we need to add get_absolute_url() to our
Author class:
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:
CreateViewdanUpdateViewmenggunakanmyapp/author_form.htmlDeleteViewmenggunakanmyapp/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)
# ...
In the view, ensure that you don't include created_by in the list of fields
to edit, and override
form_valid() to add the user:
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 prevents users who
aren't logged in from accessing the form. If you omit that, you'll need to
handle unauthorized users in 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 = self.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 = self.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.