how to pass initial value of FileField to Formset (in Django) - django

I am trying to populate a Django formset using data from a POST/FILE request. I am able to populate all the fields except the FileField. It seems that initial cannot be used to pass the request.FILE to the FormSet creator function. My question is how to pass the FILE to FormSet.
The model.py
class ArticleForm(forms.Form):
docfile = forms.FileField()
subject = forms.Charfield(max_length=128)
ArticleFormSet = formset_factory(ArticleForm, extra=2)
the views.py
formset = ArticleFormSet(request.POST, request.FILE)
#do some other work, and then re-display the POST data
data = formset.cleaned_data
formset = ArticleFormSet(initial=data)
return render_to_response('some.html',
{'formset':formset}
)

You can not pass initial data to a file field.
The <input type="file" /> will always be blank when the browser renders it.
To pass request.FILES to a formset, just specify it as the second argument after POST.
http://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-a-formset-in-views-and-templates
FormSet(request.POST, request.FILES)

You can not pass any initial values when you are working with file uploads.

Related

How to update formset with new pk id?

I have a formset that I am rendering with the default {{ formset }}. The page does a post back to itself when the form is submitted.
The issue is when new instances are created, the fields containing the ids are still blank like <input id="id_form-0-id" name="form-0-id" type="hidden">. So if I resubmit the form after it comes back, it ends up creating new instances instead of updating the newly-created instances. If I refresh the page then the fields contain the ids <input id="id_form-0-id" name="form-0-id" type="hidden" value="18">.
This is my controller function:
def main_categories (request):
dict = {}
FormSet = modelformset_factory (MainCategory, formset = MainCategoryFormSet, fields = ['name'], extra = 1, can_delete = True)
if request.method == 'POST':
formset = FormSet (request.POST)
if formset.is_valid ():
with transaction.atomic ():
formset.save ()
else:
formset = FormSet ()
dict ['formset'] = formset
return render (request, 'equipment/admin/main_categories.html', dict)
If you want to edit previously submitted queryset, you must provide queryset into FormSet class. See documentation

Django Forms choice-like field, without choice limitation

I am writing a writing a webapp that is basically just a form, and it has a button that duplicates a field so that multiple items can be entered. I can't use a SelectMultiple field or any of its variations because there is not a set number of choices to choose from. The user should be able to enter whatever they want into the fields and they must be saved in the model and linked to a record through a manytomany field. Here is a jsfiddle link for demonstration.
HTML
<form>
<label>Field 1
<textarea rows="3"></textarea>
</label>
<label>Multiple Values Possible</label>
<div>
<input type="text">
<select>
<option value="1">1</option>
<option value="2">2</option>
</select>
</div>
<button id="add_div">Add</button>
</form>
JS
add_div = document.getElementById("add_div");
add_div.onclick = function () {
var div = this.previousElementSibling;
var new_div = div.cloneNode(true);
this.parentNode.insertBefore(new_div, this);
return false;
}.bind(add_div);
I cannot figure out how to create the form backend for this. There aren't any field classes that can take in a variable amount of data and validate each one against another field.
What I have tried to do is create a MultiWidget/MultiValueField for the textbox/select dropdown pair, and then subclass my MultiValueField in a class closely following django's ModelMultipleChoiceField. I got stuck trying to get the form field to work with templates, allowing me to add all fields back to the rendered page when rendering with a particular form instance (like how when you use the CheckboxSelectMultiple widget, boxes that are checked in a form instance are rendered checked)
Is there any way to do this and have the ModelForm's save method also save the manytomany fields properly? I know I can override the form's save method and do something like in this stackoverflow question, but I would rather have all the save logic handled by the form fields themselves.
Based on looking at your example jsfiddle, it looks like you don't really need a "Choice Field", what you're looking for are Formsets.
In essence, you would have 2 forms on the page, one which is a normal form and would take care of Field 1, and one which is a Formset which deals with all the many-to-many relations for Field 2
Field2FormSet = formset_factory(Field2ToForm)
Make sure you output the management_form which you can clone with your "add" button.
What you are probably looking for is an inline formset, which can only be used if you are using django models (which you hinted at in your question).
Check out this guide: http://lab305.com/news/2012/jul/19/django-inline-formset-underscore/.
For the lazy, here is a quick example that gets you most of the way there. This app will allow you to continuously add Parent model objects to the database, along with any children that are filled out.
app/models.py
from django.db import models
class ParentModel(models.Model):
parent_field = models.CharField(choices=[(1, 1), (2, 2)])
class ChildModel(models.Model):
parent = models.ForeignKey(ParentModel)
child_field = models.IntegerField(choices=[(1, 1), (2, 2)])
app/views.py
from app import models
from django import forms
from django.forms.models import inlineformset_factory
from django.template import RequestContext, Template
from django.http import HttpResponse
class ParentForm(forms.ModelForm):
class Meta:
model = models.ParentModel
ChildFormSet = inlineformset_factory(models.ParentModel, models.ChildModel)
def test_view(request):
if request.method == "POST":
form = ParentForm(request.POST, request.FILES)
formset = ChildFormSet(request.POST, request.FILES, form.instance)
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
else:
pass # Handle validation error
template = Template(
"<form action='<url for view>' method='post'>"
"{% csrf_token %}"
"{{ form.as_p }}"
"<p>{{ formset.as_table }}</p>"
"<input type='submit' value='Submit'/>"
"</form>"
)
context = RequestContext(request, {
"form": ParentForm(),
"formset": ChildFormSet(),
})
return HttpResponse(template.render(context))
What is shown above will only allow you add up to three children (the default number of extra forms the inline form set produces). To add dynamically, you are going to have to add some java script that creates new forms in the form set on the client side. For that, I suggest you look at the guide I posted above since I don't think I can do better job of explaining it.
Thanks to #Kevin and #Thomas for pointing me towards formsets! Here is how I did it:
models.py
from django.db import models
class RelatedField(models.Model):
field1 = models.CharField(max_length=50)
field2 = models.IntegerField(choices=[(x, x) for x in xrange(1, 11)])
class Record(models.Model):
user = models.ForeignKey(User)
field = models.CharField(max_length=20)
relatedA = models.ManyToManyField(RelatedField, related_name='relatedA')
relatedB = models.ManyToManyField(RelatedField, related_name='relatedB')
views.py
def getIndexContext(data):
if data is None:
recordForm = RecordForm()
relatedFormA = RelatedFormSet(queryset=RelatedField.objects.none(), prefix='related-a')
relatedFormB = RelatedFormSet(queryset=RelatedField.objects.none(), prefix='related-b')
else:
recordForm = RecordForm(data)
relatedFormA = RelatedFormSet(data, prefix='related-a')
relatedFormB = RelatedFormSet(data, prefix='related-b')
return {
'form': recordForm,
'relatedA': relatedFormA,
'relatedB': relatedFormB,
'title': 'Index',
}
def index(request):
if request.method == 'GET':
return render(request, 'record/index.html', getIndexContext(None))
else:
context = getIndexContext(request.POST)
form = context['form']
relatedA = context['relatedA']
relatedB = context['relatedB']
if form.is_valid() and relatedA.is_valid() and relatedB.is_valid():
obj = form.save(commit=False)
obj.user_id = request.user
obj.save()
form.save_m2m()
instances = relatedA.save()
obj.relatedA.add(*instances)
instances = relatedB.save()
obj.relatedB.add(*instances)
return HttpResponse('success!')
else:
return render(request, 'record/index.html', context)
And then some javascript that can duplicate the fields in the formsets, and increment the names by one.

Django forms: prepopulate form with request.user and url parameter

I'm building simple Course Management App. I want Users to sign up for Course. Here's sign up model:
class CourseMembers(models.Model):
student = models.ForeignKey(Student)
course = models.ForeignKey(Course)
def __unicode__(self):
return unicode(self.student)
Student model is extended User model - I'd like to fill the form with request.user.
In Course model most important is course_id, which i'm passing into view throught URL parameter (for example http://127.0.0.1:8000/courses/course/1/).
What i want to achieve, is to generate 'invisible' (so user can't change the inserted data) form with just input, but containing request.user and course_id parameter.
You want hidden inputs:
<input type="hidden" name="user" value="{{request.user}}"/>
I'd pass in the course_id as a context variable personally, not in the GET:
<input type="hidden" name="course_id" value="{{course_id}}"/>
Or you can get the value from the url string using {{request.get_full_path}}, and some slicing.
I've found answer to my own question. So here's step by step:
First we need to remove all the fields in ModelForm:
class AddCourseMemberForm(forms.ModelForm):
class Meta:
model = CourseMembers
fields = {}
As there's no data we want to get through user input, we just send empty POST request, and then insert the data directly to our form model, and then save it:
if request.method == 'POST':
form = AddCourseMemberForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.student_id = user.id #from request.user
form.course_id = course.id #from url parameter i.e courses/course/1/
form.save()
return redirect('index')
As I'm still learning I need to know if this is the safe way of doing things, as well as if is_valid method() makes sense. I think I need to clean course.id just in case and maybe validate right before save?

How to extract File Object from Django Form FileField

I have created a ModelForm with fields, title, file and content. Here file is a FileField(). But I can't call the save() method of this form due to some reasons. So I have to maually created one Model Object and assign cleaned values to that object. Everything worked excpt that FileField. The file is not saving. How can I fix this? Is it the correct method to extract FileField?
Form
class TestForm(forms.ModelForm):
class Meta:
model = Test
fields = ('title','file', 'content',)
Views.py
form = TestForm(request.POST,request.FILES)
if form.is_valid():
content = form.cleaned_data['content']
file = form.cleaned_data['file']
title = form.cleaned_data['title']
fax = Fax()
fax.title = title
fax.file = file
fax.content = content
fax.save()
Here the file is not saving. How can I fix this?
Any help will be appreciated!
Have u used enctype="multipart/form-data" in your form
Seems like the code is fine.
Please using this type of validation. This may work
if request.method == 'POST':
form = ModelFormWithFileField(request.POST, request.FILES)
if form.is_valid():
# file is saved
form.save()
return HttpResponseRedirect('/success/url/')``
I think you can use
request.FILES['file']
for getting the file object

Django: Can't save a form

I allow users to view and edit a few fields of a database record represented by a ModelForm.
Here is a snippet of the code from the view:
def edit(request, id):
obj = get_object_or_404(Record, pk=record_id)
if request.method == 'POST':
form = forms.RecordForm(request.POST, instance=obj)
if form.is_valid():
form.save()
The problem is that because I don't pass all the fields to the template, form.is_valid() fails with a missing values error. How can I update an existing record with just the subset of record fields I display to the user?
Use the fields tuple in the form's Meta definition to make sure the form only includes the fields you need - or use exclude to remove the ones you don't want.