Saya mencoba untuk mengadaptasi pendekatan untuk menyimpan formet bersarang dengan bentuk utama menggunakan fitur tata letak Django-Crispy-Forms tapi saya tidak bisa menyimpannya. Saya mengikuti ini contoh proyek kode tapi tidak bisa mendapatkan formset divalidasi untuk menyimpan data. Saya akan sangat berterima kasih jika seseorang bisa menunjukkan kesalahan saya. Saya juga perlu menambahkan tiga inline dalam tampilan yang sama untuk EmployeeForm. Saya mencoba Django-Extra-Views tetapi tidak berhasil. Sangat menghargai jika Anda menyarankan untuk menambahkan lebih dari satu inline untuk tampilan yang sama seperti di sekitar 5. Yang ingin saya capai yaitu satu halaman untuk membuat Employee
dan inline-nya suka Education, Experience, Others
. Di bawah ini adalah kode:
model:
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='employees',
null=True, blank=True)
about = models.TextField()
street = models.CharField(max_length=200)
city = models.CharField(max_length=200)
country = models.CharField(max_length=200)
cell_phone = models.PositiveIntegerField()
landline = models.PositiveIntegerField()
def __str__(self):
return '{} {}'.format(self.id, self.user)
def get_absolute_url(self):
return reverse('bars:create', kwargs={'pk':self.pk})
class Education(models.Model):
employee = models.ForeignKey('Employee', on_delete=models.CASCADE, related_name='education')
course_title = models.CharField(max_length=100, null=True, blank=True)
institute_name = models.CharField(max_length=200, null=True, blank=True)
start_year = models.DateTimeField(null=True, blank=True)
end_year = models.DateTimeField(null=True, blank=True)
def __str__(self):
return '{} {}'.format(self.employee, self.course_title)
Melihat:
class EmployeeCreateView(CreateView):
model = Employee
template_name = 'bars/crt.html'
form_class = EmployeeForm
success_url = None
def get_context_data(self, **kwargs):
data = super(EmployeeCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['education'] = EducationFormset(self.request.POST)
else:
data['education'] = EducationFormset()
print('This is context data {}'.format(data))
return data
def form_valid(self, form):
context = self.get_context_data()
education = context['education']
print('This is Education {}'.format(education))
with transaction.atomic():
form.instance.employee.user = self.request.user
self.object = form.save()
if education.is_valid():
education.save(commit=False)
education.instance = self.object
education.save()
return super(EmployeeCreateView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('bars:detail', kwargs={'pk':self.object.pk})
Formulir:
class EducationForm(forms.ModelForm):
class Meta:
model = Education
exclude = ()
EducationFormset =inlineformset_factory(
Employee, Education, form=EducationForm,
fields=['course_title', 'institute_name'], extra=1,can_delete=True
)
class EmployeeForm(forms.ModelForm):
class Meta:
model = Employee
exclude = ('user', 'role')
def __init__(self, *args, **kwargs):
super(EmployeeForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Field('about'),
Field('street'),
Field('city'),
Field('cell_phone'),
Field('landline'),
Fieldset('Add Education',
Formset('education')),
HTML("<br>"),
ButtonHolder(Submit('submit', 'save')),
)
)
Objek Tata Letak Kustom seperti contoh:
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string
class Formset(LayoutObject):
template = "bars/formset.html"
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
self.fields = []
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.template, {'formset': formset})
Formset.html:
{% load static %}
{% load crispy_forms_tags %}
{% load staticfiles %}
<table>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field|as_crispy_field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'js/jquery.formset.js' %}">
</script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
Tidak ada kesalahan di terminal dan atau sebaliknya. Bantuan sangat dihargai.
sumber
Jawaban:
Anda saat ini tidak memproses formset dengan benar di
CreateView
.form_valid
dalam pandangan itu hanya akan menangani form induk, bukan formet. Apa yang harus Anda lakukan adalah menggantipost
metode, dan di sana Anda perlu memvalidasi baik formulir dan setiap formset yang dilampirkan:Kemudian Anda memodifikasi
form_valid
seperti ini:Cara yang Anda gunakan saat
get_context_data()
ini tidak benar - hapus metode itu sepenuhnya. Seharusnya hanya digunakan untuk mengambil data konteks untuk membuat template. Anda tidak boleh menyebutnya dariform_valid()
metode Anda . Alih-alih, Anda perlu meneruskan formset ke metode ini daripost()
metode seperti diuraikan di atas.Saya telah meninggalkan beberapa komentar tambahan dalam kode contoh di atas yang diharapkan akan membantu Anda mengetahui hal ini.
sumber
Mungkin Anda ingin melihat paket
django-extra-views
, tampilan menyediakanCreateWithInlinesView
, penyihir memungkinkan Anda untuk membuat formulir dengan inline bersarang seperti Django-admin inlines.Dalam kasus Anda, itu akan menjadi sesuatu seperti itu:
views.py
crt.html
Tampilan
EmployeeCreateView
akan memproses formulir untuk Anda seperti di Django-admin. Dari titik ini Anda bisa menerapkan gaya yang Anda inginkan ke formulir.Saya sarankan Anda mengunjungi dokumentasi untuk informasi lebih lanjut
Diedit: Saya menambahkan
management_form
dan tombol js untuk menambah / menghapus.sumber
management_form
untuk masingformset
Anda mengatakan bahwa ada kesalahan tetapi Anda tidak menunjukkannya dalam pertanyaan Anda. Kesalahan (dan seluruh traceback) lebih penting daripada apa pun yang Anda tulis (kecuali mungkin dari forms.py dan views.py)
Kasing Anda sedikit lebih rumit karena format dan menggunakan beberapa formulir pada CreateView yang sama. Tidak banyak (atau tidak banyak contoh) di internet. Sampai Anda menggali dalam kode Django bagaimana formline inline bekerja Anda akan mengalami masalah.
Ok langsung ke intinya. Masalah Anda adalah bahwa formet tidak diinisialisasi dengan instance yang sama dengan form utama Anda. Dan ketika formulir amin Anda menyimpan data ke database, instance dalam formset tidak berubah dan pada akhirnya Anda tidak memiliki ID objek utama untuk dimasukkan sebagai kunci asing. Mengubah atribut instance dari atribut form setelah init bukanlah ide yang baik.
Dalam bentuk normal jika Anda chnage setelah is_valid Anda akan memiliki hasil yang tidak terduga. Untuk formet yang mengubah atribut instance bahkan langsung setelah init tidak akan melakukan apa-apa, karena form dalam formset sudah diinisialisasi dengan beberapa instance, dan mengubahnya setelah itu tidak akan membantu. Berita baiknya adalah Anda dapat mengubah atribut instance setelah Formset diinisialisasi, karena semua atribut instance form akan menunjuk ke objek yang sama setelah formset diinisialisasi.
Anda memiliki dua opsi:
Alih-alih mengatur atribut instance jika formset, atur hanya instance.pk. (Ini hanya dugaan saya belum pernah melakukannya tetapi saya pikir itu harus berhasil. Masalahnya adalah itu akan terlihat seperti hack). Buat formulir yang akan menginisialisasi semua formulir / formulir sekaligus. Ketika metode is_valid () dipanggil semua fomrs harus divalidasi. Ketika metode save () dipanggil, semua formulir harus disimpan. Maka Anda perlu mengatur atribut form_class dari CreateView Anda ke kelas bentuk itu. Satu-satunya bagian yang sulit adalah setelah form utama Anda diinisialisasi, Anda perlu menginisialisasi yang lain (formests) dengan instance dari form pertama Anda. Anda juga perlu mengatur form / formet sebagai atribut dari form Anda untuk memiliki akses ke mereka di Templat. Saya menggunakan pendekatan kedua ketika saya harus membuat objek dengan semua objek terkait itu.
diinisialisasi dengan beberapa data (dalam hal ini data POST) diperiksa validitasnya dengan is_valid () dapat disimpan dengan save () ketika valid. Anda mempertahankan antarmuka bentuk dan jika Anda membuat formulir dengan benar, Anda bahkan dapat menggunakannya tidak hanya untuk membuat tetapi untuk memperbarui objek bersama-sama dengan objek terkait dan tampilan akan sangat sederhana.
sumber