Is it possible to output a ModelForm using a class method; for example:
def edit_form(self, *args, **kwargs):
from smf.node.forms import MediaBaseForm
return MediaBaseForm(instance=self)
(MediaBaseForm is a ModelForm subclass for model Media), and then in the view:
form = node.edit_form()
(node contains the instance)
This code executes without raising errors; however, when viewing the page, no form fields are generated when I try to render each field separatly. The form does display when I use {{ formset }} but not using {% for form in formset.forms %}} ..etc. {% endfor %} -->
I need this:
{{ formset.management_form }}
{% for form in formset.forms %}
{{ form.id }}
<div id="form_title">Title: {{ form.title }}</div>
<p>Description:</p> {{ form.description }}
<p>Comment settings:</p> {{ form.comment }}
<p>Promoted to frontpage:</p> {{ form.promote }}
<p>Sticky:</p> {{ form.sticky }}
<p>Ready for Publishing:</p> {{ form.status }}
{% endfor %}
instead of this:
{{ formset }}
Is it not possible to call a form this way or am i doing something wrong?
Complete view:
def node_edit(request, nid):
#Build the standard node form
node = node_load(nid)
obj = node_get_model(node.type, True)
#modelFormset = modelformset_factory(obj, max_num=1)
form = node.edit_form()
if request.method == 'POST': # If the form has been submitted...
#form = modelFormset(request.POST, request.FILES)
form = node.edit_form()
if form.is_valid():
instances = form.save(commit=False)
for instance in instances:
instance.save()
node_post_process(node, request, obj)
if 'save_edit' in request.POST:
id = int(nid)
return HttpResponseRedirect('/edit/%i' %(id))
if 'save' in request.POST:
#id = node_callback_value.Node_ptr
id = int(nid)
return HttpResponseRedirect('/content/%i' %(id))
if 'savenew' in request.POST:
return HttpResponseRedirect('/content/add/%s' %(node.type))
else:
output = {}
#form = modelFormset(queryset=obj.objects.filter(node_ptr=nid))
form = node.edit_form(instance=node)
output = {
'formset': form,
'node': node,
}
try:
return render_to_response('node/forms/%s.edit.html' %(node.type), output)
except TemplateDoesNotExist:
return render_to_response('node/forms/default.form.html', {
'formset': form,
})
I can't understand what you're trying to do. There are a number of problems with your approach, though.
Firstly, there's no way of passing the POSTed values to the form instantiation. As a result, form.is_valid() will always be False.
Secondly, the result of node.edit_form() appears to be a single form, not a formset. So I don't know why you are passing it to the template context as formset and trying to iterate through formset.forms.
Related
Am new to Django am trying to implement a simple select tag, the values are rendered with no problem but the value is not bound to the form during submission and I keep getting the message that the value is required
Form
class UploadFileForm(forms.Form):
job_type = forms.ChoiceField(widget=forms.Select, choices=JOB_TYPES)
HTML
<div class="form-group">
<select class="browser-default custom-select">
{% for type in form.job_type %}
{{ type }}
{% endfor %}
</select>
</div>
VIEW
def simple_upload(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
return render(request, 'upload/upload.html', {'form': form})
else:
form = UploadFileForm()
return render(request, 'upload/upload.html', {'form': form})
I also tried to do {{ form.job_type }} and this one works fine but then I can't use the required css, But I want to freely change css and style in the HTML file without referring to the form field in forms.py.
You can iterate over the subwidgets of the Select widget, and then render the option.data.label:
{% for option in form.job_type %}
<select value="{{ option.data.value }}">{{ option.data.label }}</select>
{% endfor %}
But if you however only want to apply a certain class to the Select widget, I advice you to specify this in the attrs, like:
class UploadFileForm(forms.Form):
job_type = forms.ChoiceField(
widget=forms.Select(attrs={'class': 'browser-default custom-select'}),
choices=JOB_TYPES
)
I've got a modelformset which is shown in below:
views.py
def manage_authors(request):
AuthorFormSet = modelformset_factory(Author, fields=('name', 'title', 'birth_date'))
if request.method == 'POST':
if "del_btn" in request.POST:
query = Author.objects.get(...).delete()
formset = AuthorFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
formset = AuthorFormSet(queryset=Author.objects.all())
print "yes"
else:
formset = AuthorFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
formset = AuthorFormSet(queryset=Author.objects.all())
else:
formset = AuthorFormSet(queryset=Author.objects.all())
return render(request, "manage_authors.html", {"formset": AuthorFormSet, })
manage_authors.html
<form method="post" action="/manage_authors.html">{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{{ form.id }}
<ul>
{{ form.name }} {{ form.title }} {{ form.birth_date }}
<input type="submit" name="del_btn" value="Delete"/>
</ul>
{% endfor %}
<input type='submit' name="edit_btn" value='Edit / Add'/>
What query I can define so that the Delete button could work?
Now, I got problem with knowing which row must be deleted.
Thanks in advance
First I recommend registering your user model in admin.py:
admin.site.register(Author)
Django will handle the rest.
But if you want to do it with this code do like this:
<form method="post" action="/manage_authors.html">{% csrf_token %}
{{ formset.management_form }}
for form in formset %}
{{ form.id }}
<ul>
{{ form.name }} {{ form.title }} {{ form.birth_date }}
<input type="submit" name="del_btn{{ form.instance.id }}" value="Delete"/>
</ul>
{% endfor %}
<input type='submit' name="edit_btn" value='Edit / Add'/>
So that primary key of object will relate to the delete button name.
Now, in views.py:
import re, urllib
def manage_authors(request):
AuthorFormSet = modelformset_factory(Author, fields=('name', 'title', 'birth_date'))
if request.method == 'POST':
enurl=urllib.urlencode(request.POST) # To convert POST into a string
matchobj=re.search(r'del_btn\d', enurl) # To match for e.g. del_btn1
btnname=matchobj.group() # Contains matched button name
pri_key=btname[-1] # slice the number of btn to identify primarykey
if matchobj:
query = Author.objects.get(pk=pri_key).delete()
formset = AuthorFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
formset = AuthorFormSet(queryset=Author.objects.all())
print "yes"
else:
formset = AuthorFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
formset = AuthorFormSet(queryset=Author.objects.all())
else:
formset = AuthorFormSet(queryset=Author.objects.all())
return render(request, "manage_authors.html", {"formset": AuthorFormSet, })
I'm looking for a convenient solution to create an 'edit settings' key/values page.
Parameters model :
class Parameter(models.Model):
key = models.CharField(max_length=50)
value = models.CharField(max_length=250)
showInUI = models.SmallIntegerField()
Initial Keys/Values are already inserted in table.
I load them and send them using a model formset factory using these lines :
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('key', 'value'))
parameterFormSet = ParameterFormSet(queryset=Parameter.objects.filter(showInUI=1))
return render_to_response('config.html', {'parameterFormSet': parameterFormSet}, context_instance=RequestContext(request))
Template side, when formset is displayed, keys and values are shown as inputs.
I'd like to find a convenient way to display form keys as readonly labels and values as inputs. And, when submited, validate them according django standards.
I've read a lot of stuff, I guess the solution may be a custom widget, but I could find a reliable solution.
Thanks for reading.
EDIT :
Working solution
views.py
def config(request):
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',))
if request.method == "POST":
try:
formset = ParameterFormSet(request.POST, request.FILES)
except ValidationError:
formset = None
return HttpResponse("ko")
if formset.is_valid():
formset.save()
return HttpResponse("ok")
#ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',))
parameterFormSet = ParameterFormSet(queryset=Parameter.objects.filter(showInUI=1))
return render_to_response('config.html', {'parameterFormSet': parameterFormSet}, context_instance=RequestContext(request))
template
<form method="post">
{% csrf_token %}
{{ parameterFormSet.management_form }}
{% for form in parameterFormSet %}
<div>
{{ form.instance.key }}
{{ form }}
</div>
{% endfor %}
<input type="submit" />
</form>
If you do not want the value to be editable, don't include it in fields when creating the form set.
ParameterFormSet = modelformset_factory(Parameter, extra=0, fields=('value',)) # don't forget the trailing comma after 'value' otherwise it's not a tuple!
In your template, you can then loop through the forms in the form set, and display the key at the same time.
{% for form in parameter_form_set %}
{{ form.instance.key }}{# display the key related to this form #}
{{ form }}{# display the form #}
{% endfor %}
I have a ModelForm that users can submit to save information to a database. I want to extend it with a ModelFormset so that the user can view and submit the multiple of the same model forms with different information at the same time. However, my POST data isn't binding to the ModelFormset, so the ModelFormset fails as invalid upon is_valid(). I see there is data associated with request.POST.copy(), it just
views.py
def create(request):
if request.method == 'POST':
post_data = request.POST.copy()
print "POST DATA"
print post_data
for i in post_data:
print i
formSet = WorkOrder_Form(post_data)
print "FORMSET"
print formSet
if formSet.is_valid():
formSet.save()
else:
print 'INVALID'
return HttpResponseRedirect('/Shelling/')
else:
formSet = formset_factory(WorkOrder_Form, extra=1)
return render_to_response('create.html',{'WorkOrder_Form':formSet}, context_instance=RequestContext(request))
template: (create.html)
{% load url from future %}
Return to Index </li>
<br>
<br>
<form action="{% url 'create' %}" method="post"> {% csrf_token %}
{% for WorkOrder in WorkOrder_Form %}
{{ WorkOrder.as_ul }}
<br>
{% endfor %}
You are using model forms, so you should use modelformset_factory instead of formset_factory. You can create the formset class outside of the create view. Then, you need to instantiate the formset in the GET and POST branches of your view.
Putting it together, you have the following (untested, so there might be some typos!)
WorkOrderFormSet = formset_factory(WorkOrder_Form, extra=1)
def create(request):
if request.method == 'POST':
post_data = request.POST.copy()
formset = WorkOrderFormSet(data=post_data, queryset=WorkOrder.objects.none())
if formset.is_valid():
formset.save()
else:
print 'INVALID'
return HttpResponseRedirect('/Shelling/')
else:
formset = WorkOrderFormSet(queryset=WorkOrder.objects.none())
return render_to_response('create.html',{'formset':formset}, context_instance=RequestContext(request))
And in the template:
{% for form in formset %}
{{ form.as_ul }}
{% endfor %}
I'm using Django and have a form with two additional inline formsets. I want to validate that each formset contains at least one populated form. I've written code such that this works but it only works for each formset at a time. If I submit the form without any formset forms populated, only the first one shows a validation error. If I then populate the first formset form, and leave the second one blank, the second one errors.
I want errors to appear on both forms if both are not valid.
The forms are just standard ModelForm instances. Here's my view:
class RequiredBaseInlineFormSet(BaseInlineFormSet):
def clean(self):
self.validate_unique()
if any(self.errors):
return
if not self.forms[0].has_changed():
raise forms.ValidationError("At least one %s is required" % self.model._meta.verbose_name)
def create(request):
profile_form = ProfileForm(request.POST or None)
EmailFormSet = inlineformset_factory(Profile, Email, formset=RequiredBaseInlineFormSet, max_num=5, extra=5, can_delete=False)
email_formset = EmailFormSet(request.POST or None)
PhoneFormSet = inlineformset_factory(Profile, Phone, formset=RequiredBaseInlineFormSet, max_num=5, extra=5, can_delete=False)
phone_formset = PhoneFormSet(request.POST or None)
if profile_form.is_valid() and email_formset.is_valid() and phone_formset.is_valid():
profile = profile_form.save()
emails = email_formset.save(commit=False)
for email in emails:
email.profile = profile
email.save()
phones = phone_formset.save(commit=False)
for phone in phones:
phone.profile = profile
phone.save()
messages.add_message(request, messages.INFO, 'Profile successfully saved')
return render_to_response(
'add.html', {
'profile_form': profile_form,
'email_formset': email_formset,
'phone_formset': phone_formset
}, context_instance = RequestContext(request)
)
And here's my template's form, incase it's useful:
<form action="" method="post" accept-charset="utf-8">
{{ email_formset.management_form }}
{{ phone_formset.management_form }}
{{ profile_form|as_uni_form }}
<div class="formset-group" id="email_formset">
{{ email_formset.non_form_errors }}
{% for email_form in email_formset.forms %}
<div class='form'>
{{ email_form|as_uni_form }}
</div>
{% endfor %}
</div>
<div class="formset-group" id="phone_formset">
{{ phone_formset.non_form_errors }}
{% for phone_form in phone_formset.forms %}
<div class='form'>
{{ phone_form|as_uni_form }}
</div>
{% endfor %}
</div>
<input type="submit" value="Save Profile" id="submit">
</form>
call the is_valid() function for each form that you want validation to occur on. In your example you do if a.is_valid and b.is_valid anc c.is_valid... If a is false, b and c will never get called. Try something different, like:
alpha=a.is_valid()
beta=b.is_valid()
gamma=c.is_valid()
if alpha and beta and gamma:
do stuff
I had a similar issue and the problem was that extra forms were not being validated due to how Django handles extra form fields. Take a look: Django Formset.is_valid() failing for extra forms