Using ModelFormMixin (base class of MFormsView) without the 'fields' attribute is prohibited.
class MFormsView(LoginRequiredMixin, MFView):
template_name = 'temps/cnfg_form.html'
form_classes = {
'abc': abcForm,
'md': mdForm,
}
...
...
class MFView(ProcessFormView):
"""
A mixin that processes multiple forms on POST. Every form must be
valid.
"""
def get(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
return self.render_to_response(self.get_context_data(forms=forms))
...
...
<div class="col-md-5">{{ forms.abc.days }}</div>
<div class="col-md-13 checkbox col-sm-pad">{{ forms.md.started }}</div>
Using ModelFormMixin (base class of MFormsView) without the 'fields' attribute is prohibited.
On Monday 23 January 2017 06:41:53 scha...@gmail.com wrote:
> This worked well like that until Django 1.8, but now with Django 1.9
This stopped working in Django 1.8 :
"Class-based views that use ModelFormMixin will raise an ImproperlyConfigured exception when both the fields and form_class attributes are specified. Previously, fields was silently ignored."
And burried in the docs, a different case for the same thing:
In older versions, omitting both fields and exclude resulted in a form with all the model’s fields. Doing this now raises an ImproperlyConfigured exception.
For someone who didn't follow the discussion on the topic, it sure is hard to find in release notes.
> the
> following error occurs:
> > Using ModelFormMixin (base class of MFormsView) without the 'fields'
> > attribute is prohibited.
> Now my question:
> 1.) Why do I get this message, as the fields are defined in both
> ModelForms?
The model form is missing a 'fields' attribute. Example:
class ComponentStockAllocationAssign(StockMixin, generic.CreateView):
model = models.ComponentLocationStock
fields = ['component', 'location', 'stock']
success_url = reverse_lazy("component_allocation_list")
> 2.) What is the best way to include 2 different forms
> into the same template? Do you have an example?
That's a much bigger question than it seems. What's the relation between the forms?
If they're unrelated, implement get_context_data() to assign them, and form_valid() to process them.
Also, bookmark this: https://ccbv.co.uk/
--
Melvyn Sopacua
This stopped working in Django 1.8 :
"Class-based views that use ModelFormMixin will raise an ImproperlyConfigured exception when both the fields and form_class attributes are specified. Previously, fields was silently ignored."
In older versions, omitting both fields and exclude resulted in a form with all the model’s fields. Doing this now raises an ImproperlyConfigured exception.
Hello,
Assuming the fields issue is resolved. If not, please show the abcForm.
On Monday 23 January 2017 08:14:09 scha...@gmail.com wrote:
> I guess I first need to fully understand the Django ModelForm and how
> a template can handle two independent forms. Maybe I also have to
> change the concept?
> data from different models have to be displayed on one template and
> for that 2 ModelForms were generated and added to a main view, but
> this does notwork in 1.9 till now.
The generic class-based views support common patterns, but in a way that more complex patterns can be built. Yours is a more complex pattern.
The defaults always assume a one on one relation between a ListView/DetailView and a Model and also one Form with one FormView.
Under the hood, Model forms are handled like this:
* add the form instance to the template context (using get_context_data) so it can be rendered
* deligate to form_valid or form_invalid based on the form's is_valid() method, if the request method is put or post (using ProcessFormView post and put)
* call save() on the ModelForm to save the object(s) (using form_valid)
What I'm not seeing in your code is how ModelFormMixin ties in. I'm going to guess that ProcessFormView is not the ProcessFormView from django.views.generic.edit.
Could you run the django shell (python manage.py shell) import the view and show it's MRO? For example:
>>> from kernel.views import ModelJSONEncoder
>>> ModelJSONEncoder.__mro__
(<class 'kernel.views.ModelJSONEncoder'>, <class 'django.core.serializers.json.DjangoJSONEncoder'>, <class 'json.encoder.JSONEncoder'>, <class 'object'>)
--
Melvyn Sopacua
views.py
class MFormsView(LoginRequiredMixin, StaffuserRequiredMixin, UpdateView, MFView):
template_name = 'temps/cnfg_form.html'
form_classes = {
'abc': abcForm,
'md': mdForm,
}
breadcrumbs = ['cnfg_ovw']
def get_initial(self):
return {'pk': self.kwargs['pk']}
def get_success_url(self):
return reverse('cnfg_ovw')
def post(self, request, *args, **kwargs):
return super(MFormsView, self).post(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
return super(MFormsView, self).get(request, *args, **kwargs)
def forms_valid(self, form):
if 'save' in self.request.POST.keys():
change_settings(form=form, **self.kwargs)
return HttpResponseRedirect(self.get_success_url())
elif 'reset_to_default' in self.request.POST.keys():
reset_to_default(form=form, **self.kwargs)
return HttpResponseRedirect(reverse('updt_cnfg', kwargs={'pk': self.kwargs['pk']}))
def get_object(self, queryset=None):
self.object = ConfigSetting.objects.all().filter(config__settings__pk=self.kwargs['pk']).get()
return self.object
def get_queryset(self):
self.queryset = ConfigSetting.objects.all().filter(config__settings__pk=self.kwargs['pk'])
return self.queryset
def get_context_data(self, **kwargs):
context = super(MFormsView, self).get_context_data(**kwargs)
if self.object.label not in ['default']:
context['label'] = "custom config"
else:
context['label'] = self.object.label
return context
mfview.py
from django.views.generic.base import View, TemplateResponseMixin
from django.views.generic.edit import FormMixin, ProcessFormView
class MFMixin(FormMixin):
form_classes = {}
def get_form_classes(self):
return self.form_classes
def get_forms(self, form_classes):
return dict([(key, klass(**self.get_form_kwargs())) \
for key, klass in form_classes.items()])
def forms_valid(self, forms):
return super(MFMixin, self).form_valid(forms)
def forms_invalid(self, forms):
return self.render_to_response(self.get_context_data(forms=forms))
class ProcesMFView(ProcessFormView):
def get(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
return self.render_to_response(self.get_context_data(forms=forms))
def post(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
if all([form.is_valid() for form in forms.values()]):
return self.forms_valid(forms)
else:
return self.forms_invalid(forms)
class BaseMFView(MFMixin, ProcesMFView):
class MFView
(TemplateResponseMixin, BaseMFView):
forms.py
class abcForm(ModelForm):
def __init__(self, *arg, **kwargs):
super(abcForm, self).__init__(*arg, **kwargs)
c = model_to_dict(self.instance.abc_setting)
for k, v in self.fields.items():
setattr(v, 'initial', c[k])
if k in ['c']:
setattr(v, 'label', "Active settings")
self.prefix = "abc"
class Meta:
model = abcSetting
fields = ['a', 'b', 'c', 'f',]
class mdForm(ModelForm):
def __init__(self, *arg, **kwargs):
super(mdForm, self).__init__(*arg, **kwargs)
c = model_to_dict(self.instance.md_setting)
for k, v in self.fields.items():
setattr(v, 'initial', c[k])
self.prefix = "md"
class Meta:
model = mdSetting
fields = ['k', 'r',]
<div class="col-md-3">{{ forms.abc.f }}</div>
form_classes = {
'abc': abcForm,
'md': mdForm,
}
On Tuesday 24 January 2017 05:07:25 scha...@gmail.com wrote:
> sorry I wanted to shorten the code before, to not post too much.
> Here the code:
>
> views.py
> class MFormsView(LoginRequiredMixin, StaffuserRequiredMixin,
> UpdateView, MFView):
And there you have it:
MFormsView uses UpdateView which pulls in ModelFormMixin. So this view needs a fields attribute:
class M MFormsView(LoginRequiredMixin, StaffuserRequiredMixin, UpdateView, MFView):
fields = ['foo', 'bar', 'baz']
--
Melvyn Sopacua
class LayoutFormView(LoginRequiredMixin, StaffuserRequiredMixin, UpdateView):
template_name = 'abc/layout.html'
form_class = LayoutFormSet
breadcrumbs = ['config_list']
def form_valid(self, form):
if 'save' in self.request.POST.keys():
layout_change_settings(form=form, **kwargs)
return HttpResponseRedirect(self.get_success_url())
elif 'reset_to_default' in self.request.POST.keys():
layout_reset_to_default(form=form, **kwargs)
return HttpResponseRedirect(reverse('update_layout', kwargs={'pk': self.kwargs['pk']}))
def get_object(self, queryset=None):
return LayoutSet.objects.get(settings__pk=self.kwargs['pk'])
def get_context_data(self, **kwargs):
context = super(LayoutFormView, self).get_context_data(**kwargs)
if self.object.label not in ['default']:
context['label'] = "custom"
else:
context['label'] = self.object.label
return
context
def get_success_url(self):
return reverse('update_layout', kwargs={'pk': self.kwargs['pk']})
def get_form(self, form_class=None):
if self.request.POST:
return form_class(self.request.POST)
else:
initial = [{'param': 'abc',
'choosen': 'None'}]
return form_class(initial=initial)
class LayoutForm(forms.Form):
def __init__(self, *args, **kwargs):
super(LayoutForm, self).__init__(*args, **kwargs)
param = forms.CharField()
choosen = forms.BooleanField()
class Meta:
fields = ['choosen']
def is_valid(self):
return True
LayoutFormSet = formset_factory(LayoutForm, extra=0)