Django. Набір форм.
Основи Python и Django. -> Набір форм.
Набір форм. Formset.
Набір форм - це абстрактний шар для роботи з безліччю форм на одній сторінці. Його можна порівняти з таблицею даних.
Ви можете дозволити користувачеві створювати декілька об’єктів моделі за один раз.
Припустимо, у нас
from django.db import models
from django.contrib.auth.models import User
class Cv(models.Model):
name = models.CharField(max_length=255)
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True)
class WorkExperience(models.Model):
cv = models.ForeignKey(Cv, on_delete=models.CASCADE, blank=True)
position = models.CharField(max_length=255, blank=True)
company = models.CharField(max_length=255, blank=True)
class Certification(models.Model):
cv = models.ForeignKey(Cv, on_delete=models.CASCADE, blank=True)
name = models.CharField(max_length=255, blank=True)
provider = models.CharField(max_length=255, blank=True)
Ми пов’язали одне резюме користувача з безліччю моделей WorkExperience і Certification.
Адмін інтерфейс.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
from .models import *
class WorkExperienceInine(admin.TabularInline):
model = WorkExperience
class CertificationAdmin(admin.TabularInline):
model = Certification
class CvAdmin(admin.ModelAdmin):
list_display = ['name']
inlines = [WorkExperienceInine, CertificationAdmin]
admin.site.register(Cv, CvAdmin)
Як видно в адмінці ми вже маємо можливість за допомогою набору форм створити відразу кілька об’єктів, прив’язаних до об’єкта резюме.
Спробуймо реалізувати подібне на фронтенді.
Створимо класи форм у в forms.py.
from django.forms import ModelForm
from .models import *
class CvForm(ModelForm):
class Meta:
model = Cv
fields = ['name', 'user']
class WorkExperienceForm(ModelForm):
class Meta:
model = WorkExperience#
fields = ['cv', 'position', 'company']
class Certification(ModelForm):
class Meta:
model = Certification
fields = ['cv', 'name', 'provider']
Передаємо одну форму в вьюшке.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render
from .forms import *
def create_cv(request):
form = CvForm()
return render(request,'create_cv.html',{'form': form})
Роутинг.
from django.conf.urls import url
from django.contrib import admin
from main.views import create_cv
urlpatterns = [
url(r'', create_cv),
url(r'^admin/', admin.site.urls),
]
Шаблон.
<html>
<head></head>
<body>
<form action="" method="POST">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
</body>
</html>
Тепер збережемо форму та переправимо користувача на іншу вьюшку.
...
def create_cv(request):
form = CvForm(request.POST or None)
if request.method == 'POST':
cv = form.save()
return HttpResponseRedirect(reverse('create_options', kwargs={'id':cv.pk}))
return render(request,'create_cv.html',{'form': form})
def create_options(request,id):
cv = Cv.objects.get(pk=id)
form = WorkExperienceForm()
return render(request,'create_options.html', {'cv': cv, 'form': form})
Роутинг
from django.conf.urls import url
from django.contrib import admin
from main.views import create_cv, create_options
from django.urls import path
urlpatterns = [
path('', create_cv),
path(r'create/options/<int:id>/', create_options, name='create_options'),
url(r'^admin/', admin.site.urls),
]
Шаблон другої сторінки.
<html>
<head></head>
<body>
<h1>Create options for {{ cv.name }}</h1>
<form action="" method="POST">
{{ form }}
<input type="submit" value="Submit" />
</form>
</body>
</html>
Тепер спробуємо додати кілька форм як набір.
from django.forms.formsets import formset_factory
def create_options(request,id):
cv = Cv.objects.get(pk=id)
expirience_formset = formset_factory(WorkExperienceForm, extra=2)
return render(request,'create_options.html', {'cv': cv, 'expirience_formset': expirience_formset})
Шаблон.
<html>
<head></head>
<body>
<h1>Create options for {{ cv.name }}</h1>
<form action="" method="POST">
{% for form in expirience_formset %}
{{ form.as_p }}
{% endfor %}
<input type="submit" value="Submit" />
</form>
</body>
</html>
Заповнимо початковими значеннями з об’єкта резюме.
def create_options(request,id):
cv = Cv.objects.get(pk=id)
ExpirienceFormSet = formset_factory(WorkExperienceForm, extra=2)
expirience_formset = ExpirienceFormSet(initial=[
{'cv': cv }
])
Як бачимо, заповнилася тільки перша форма і тепер їх стало 3.
Змінимо трохи структуру та визначимо збереження постом.
if request.method == 'POST':
expirience_formset = ExpirienceFormSet(request.POST)
for form in expirience_formset:
form.save()
else:
expirience_formset = ExpirienceFormSet(initial=[
{'cv': cv },
{'cv': cv }
])
При збереженні помилку.
Необхідно було додати шаблон поля менеджера форм.
...
{{ expirience_formset.management_form }}
{% for form in expirience_formset %}
{{ form.as_p }}
{% endfor %}
...
При перезбереженні додаються дублікати та не редагуються вже створені записи.
Для цього спробувавши ввести у форму ідентифікатори
class WorkExperienceForm(ModelForm):
id = forms.CharField(label='Id', max_length=100)
class Meta:
model = WorkExperience
fields = ['cv', 'position', 'company', 'id']
Перевизначимо метод save.
def save(self, commit=False, *args, **kwargs):
m = super(WorkExperienceForm, self).save(commit=False, *args, **kwargs)
if not m.id:
ex = WorkExperience()
ex.position = m.position
ex.cv = m.cv
else:
ex = WorkExperience.objects.get(pk=m.id)
ex.position = m.position
ex.save()
return ex.cv
Додамо об’єкти у завірюсі у формсет.
def create_options(request,id):
cv = Cv.objects.get(pk=id)
objs = []
for exp in WorkExperience.objects.filter(cv=cv):
objs.append({'cv': exp.cv, 'position': exp.position, 'company': exp.company, 'id': exp.id})
ExpirienceFormSet = formset_factory(WorkExperienceForm,extra=0)
objs.append({'cv': cv})
if request.method == 'POST':
expirience_formset = ExpirienceFormSet(request.POST)
for form in expirience_formset:
if form.is_valid():
cv = form.save()
return HttpResponseRedirect(reverse('create_options', kwargs={'id':cv.pk}))
else:
expirience_formset = ExpirienceFormSet(initial=objs)
return render(request,'create_options.html', {'cv': cv, 'expirience_formset': expirience_formset})
Виведемо приховані поля формою.
{% for form in expirience_formset %}
{{ form.as_p }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endfor %}