Validate a form against a formset in Django - django

I have a formset with three forms that the user inputs. Next to this, I have another form with one field that I want to validate against the fields inputted in the formset. Views.py looks something like:
FormSet = formset_factory(Form1, formset=BaseFormSet, extra=3)
if request.method == 'POST':
formset = FormSet(request.POST, request.FILES, prefix='first_form')
form2 = Form2(request.POST)
if formset.is_valid() and form2.is_valid():
# do something with the data
pass
else:
formset = FormSet(prefix='first_form')
target_shoe_form = TargetShoeForm()
return render(request, 'my_template.html', {
'formset': formset,
'form2': form2,
})
Is there a way to validate Form2 against the values in my Formset? As currently above they only validate internally, not against each other. Or, is it necessary to either nest the singleton form inside the formset, or nest the formset in the singleton form somehow? Thanks!
Update on validation:
Form1 has two fields, and rendered three times as part of a formset. Form2 has one field. When the user submits, I want to check that Form2's field is distinct from any of the values submitted in Form1

Related

How to access individual form objects from a modelformset_factory?

I've deployed a formset using modelformset_factory. However rather than saving the entire formset, I need to loop through the forms in the formset, perform some logic on them, and save each one individually. At the moment I'm having to use the ID from each form in the formset to get the object it represents. Is there a cleaner way of doing this?
def accounts_import(request,pk):
account = get_object_or_404(Account, pk=pk)
# Create transactions queryset for use in formset
transactions = Transaction.objects.filter(account=account.monzo_account, import_type=None).order_by('-id')
FormSet = modelformset_factory(Transaction, form=TransactionsImportForm, extra=0)
if request.method == 'POST':
formset = FormSet(request.POST)
if formset.is_valid():
for form in formset:
object = Transaction.objects.get(id=form.cleaned_data['id'])
# Do some stuff on the object
object.save()
Ok looks like form.cleaned_data['id'] returns the object and not the ID, so I got what I wanted.

How to save couple forms of same model class?

How to save couple forms of same model?
Here is example, I have one model with a lot of fields, so I decided to create a couple forms with different model fields of that model instead of creating one big form because site requires custom styling for each part of data, so it easer to group input fields with same style to one form.
def add_pet(request):
form1 = PetMainInfoForm()
form2 = PetFoodForm()
form3 = PetDescriptionForm()
if request.method == 'POST':
form1 = PetMainInfoForm(request.POST)
form2 = PetFoodForm(request.POST)
form3 = PetDescriptionForm(request.POST)
if form1.is_valid() and form2.is_valid() and form3.is_valid():
form1.save()
form2.save()
form3.save()
return HttpResponse('ok ok ok')
return render(request, 'add_pet.html', {'form1': form1,
'form2': form2,'form3': form3})
Did you try using prefix for each form? Prefix should be unique for each form. This way, when you post back you know which form has posted. It helped me post multiple forms from same template. I hope it helps you too.
form1 = PetMainInfoForm(request.POST, prefix='PetMainInfoForm')

Insert or update data with Django formsets

it's not clear to me how to manage formsets in Django. This is my views.py:
def newAd(request):
newAdFormSet = modelformset_factory(Ad)
if request.method == 'POST':
formset = newAdFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
return render_to_response('conf.html',
{'state':'Your ad has been successfull created.'},
context_instance = RequestContext(request),)
else:
formset = newAdFormSet()
return render_to_response('ad_form.html',
{'form':formset},
context_instance=RequestContext(request),)
It works but it always returns one prefilled form for each existing tuple plus, at the end, a blank form.
Now, i can't get how to say where it must return a blank form (to perform a new insert), and where it must instead return a single prefilled form (possibly passing the Ad's id) to perform an update.
modelformset_factory and formset helps to solve a lot, take your code for example
def newAd(request):
newAdFormSet = modelformset_factory(Ad, extra=1)
if request.method == 'POST':
formset = newAdFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
return render_to_response('conf.html',
{'state':'Your ad has been successfull created.'},
context_instance = RequestContext(request),)
else:
formset = newAdFormSet(queryset=Ad.objects.all())
return render_to_response('ad_form.html',
{'form':formset},
context_instance=RequestContext(request),)
Note the extra=1 in modelformset_factory line, it ensures there is only one extra blank form. And queryset=Ad.objects.all() in the second newAdFormSet inside else statement, it pre-fills forms for Ad objects from DB and correctly set PK in, mostly hidden, field for backend code to recognize submitted objects.
update
if you want to set Ad().codU to point to an User() instance, request.user for example, you could simply just set it by
instances = formset.save(commit=False)
for obj in instances:
obj.codU = request.user
obj.save()
I'm still not 100% clear what your question is, but it sounds like you don't want a formset at all. If you're only interested in adding or updating a single record at a time, you want a simple ModelForm, not a formset. So:
class AdForm(forms.ModelForm):
class Meta:
model = Ad
def add_update_ad(request, pk=None):
if pk is not None:
instance = Ad.objects.get(pk=pk)
else:
instance = Ad()
if request.POST:
form = AdForm(request.POST, instance=instance)
if form.is_valid():
new_instance = form.save()
return HttpResponseRedirect('my_confirmation_view')
else:
form = AdForm(instance=instance)
return render(request, 'ad_form.html', {'form': form})

Django - Adding initial value to a formset

I have a many-to-many relationship between two classes (Lesson and Student), with an intermediary class (Evaluation).
I am trying to set up a form which will allow me to add a lesson with students and the related evaluation data. I can get all of the fields I want to display correctly, however I also need to set an initial value behind the scenes (the current user), as it does not make sense to have it in the form.
I have tried following the docs but I think I have a syntax error in the way I am passing the data to the formset.
The error I receive is as follows:
__init__() got an unexpected keyword argument 'initial'
My actual view (with my attempt at adding the initial data removed) looks like this:
def addlesson(request):
LessonFormset = inlineformset_factory(Lesson, Evaluation, exclude=('user',), max_num=5)
if request.method == 'POST':
lesson = Lesson(user=request.user)
form = LessonForm(request.POST, instance=lesson, user = request.user)
formset = LessonFormset(request.POST, instance = lesson)
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
return HttpResponseRedirect("/")
else:
form = LessonForm(user = request.user)
formset = LessonFormset()
return render_to_response("addlesson.html", {
'form': form,
'formset' : formset,
}, context_instance=RequestContext(request))
Could anyone show me to correct syntax to use to set the current user in the formset?
This is what I had before but it was giving me the error at the start of my post:
initial={'user': request.user},
Any advice appreciated
Thanks
It's not clear to me why you are using a formset when it looks like you only want to add one row. A regular form would have been how I would do it if there was only one row. But, here's how I set the default value in a formset.
I exclude the field, just like you already have in your code. Then:
if form.is_valid() and formset.is_valid():
form.save()
models = formset.save(commit=False)
for i in models:
i.user = request.user
i.save()
return HttpResponseRedirect("/")
I tried Umang's answer and it didn't work good for when you want to change a value with a specific index. When you save the formset it will change the values that was changed.
But if you change models = formset.save(commit=False) to models = formset
and then you also need to change i.user = request.user to i.instance.user = request.user
if form.is_valid() and formset.is_valid():
form.save()
# changed to formset instead of formset.save(commit=False)
models = formset
for model in models:
# changed to i.instance.user instead of i.user, except renamed i to model.
model.instance.user = request.user
model.save()
# save the formset
formset.save()
return HttpResponseRedirect("/")
Now when you want to change an index it will include all the forms, not only the ones that was changed when you save.
Example:
views.py
if form.is_valid() and formset.is_valid():
form.save()
models = formset
index = 0
# This will only change the first form in the formset.
models[index].instance.user = request.user
models.save()
formset.save()
return HttpResponseRedirect("/")

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.