What is the correct way to process a Django form within a view if you don't know which form to expect when entering the view? I have a view which determines the type of form to render based on a session variable:
# views.py
def enter_location(request):
country = request.session['country']
if request.method == "POST":
if country == 'US':
form = USLocationForm(request.POST)
elif country == 'GB':
form = GBLocationForm(request.POST)
elif country == 'CA':
form = CALocationForm(request.POST)
else:
form = OtherLocationForm(request.POST)
if form.is_valid():
# do stuff...
pass
else:
if country == 'US':
form = USLocationForm(initial={'country': country})
elif country == 'GB':
form = GBLocationForm(initial={'country': country})
elif country == 'CA':
form = CALocationForm(initial={'country': country})
else:
form = OtherLocationForm(initial={'country': country})
context = {'form': form}
return render(request, template, context)
Clearly this is ugly code and it doesn't scale as I add more countries. I tried to keep my view short by determining the form type via a helper function:
# views.py
from location.forms import LocationForm
def enter_location(request):
country = request.session['country']
if request.method == "POST":
form = LocationForm.get_form(country)
submitted_form = form(request.POST)
if submitted_form.is_valid():
# do stuff...
pass
else:
form = LocationForm.get_form(country)
context = {'form': form}
return render(request, template, context)
# location/forms.py
class LocationForm(forms.Form):
country = forms.ChoiceField(choices = choices.COUNTRIES_AND_EMPTY,)
city = forms.CharField()
email = forms.EmailField()
#staticmethod
def get_form(country):
if country == 'US':
return form = USLocationForm(initial={'country': country})
elif country == 'GB':
return form = GBLocationForm(initial={'country': country})
elif country == 'CA':
return form = CALocationForm(initial={'country': country})
else:
return form = OtherLocationForm(initial={'country': country})
The problem is that when the view runs, I get "TypeError "'USLocationForm' object is not callable"" at the line:
submitted_form = form(request.POST)
This also happens when country is 'GB' or 'CA' and the appropriate form has been chosen. I ran the debugger and did "type(form)" using both views and the form attributes appear to be the same in either case. Is there something different happening when you first instantiate the form in the GET block versus instantiating it again during the POST and then populating it with the posted data? What's the Django way of handling this situation when one of many possible forms can be chosen?
Thanks.
There is no "right way" to handle this, but one thing you could do to consolidate is use a mapping.
Example:
form_mappings = {
'US': USLocationForm,
'GB': GBLocationForm,
#...
}
def enter_location(request):
country = request.session['country']
country_form = form_mappings.get(country, OtherLocationForm)
if request.method == "POST":
form = country_form(request.POST)
#...
else:
form = country_form(initial={'country': country})
This way, you can extend the code for further countries, and not have to change the code at all..
Related
[RESOLVED] I FOUND THE SOLUTION AND UPDATED WITH THE WORKING CODE BELOW
I'm trying to submit two forms with one button.
I check a few other posts in here, but I'm not sure if the code below is the correct.
def cars_detail(request, car_id):
car = Car.objects.get(id=car_id)
profile_form = ProfileForm()
booking_form = BookingForm()
return render(request, 'cars/detail.html', { 'car': car, 'booking_form': booking_form, 'profile_form': profile_form })
def addbooking(request, car_id):
if request.method == 'POST':
profile_form = ProfileForm(request.POST)
booking_form = BookingForm(request.POST)
print(request.POST)
if profile_form.is_valid() or booking_form.is_valid():
# do stuff here
# form = ProfileForm(request.POST)
new_profile = profile_form.save(commit=False)
new_profile.car_id = car_id
new_profile.user_id = request.user.id
new_profile.save()
# do stuff here
# form = BookingForm(request.POST)
new_booking = booking_form.save(commit=False)
new_booking.car_id = car_id
new_booking.user_id = request.user.id
new_booking.save()
return redirect('detail', car_id=car_id)
else:
profile_form = ProfileForm(prefix="profile_form")
booking_form = BookingForm(prefix="booking_form")
You have no code in the block after the “if”.
If there’s nothing to do in that instance negate the condition and remove the “else”
But do you want to reset both forms if only one of them is invalid? You might want this instead
if !profile_form.is_valid():
profile_form = Profile_form(prefix="profile_form")
if !booking_form.is_valid():
booking_form = Booking_form(prefix="booking_form")
I am using a function to record the time between which the page is loaded and the user submits the data. In that form, I have created a hidden field so that the admin can see it. However, I am unable to implement my idea since I don't understand how I can submit the time to the form from views.py. If someone can suggest an easier and simpler alternative methods to achieve what I am trying to do, that would be very helpful to me as well. My code is as follows.
models.py
class Responses(models.Model):
question1 = models.TextField()
question2 = models.TextField()
question3 = models.TextField()
timespent1 = models.TextField()
forms.py
class Question1Form(forms.ModelForm):
question1 = forms.CharField()
timespent1 = forms.TimeField(widget=forms.HiddenInput())
class Meta:
model = Responses
fields = ('question1','timespent1')
views.py
def qone(request):
if request.method =="GET":
starttime = my_timer()
elif request.method == "POST":
form = Question1Form(request.POST)
if form.is_valid():
endtime = my_timer()
timespentstr = '{0} seconds'.format(endtime-starttime)
#Do something to set timespent1 field = timespentstr here
form = form.save()
else:
form = Question1Form
return render(request,'question1.html',{'form':form})
You'll need to store the start time "somewhere else" - perhaps store it in the session in the GET, and then pull it back out from there in the POST:
def qone(request):
if request.method == 'GET':
request.session['start_time'] = mytimer()
form = Question1Form()
elif request.method == 'POST':
form = Question1Form(request.POST, request.FILES)
if form.is_valid():
end_time = mytimer()
start_time = request.session.get('start_time')
time_spent = end_time - start_time
....
form.save()
return render(request, 'question1.html', {'form': form})
You could also look at passing in the start time to the form directly, and then having all of the logic in there, but that might complicate things. Perhaps passing the "time taken" to the form constructor might be the best way.
class Question1Form(forms.Form):
def __init__(self, *args, **kwargs):
self.time_taken = self.kwargs.pop('time_taken', None)
super().__init__(*args, **kwargs)
def qone(request):
if request.method == 'GET':
form = Question1Form()
request.session['start_time'] = my_timer()
elif request.method == 'POST':
form = Question1Form(
request.POST, request.FILES,
time_taken=my_timer() - request.session.pop('start_time')
)
if form.is_valid():
form.save()
# ...
I'm trying to perform multiple operation for single web page. I'm struggling to write multiple function view for single web page. How to do that. Help me in that.
def home_view(request):
if 'username' in request.session:
if request.method == 'GET':
form = ProfileForm(request.GET)
if form.is_valid:
profile_info = Profile.objects.filter(username=username).values()
for i in profile_info:
profiledict = i
return render(request, 'home/index.html', {'profile_first_name': profiledict['first_name'],
'profile_last_name': profiledict["last_name"],
'profile_phone_number': profiledict['phone_number'],
'profile_email': profiledict['email'],
'profile_address': profiledict['address'],
'profile_image': profiledict['image']})
elif request.method == 'POST':
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name')
phone_number = request.POST.get('phone_number')
email = request.POST.get('email')
address = request.POST.get('address')
image = request.FILES['images']
file_storage = FileSystemStorage()
obj = Profile.objects.all().filter(username=username)
if len(obj) > 0:
obj.update(first_name=first_name, last_name=last_name, phone_number=phone_number,
email=email, address=address, image=file_storage.save(image.name, image))
return redirect('/home/')
return redirect('/home/')
else:
return redirect('/login/')
def home2_view(request):
if 'username' in request.session:
business_objs = AddBusiness.objects.all().values()
return render(request, 'home/index.html', {'business_objs': business_objs})
else:
return redirect('/login/')
I tried like this, it performing only first operation. Here i'm trying to perform 3 operation, GET(extract data from database and displaying in HTML form), POST(updating the form data), and Displaying all database rows and columns in HTML page.
model:
class locations(models.Model):#table
Name = models.CharField(max_length=50,default='Joes quick stop', unique=True)
shop_code = models.CharField(max_length=5,default='AB005',unique=True)
manager = models.ManyToManyField(users)
ast_manager = models.ManyToManyField(users, blank=True, related_name='ast_mng')
sales_manager = models.ManyToManyField(users, blank=True, related_name='sales_mng')
forms:
class locations(models.Model):
Name = models.CharField(max_length=50,default='Joes quick stop', unique=True)
shop_code = models.CharField(max_length=5,default='AB005',unique=True)
manager = models.ManyToManyField(users)
ast_manager = models.ManyToManyField(users, blank=True, related_name='ast_mng')
sales_manager = models.ManyToManyField(users, blank=True, related_name='sales_mng')
class locationsForm(ModelForm):
class Meta:
model = locations
Views
def locations(request):
locations = locations.objects.values().filter(id=request.session['location'])
data=locations[0]
if request.method == 'GET':
if request.session['Manager']== True:
form = locationsForm(initial=data)
context = {'locations': locations, 'form': form}
return render(request, 'locations/locations.html', context)
elif request.method == 'POST':
form=locationsForm(request.POST, instance=data)
if form.is_valid():
cd=form.cleaned_data
form.save()
form = locationsForm()
locations= locations.objects.values().filter(id=request.session['depot'])
context = {'locations': locations}
return render(request, 'locations/locations.html', context)
else:
context = {'locations': locations, 'form': form}
return render(request, 'locations/locations.html', context)
I am trying to display a form that is populated with the relevant data but the user can change and then save/update the form. The above code does a good job of displaying the form with the relevant data but when the user tries to submit it the system tries to save a new record instead of updating the old and fails. I never get past if form.is_valid():
Your problem is that you are converting your locations objects into a list of dictionaries. And then passing a dictionary into the form as instance.
This is happening because you are calling the .values() method on the queryset. That method returns a special ValuesQuerySet which basically looks like a list of dictionaries. Not a list of locations objects.
The instance parameter on the form needs to be an object, not a dictionary. So just simply remove the .values() calls, and it should work. Like this:
def locations(request):
locations = locations.objects.filter(id=request.session['location'])
first_location=locations[0]
if request.method == 'GET':
if request.session['Manager']== True:
form = locationsForm(instance=first_location)
context = {'locations': locations, 'form': form}
return render(request, 'locations/locations.html', context)
elif request.method == 'POST':
form=locationsForm(request.POST, instance=first_location)
if form.is_valid():
cd=form.cleaned_data
form.save()
form = locationsForm()
locations= locations.objects.values().filter(id=request.session['depot'])
context = {'locations': locations}
return render(request, 'locations/locations.html', context)
else:
context = {'locations': locations, 'form': form}
return render(request, 'locations/locations.html', context)
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 })