Próbuję dostosować podejście do zapisywania zagnieżdżonych zestawów formularzy w formularzu głównym za pomocą funkcji układu Django-Crispy-Forms, ale nie mogę go zapisać. Śledzę ten przykładowy projekt kodu, ale nie mogłem uzyskać poprawności zestawu formatów w celu zapisania danych. Będę naprawdę wdzięczny, jeśli ktoś wskaże mój błąd. Muszę również dodać trzy wstawki w tym samym widoku dla EmployeeForm. Próbowałem Django-Extra-Views, ale nie mogłem tego zrobić. Byłbym wdzięczny, gdybyś zalecił dodanie więcej niż jednego wstawki dla tego samego widoku, jak około 5. Wszystko, co chcę, aby osiągnąć tę jedną stronę do tworzenia Employee
i jej wstawki jak Education, Experience, Others
. Poniżej znajduje się kod:
modele:
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)
Widok:
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})
Formularze:
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')),
)
)
Niestandardowy obiekt układu jak na przykład:
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>
Nie ma błędów w terminalu ani w żaden inny sposób. Pomoc jest bardzo ceniona.
źródło
Odpowiedzi:
Obecnie nie przetwarzasz poprawnie zestawu formularzy w swoim
CreateView
.form_valid
w tym widoku obsłuży tylko formularz nadrzędny, a nie zestawy formularzy. To, co powinieneś zrobić, to przesłonićpost
metodę i tam musisz sprawdzić poprawność zarówno formularza, jak i wszelkich dołączonych do niego zestawów formularzy:Następnie modyfikujesz
form_valid
tak:Sposób, w jaki obecnie korzystasz,
get_context_data()
jest nieprawidłowy - usuń tę metodę całkowicie. Należy go używać tylko do pobierania danych kontekstowych do renderowania szablonu. Nie powinieneś wywoływać tego ze swojejform_valid()
metody. Zamiast tego musisz przekazać zestaw formularzy do tej metody zpost()
metody opisanej powyżej.W powyższym przykładowym kodzie zostawiłem kilka dodatkowych komentarzy, które, mam nadzieję, pomogą ci to rozgryźć.
źródło
Być może chciałbyś zobaczyć pakiet
django-extra-views
, zapewnia widokCreateWithInlinesView
, który pozwala tworzyć formularz z zagnieżdżonymi wstawkami, takimi jak wstawki Django-admin.W twoim przypadku byłoby to coś takiego:
views.py
crt.html
Widok
EmployeeCreateView
przetworzy dla ciebie formularze jak w Django-admin. Od tego momentu możesz zastosować żądany styl do formularzy.Polecam odwiedzić dokumentację, aby uzyskać więcej informacji
ZMIENIONO: Dodałem
management_form
i przyciski js, aby dodać / usunąć.źródło
management_form
dla każdegoformset
Powiedziałeś, że wystąpił błąd, ale nie wyświetlasz go w swoim pytaniu. Błąd (i całe śledzenie) jest ważniejszy niż cokolwiek, co napisałeś (może być z forms.py i views.py)
Twoja sprawa jest nieco trudniejsza ze względu na zestawy formularzy i używanie wielu formularzy w tym samym CreateView. W Internecie nie ma wielu (lub wielu dobrych) przykładów. Dopóki nie zagłębisz się w kod django, jak działają wbudowane zestawy formularzy, będziesz mieć problemy.
Ok od razu do rzeczy. Problem polega na tym, że zestawy formularzy nie są inicjowane z tą samą instancją, co formularz główny. A kiedy formularz aminowy zapisuje dane w bazie danych, instancja w zestawie formularzy nie jest zmieniana, a na końcu nie masz identyfikatora głównego obiektu, aby można go było wprowadzić jako klucz obcy. Zmiana atrybutu instancji atrybutu formularza po init nie jest dobrym pomysłem.
W normalnych formach, jeśli zmienisz go po is_valid, będziesz mieć nieprzewidywalne wyniki. W przypadku zestawów formularzy zmiana atrybutu instancji nawet bezpośrednio po init nie zmieni się, ponieważ formularze w zestawie formularzy są już zainicjowane z jakąś instancją, a zmiana po niej nie pomoże. Dobrą wiadomością jest to, że można zmienić atrybuty instancji po zainicjowaniu zestawu formularzy, ponieważ atrybuty instancji wszystkich formularzy będą wskazywały na ten sam obiekt po zainicjowaniu zestawu formularzy.
Masz dwie opcje:
Zamiast ustawiać atrybut instancji, jeśli zestaw formularzy, ustaw tylko instance.pk. (To tylko przypuszczenie, że nigdy tego nie zrobiłem, ale myślę, że powinno działać. Problem polega na tym, że będzie wyglądać jak hack). Utwórz formularz, który zainicjuje wszystkie formularze / zestawy formularzy jednocześnie. Gdy wywoływana jest metoda is_valid (), wszystkie fomrs powinny zostać sprawdzone. Po wywołaniu metody save () wszystkie formularze muszą zostać zapisane. Następnie należy ustawić atrybut form_class obiektu CreateView na tę klasę formularza. Jedyną trudną częścią jest to, że po zainicjowaniu głównego formularza należy zainicjować pozostałe (formsests) za pomocą wystąpienia pierwszego formularza. Musisz także ustawić formularze / zestawy formularzy jako atrybuty formularza, aby mieć do nich dostęp w szablonie. Korzystam z drugiego podejścia, gdy muszę utworzyć obiekt ze wszystkimi powiązanymi obiektami.
zainicjowany z niektórymi danymi (w tym przypadku danymi POST) sprawdzonymi pod kątem ważności za pomocą is_valid () można zapisać za pomocą save (), gdy jest ważny. Zachowujesz interfejs formularza i jeśli poprawnie wykonałeś formularz, możesz go nawet użyć nie tylko do tworzenia, ale i do aktualizacji obiektów wraz z obiektami pokrewnymi, a widoki będą bardzo proste.
źródło