I'm a Python/Django newbie and have been facing the following problem for several days now. I've got 2 ModelForms for a Model and the idea is that the data for one will help populate the multiplechoicefield of the second. The multiplechoice field for the second form populates ok but the problem is that the user's choices don't get saved. I've tried the save(commit=false) and save_m2m() but it hasn't fixed the issue.
models.py:
class Question(models.Model):
asker = models.ForeignKey(Student, related_name='asker+')
suggestedHelpers = models.ManyToManyField(Student, related_name='suggested')
chosenHelpers = models.ManyToManyField(Student, related_name='chosen')
class QuestionForm(ModelForm):
class Meta:
model = Question
exclude = ('chosenHelpers','suggestedHelpers', 'asker',)
class HelpersForm(ModelForm):
def suggesthelpers(self, question):
# populate chosenHelpers field according to data from QuestionForm
class Meta:
model = Question
exclude = ('suggestedHelpers', 'asker', 'questionSubj', 'Qtext',)
views.py
def askq_page(request):
question = Question(asker=Student.objects.get(user=request.user))
if request.method == 'POST':
form = QuestionForm(request.POST, instance=question)
if form.is_valid():
question = form.save()
# process data
question.save()
form2 = HelpersForm(instance=question)
form2.suggesthelpers(question)
variables = {'form':form2, 'qid':question.id}
return render_to_response('choosehelper.html', variables,context_instance=RequestContext(request))
else:
variables = RequestContext(request, {'form': QuestionForm()})
return render_to_response('askq.html', RequestContext(request, {'form': QuestionForm(instance=question)}))
def choosehelper_page(request, form='', qid=''):
question = Question.objects.get(id=qid)
if request.method == 'POST':
form2 = HelpersForm(request, instance=question)
# here lies the problem- the data gathered from form2 is not saved i.e. the multiple choices chosen by user are not returned
if form2.is_valid():
question = form2.save()
form2.save_m2m()
helpers = question.chosenHelpers.all()
for helper in helpers:
sentQ = ReceivedQ(question=question)
sentQ.student = helper
sentQ.save()
return render_to_response('questionsent.html', {'helpers': helpers, 'qid': question.id, 'qtext': q}, context_instance=RequestContext(request))
else:
form2 = HelpersForm(instance=question)
form2.suggesthelpers(question)
return render_to_response('choosehelper.html', RequestContext(request, {'form': form2, 'qid': qid}))
Related
I'm creating an update view using django-form for updating one of my objects that have the following fields:
class Object(models.Model):
name = models.CharField(max_length=40)
logo = models.ImageField(upload_to='object_logo/')
text_1 = models.TextField()
text_2 = models.TextField()
So, i have created the following form in forms.py:
class ObjectForm(forms.ModelForm):
class Meta:
model = Object
fields = [
'name',
'logo',
'text_1',
'text_2',
]
labels = {
'name': 'Name',
'logo': 'Logo',
'text_1': 'Text 1',
'text_2': 'Text 2',
}
and defined the following view called update_object:
def update_object(request, value):
object = get_object_or_404(Object, pk=value)
if request.method == "POST":
form = ObjectForm(request.POST, request.FILES)
if form.is_valid():
object.name = form.cleaned_data['name']
object.logo = form.cleaned_data['logo']
object.text_1 = form.cleaned_data['text_1']
object.text_2 = form.cleaned_data['text_2']
object.save()
return HttpResponseRedirect(reverse('myApp:detail_object', args=(value, )))
else:
form = ObjectForm(
initial={
'name': object.name,
'logo': object.logo,
'text_1': object.text_1,
'text_2': object.text_2,
}
)
context = {'object': object, 'form': form}
return render(request, 'myApp/update_object.html', context)
My problem is: even with an "initial" value stetted up for logo, i have to select an image every time i will update my object (otherwise i receive the update_object page with the message "This field is required").
Is there a way to make the current object.logo as the pre-defined value of the input in my ObjectForm in the update_object view?
I've already tried to set blank = True in the logo model field (which was a bad idea). I also thought in make an alternative conditional code for form.is_valid() but i dont know how to do it.
Update your forms.py like so:
class ObjectForm(forms.ModelForm):
class Meta:
model = Object
fields = '__all__'
...and views.py:
def update_object(request, value):
object = get_object_or_404(Object, pk=value)
if request.method == "POST":
form = ObjectForm(request.POST, request.FILES, instance=object)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('myApp:detail_object', args=(value, )))
else:
form = ObjectForm(instance=object)
context = {'object': object, 'form': form}
return render(request, 'myApp/update_object.html', context)
It can be done like this (more clean)
In case share your template code
I am trying to use a ModelForm to save a model.
forms.py
class PurchaseForm(forms.ModelForm):
weight = forms.IntegerField()
class Meta:
model = Purchase
fields = ["number", "pieces"]
views.py
if request.method == "POST":
form = PurchaseForm(request.POST)
if form.is_valid():
purchase = form.save(commit=False)
purchase.contract = Contract.objects.get(number=slug)
weight = form.cleaned_data.get('weight')
if check_weight(weight, purchase.contract):
weight_type = purchase.contract.supplier.market.weights
purchase.lbs, purchase.kgs = generate_weights(weight, weight_type)
purchase.save()
In the view above, I need to prevent the model from saving if the check_weight function returns False.
This function requires some data from the related object. I'm having some trouble figuring this out. What should I do?
If I'm understood your question correctly, this would work,
from django.http import HttpResponse
def my_form_view(request):
if request.method == "POST":
form = PurchaseForm(request.POST)
if form.is_valid():
purchase = form.save(commit=False)
purchase.contract = Contract.objects.get(number=slug)
weight = form.cleaned_data.get('weight')
if check_weight(weight, purchase.contract):
weight_type = purchase.contract.supplier.market.weights
purchase.lbs, purchase.kgs = generate_weights(weight, weight_type)
purchase.save()
return HttpResponse("save success")
return HttpResponse("'check_weight' returned False")
else: # if a GET (or any other method) we'll create a blank form
form = PurchaseForm()
return render(request, 'some_html_template.html', {'form': form})
I am following a tutorial and I want to create an edit button for my input but when I click the edit button it returns the form but empty:
forms.py
class RecordingForm(forms.ModelForm):
class Meta:
model = Recording
fields = ['component', 'group', 'failure', 'degree_of_failure']
views.py
def edit_recording(request,slug, recording_id):
recording = get_object_or_404(Recording, pk=recording_id)
if request.method == "POST":
form = RecordingForm(request.POST, instance=recording)
if form.is_valid():
recording = form.save(commit=False)
recording.save()
return redirect('data:detail')
else:
form = RecordingForm(instance=recording)
template = 'data/create_recording.html'
context = {'form': form}
return render(request, template, context)
the form is empty :(
The answer is:
Daniel Roseman is correct about GET
And I changed these two in order to fix the NoReverseMatch
plant = get_object_or_404(Plant, slug=slug)
return redirect('data:detail', slug=plant.slug)
I am using django ModelForms to generate my input forms.
I specify in my form model to only use a set of fields:
class <Model>Form(ModelForm):
class Meta:
model = <Model>
fields = ('date', 'comment_1')
My model is defined as:
class <Model>(models.Model):
fk_id_1 = models.ForeignKey(<ExternalModel1>, null=False, blank=False)
fk_id_2 = models.ForeignKey(<ExternalModel2>, null=False, blank=False)
date = models.DateField()
comment_1 = models.CharField(max_length=100)
comment_2 = models.CharField(max_length=100)
However, the ForeignKey boxes show.
How is it possible for me to hide them from the form? Also, how can I set the values for those dropboxes from within the view and not sure, say JQuery externally to do it? Ideally, after the ''is_valid()'' check, I would like to set the IDs of my Foreign Keys and then do save. Maybe I should look into solving this using another way?
This is the View:
def <Model>_add(request, trainee_id):
<Model>FormSet = modelformset_factory(<Model>)
if request.method == 'POST':
formset = <Model>FormSet(request.POST, request.FILES)
if formset.is_valid() and formset.has_changed():
formset.save()
# do something.
else:
formset = <Model>FormSet(queryset=<Model>.objects.none())
return render_to_response("<Model>_add.html", {
"formset": formset, "fk_id_1": fk_id_1,
}, context_instance=RequestContext(request))
I can solve this issue using JQuery but I would like a more elegant approach.
Note: I tried posting this earlier but I think it was not as clear as it is here: Presetting values on a foreign entity relationship in a ModelForm ... I didn't understand exactly what was said about QuerySet.
You need to be a bit more explicit in how you define the form:
class <Model>Form(ModelForm):
class Meta:
model = <Model>
fields = ['date', 'comment_1']
exclude = ['fk_id_1', 'fk_id_2']
Then in your view:
from django.shortcuts import render, redirect
def <Model>_add(request, trainee_id):
<Model>FormSet = modelformset_factory(<Model>)
if request.method == 'POST':
formset = <Model>FormSet(request.POST, request.FILES)
if formset.is_valid() and formset.has_changed():
forms = formset.save(commit=False)
for form in forms:
form.fk_id_1 = SomeOtherModel.objects.get(pk=1)
form.fk_id_2 = SomeOtherModel.objects.get(pk=2)
form.save()
# add your success redirect here, for example:
return redirect('/')
else:
formset = <Model>FormSet(queryset=<Model>.objects.none())
return render(request, "<Model>_add.html", {"formset": formset})
Every ModelForm also has a save() method. doc
or:
in views.py
form.instance.fk_id_1 = ...
form.instance.fk_id_2 = ...
form.save()
Let's say I want a system where 5 people want to register a service at once, all starting at the same date.
Explicit: 5 name fields (passed with extra=5) and just one date field.
I have tried with BaseFormSet and add_fields, but then I get five date fields too.
An example forms.py:
class NameForm(forms.Form):
name = forms.CharField()
class DateForm(form.Form):
date = forms.DateField()
An example views.py:
NameFormSet = formset_factory(NameForm, extra=5)
#The line under will not work, but illustrates what I want to do.
NameFormSet.append(DateForm)
if request.method = 'POST':
formset = NameFormSet(request.POST)
#Do validation etc..
else:
formset = NameFormSet()
return render_to_response('template.html', { 'formset' : formset })
Please help =)
Can you just include another DateForm like so?
NameFormSet = formset_factory(NameForm, extra=5)
if request.method = 'POST':
formset = NameFormSet(request.POST)
date_form = DateForm(request.POST)
if formset.is_valid() and date_Form.is_valid():
date = date_form.cleaned_data['date']
for form in formset:
name = form.cleaned_data['name']
# replace registration with registration model name
registration = Registration(name=name, date=date)
registration.save()
return
else:
formset = NameFormSet()
date_form = DateForm()
return render_to_response('template.html', { 'formset' : formset, 'date_form' : date_form })