Preserve and display Django form inputs on POST submission - html

--PROBLEM--
I have created a basic calculator using Django.
The app takes some basic form inputs before executing some calculations and returning the outcomes to the user, however I want the user's inputs to remain visible in the form on submission.
At present the followin experience occurs.
User enters values into form and submits using 'Calculate' button.
Results are returned as expected but form and values are no longer present.
User can press 'Calculate' button to return to start of process and blank form.
--DESIRED OUTCOME--
--CODE--
forms.py
from django import forms
class InvestmentForm(forms.Form):
starting_amount = forms.FloatField()
number_of_years = forms.FloatField()
return_rate = forms.FloatField()
annual_additional_contribution = forms.FloatField()
views.py
from django.shortcuts import render
from django.views import View
from .forms import InvestmentForm
class Index(View):
def get(self, request):
form = InvestmentForm()
return render(request, 'loancalculator/index.html', {'form': form})
def post(self, request):
form = InvestmentForm(request.POST)
if form.is_valid():
total_result = form.cleaned_data['starting_amount']
total_interest = 0
yearly_results = {}
for i in range(1, int(form.cleaned_data['number_of_years'] +1)):
yearly_results[i] = {}
# CALCULATE THE INTEREST
interest = total_result * (form.cleaned_data['return_rate'] / 100)
total_result += interest
total_interest += interest
# ADDITIONAL CONTRIBUTION
total_result += form.cleaned_data['annual_additional_contribution']
# SET YEARLY RESULTS
yearly_results[i]['interest'] = round(total_interest, 2)
yearly_results[i]['total'] = round(total_result,2)
# CREATE CONTEXT
context = {
'total_result': round(total_result,2),
'yearly_results': yearly_results,
'number_of_years': int(form.cleaned_data['number_of_years'])
}
# RENDER THE TEMPLATE
return render(request, 'loancalculator/index.html', context)
else:
form = InvestmentForm()
return render(request, 'loancalculator/index.html', {'form':form})
index.html
{% extends 'loancalculator/base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row justify-content-center">
<div class="col-4 border bg-light">
<div class="row t-5">
<div class="col-12 col-md-6 mx-md-auto">
<h1> Investment Calculator</h1>
<h5> Enter the details below to see your estimate</h5>
<form action="" method="POST">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" type="submit">Calculate</button>
</form>
<br>
</div>
</div>
</div>
<div class="col-4 border">
<div class="row t-5">
<div class="col-12 col-md-6 mx-md-auto">
<h1>Investment Results</h1>
<h3> After {{ number_of_years }} years, your investment is worth ${{ total_result }}</h3>
<div class="mt-5">
<table class="table">
<thead>
<tr>
<th scope="col">Year</th>
<th scope="col">Interest</th>
<th scope="col">Total</th>
</tr>
</thead>
<tbody>
{% for key, value in yearly_results.items %}
<tr>
<th scope="row">{{ key }}</th>
<td>{{ value.interest }}</td>
<td>{{ value.total }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}

Two ways you can go about doing what you want as I see it:
Method 1
Use sessions to hold the data and then refeed it to the form using initial form values. This is probably the easiest since it most closely matches what you already have.
class Index(View):
def get(self, request):
# Fill in the initial values on the unbound form:
initial = {
'starting_amount': request.session.get('starting_amount'),
'number_of_years': request.session.get('number_of_years'),
'return_rate': request.session.get('return_rate'),
'annual_additional_contribution': request.session.get('annual_additional_contribution'),
}
form = InvestmentForm(initial=initial)
return render(request, 'home/index.html', {'form': form})
def post(self, request):
form = InvestmentForm(request.POST)
if form.is_valid():
# After getting the cleaned_data, set a session variable to hold it
total_result = form.cleaned_data['starting_amount']
request.session['starting_amount'] = total_result
number_of_years = int(form.cleaned_data['number_of_years'])
request.session['number_of_years'] = number_of_years
return_rate = form.cleaned_data['return_rate']
request.session['return_rate'] = return_rate
annual_additional_contribution = form.cleaned_data['annual_additional_contribution']
request.session['annual_additional_contribution'] = annual_additional_contribution
total_interest = 0
yearly_results = {}
for i in range(1, number_of_years +1):
yearly_results[i] = {}
# CALCULATE THE INTEREST
interest = total_result * (return_rate / 100)
total_result += interest
total_interest += interest
# ADDITIONAL CONTRIBUTION
total_result += annual_additional_contribution
# SET YEARLY RESULTS
yearly_results[i]['interest'] = round(total_interest, 2)
yearly_results[i]['total'] = round(total_result,2)
# CREATE CONTEXT
context = {
# CHANGED: pass the 'form' to the context!
'form': form,
'total_result': round(total_result,2),
'yearly_results': yearly_results,
'number_of_years': number_of_years
}
# RENDER THE TEMPLATE
return render(request, 'home/index.html', context)
else:
form = InvestmentForm()
return render(request, 'home/index.html', {'form':form})
Method 2
From what you show here, anyway, you could just use JavaScript to do the calculations by grabbing the values directly from the DOM after they are entered, doing the calculations on the front end with JavaScript, and then fill in the values by targeting the locations where you want the results displayed in the DOM. No need for forms, no reloading of pages.

When you create the context, the value 'form' doesn't get set (views.py, under get())

Related

django response method POST don't render to the html template

I have two request function in views one is with .get method and the other one with .post. Both of the function works properly because in the the terminal the code is 200.
[01/Apr/2021 08:04:39] "GET /search/search HTTP/1.1" 200 4164
[01/Apr/2021 08:04:57] "POST /search/search HTTP/1.1" 200 4164
The problem comes when i try to render the function with .post method to the html template nothing appear on the html page.
def wind_search(request):
if request.method == 'post':
city = request.post['city']
weather_city_url = urllib.request.urlopen('api.openweathermap.org/data/2.5/weather?q=' + city + '&appid=1a7c2a40a0734d1dc18141fc6b6241bb').read()
list_of_data = json.loads(waether_city_url)
# main wind information
wind_speed = list_of_data['wind']['speed']
# wind_gust = wea['current']['wind_gust']
wind_deg = list_of_data['wind']['deg']
# wind conversiont m/s to knots
def wind_converter(w):
knots = 2
kt = (float(w)) * knots
return kt
wind_response = wind_converter(wind_speed)
#convert wind degree in cardinal direction.
def degrees_to_cardinal(d):
dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
ix = round(d / (360. / len(dirs)))
return dirs[ix % len(dirs)]
direction = degrees_to_cardinal(wind_deg)
wind_data = {
"wind_response":wind_response,
"wind_direction":direction,
}
else:
wind_data={}
context = {"wind_data":wind_data}
return render(request, 'API/wind_search.html',context)
This is the html template:
{% extends "API/base.html" %}
{% block content %}
<!--Jumbotron -->
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4">Wind search</h1>
<p class="lead">Write the city and check th wind condition. </p>
<!-- form input search tool -->
<nav class="navbar navbar-expand-lg navbar-dark">
<form method="post" class="col-md"">
{% csrf_token %}
<div class=" input-group">
<input type="text" class="form-control" name="city" placeholder="Choose Your City ...">
<div class="input-group-btn">
<button type="submit" class="btn btn-primary">Search</button>
</div>
</div>
</form>
</nav>
<div class="row">
{% if wind_response and wind_direction %}
<h4><span>Wind Speed :</span> {{wind_data.wind_speed}}</h4>
<h4><span>Wind_Direction :</span> {{wind_data.wind_direction}}</h4>
</div>
{% endif %}
</div>
{% endblock content %}
I think the problem is with the html because basically the view don't show any error message, so i tried to change couple of time the html code but without success. Any help/ explenation is nice.
Thank you very much.
Please set the render and redirect template,
def wind_search(request):
if request.method == 'POST':
#this block of code manages if there is a POST request...
city = request.POST.get('city')
weather_city_url = urllib.request.urlopen('api.openweathermap.org/data/2.5/weather?q=' + city + '&appid=1a7c2a40a0734d1dc18141fc6b6241bb').read()
list_of_data = json.loads(waether_city_url)
# main wind information
wind_speed = list_of_data['wind']['speed']
# wind_gust = wea['current']['wind_gust']
wind_deg = list_of_data['wind']['deg']
# wind conversiont m/s to knots
def wind_converter(w):
knots = 2
kt = (float(w)) * knots
return kt
wind_response = wind_converter(wind_speed)
#convert wind degree in cardinal direction.
def degrees_to_cardinal(d):
dirs = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
ix = round(d / (360. / len(dirs)))
return dirs[ix % len(dirs)]
direction = degrees_to_cardinal(wind_deg)
wind_data = {
"wind_response":wind_response,
"wind_direction":direction,
}
# the page that you want to load after submitting your POST request <-----------------------------------------------------
return redirect( 'redirect to a view ' )
#the below block of code will cater for the GET method request
else:
wind_data={
'foo' : 'foo'
}
#the page you want to render on a Get Request <-----------------------------------------------------
return render(request,'render the required html for GET request' , wind_data)

How do I use a for loop to reuse a django form in a template

After struggling with this issue for a while, I am hoping someone here can point me in a more productive direction.
I am trying to take an indeterminate number of variables in a database (obtained through a different template) and render them on a webpage, each variable with a simple data entry form, to be saved back to the database. Basically, it's a tracker for analysis. Say I want to track my daily sleep, running time, and calorie intake (the variables). I have those saved in a database as variables and want to call upon those variables and show them on a webpage with a daily entry form. I am using a "for" loop right now and it renders the way I want it to, with the variable name and the form, but it is only saving the last item in the variable list. How do I amend the code below such that when I hit the save button for each form rendeded, it saves the information for that variable (not just the last one rendered).
Below is the code. Any and all help would be infinitely appreciated.
Models...
class Variable(models.Model):
date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
ENTRY_TYPE_CHOICES = [
('numeric', 'enter a daily number'),
('scale', 'rate daily on a scale of 1-10'),
('binary', "enter daily, 'yes' or 'no' "),
]
variable = models.CharField(max_length=50, default='')
entry_type = models.CharField(max_length=50, choices=ENTRY_TYPE_CHOICES, default="numeric")
def __str__(self):
return self.variable
class DailyEntry(models.Model):
date = models.DateField(default=datetime.date.today)
# date_added = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(get_user_model(), default='', on_delete=models.CASCADE) # id the active user
variable_name = models.CharField(max_length=50, default='')
variable_id = models.SmallIntegerField(default=0000)
entry = models.FloatField(default=9999)
class Meta:
verbose_name_plural = 'Daily Entries'
def __str__(self):
return self.variable
Form...
class VariablesForm(forms.ModelForm):
class Meta:
model = Variable
fields = ['variable', 'entry_type' ]
labels = {'variable':'Dependent variable to track', 'entry_type': 'Type of measure'}
class DailyEntryForm(forms.ModelForm):
class Meta:
model = DailyEntry
fields = ['variable_name', 'variable_id', 'entry', 'date']
labels = {'entry': 'Daily entry', 'date': 'Date'}
widgets = {'variable_name': forms.HiddenInput(), 'variable_id': forms.HiddenInput()}
Views...
def daily_entry(request):
''' page to make daily entries '''
vars = Variable.objects.filter(id__gt = 0 )
if request.method != 'POST':
# No data submitted. GET submitted. Create a blank form
form = DailyEntryForm()
else:
#POST data submitted. Process data
form = DailyEntryForm(data=request.POST)
if form.is_valid():
data = form.save(commit=False)
data.created_by = request.user
data.save()
return HttpResponseRedirect(reverse('entry_new'))
context = {'form': form, 'vars': vars}
return render(request, 'entry_new.html', context)
and HTML...
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% for var in vars %}
<div>
<ul>
<h3>{{ var.variable }}</h3>
<form class="" action="" method="post">
{% csrf_token %}
{{ form|crispy }}
<input type="hidden" name="variable_id" value="{{ var.id }}" >
<input type="hidden" name="variable_name" value="{{ var.variable }}">
<input type="submit" name="" value="Save" />
</ul>
</div>
{% endfor %}
{% endblock content %}
Any help, well, helps...
Thanks!

how to pass value from html to view in django?

I have made this HTML code:
<h3>your major is {{user.userprofile.major}}</h3>
This will correctly show the major on the webpage, but I want to use this string to get something from another table in view.
How would I pass this string to view?
edit:
Here is my view.py
def dashboardView(request):
obj = BooksFile.objects.all()
query = BooksFile.objects.filter(book_major='cs)
return render(request, 'dashboard.html', {'books': obj, 'major': query})
def registerView(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
profile_form = UserProfileForm(request.POST)
if form.is_valid() and profile_form.is_valid():
user = form.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
return redirect('login_url')
else:
form = UserCreationForm()
profile_form = UserProfileForm()
context = {'form': form, 'profile_form': profile_form}
return render(request, 'registration/register.html', context)
here is my template:
{% extends 'index.html' %}
{% block content %}
<h1>Welcome, {{user.username}}</h1>
<h2>Your major is {{user.userprofile.major}}</h2>
{% for book in books %}
<h3>Your book name is {{book.book_name}}</h3>
{% endfor %}
{% endblock %}
I am trying to show the book names from the booksfile table by corresponding major that user has. Right its showing the books that has "cs" attribute because I manually put "cs" in the get function in view. I am trying to send the major string from template to view, so that I can put what ever the user's major is in the get function. Or is there any other way to do it.
You need to use a form in your template and submit it to call your view. i.e.
<form action="your_view_url" method="POST">
<input type="text" name="major" value="{{user.userprofile.major}}"/>
<input type="submit"/>
</form>
an then in your view you access that with:
if request.POST:
major = request.POST.get('major')
As per documentation: https://docs.djangoproject.com/en/2.2/topics/forms/
First of all you have to get the value of model with help of queryset, and put it in the dictionary and then pass it with the template.
In views:
def get(self, request):
queryset = Model_name.objects.all()
ctx = {
'queryset': queryset,
}
return render(request, 'page_name(or template_name).html', ctx)
in template:
<form action="{%url'(your_view_name without brackets)'%}" method="POST">
{% for data in queryset%}
<span class="username">{{data.name(field of your model)}} .
</span>
<span class="email">{{data.email(field of your model)}} .
</span>
{% endfor%}
</form>

How to separate input form for each entry in a single template

I have a list of entries which are displayed in a webpage. Each entry has the same input form. But my problem arises when data is put into the input form, it appears on every single one of them. How do I change this.
Each input form is treated as a single cloned entity distributed over many entries.
views.py
def topic(request, topic_id, type): #
topic = Topic.objects.get(id = topic_id, type = 't_topic')
entries = topic.entry_set.order_by('-date_added')
images = Image.objects.filter(imgtopic__in = entries)
ArticleFormSet = formset_factory(CheckAnswer, extra = 2)
if request.method == 'POST':
formset = ArticleFormSet(request.POST)
if form.is_valid():
return HttpResponseRedirect(reverse('learning_logs:index'))
else:
formset = ArticleFormSet()
context = {'topic': topic, 'entries': entries, 'images': images, 'formset': formset}
return render(request, 'learning_logs/topic.html', context)
topic.html template
{%load staticfiles %}
{% block content %}
{% for entry in entries(n) %}
<div class = 'secondary-container'>
<li>
<div class = 'date'>
<p >{{ entry.date_added|date:'M d, Y H:i'}}</p>
</div>
{%include 'learning_logs/view_files.html'%}
<div class = 'video-container'>
{%include 'learning_logs/video.html' %}
</div>
<div class = 'entry-text'>
<p>{{ entry.text|linebreaks }}</p>
<p>$ {{entry.price_set.get.ptext}}</p>
</div>
</li>
<li>
<p> Enter Answer: </p>
<form action = "{%url 'learning_logs:topic' topic.id topic.type%}" method = 'post'>
{{formset.management_form}}
<table>
{% for form in formset%}
{{form.as_p}}
{% csrf_token %}
<button name = "submit">Submit answer</button>
{% endfor %}
</table>
</form>
</div>
forms.py
class CheckAnswer(forms.Form):
your_answer = forms.CharField(label = "Enter Your Key", max_length = 100)
def clean(self):
cleaned_data=super(CheckAnswer, self).clean()
your_answer = cleaned_data.get("your_answer")
try:
p = Keys.objects.get(key = your_answer)
except Keys.DoesNotExist:
raise forms.ValidationError("Incorrect code.")
My new problem is that I can't separate the forms into the different lists. Since extra = 2, I get 2 instances of forms on the same entry list.
I tried using the {%for form in formset%} command in the topic.html template to give each entry a form, but I get an of ['ManagementForm data is missing or has been tampered with'] when I click submit.
Image of the problem

form field not showing (django 1.7)

The form field (text area) is not showing in my django template. I can figure out where the problem is.
Views.py
class Profile(View):
"""User Profile page reachable from /user/<username> URL"""
def get(self, request, username):
params = dict()
user = User.objects.get(username=username)
tweets = Tweet.objects.filter(user=user)
params["tweets"] = tweets
params["user"] = user
return render(request, 'profile.html', params)
class PostTweet(View):
"""Tweet Post form available on page /user/<username> URL"""
def post(self, request, username):
if request.method == 'GET':
form = TweettForm()
else:
form = TweetForm(self.request.POST)
if form.is_valid():
user = User.objects.get(username=username)
tweet = Tweet(text=form.cleaned_data['text'], user=user, country=form.cleaned_data['country'])
tweet.save()
words = form.cleaned_data['text'].split(" ")
for word in words:
if word[0] == "#":
hashtag, created = HashTag.objects.get_or_create(name=word[1:])
hashtag.tweet.add(tweet)
return HttpResponseRedirect('/user/'+username)
return render(request, 'profile.html', {'form': form})
forms.py
from django import forms
class TweetForm(forms.Form):
text = forms.CharField(widget=forms.Textarea(attrs={'rows': 1, 'cols':85}), max_length=160)
country = forms.CharField(widget=forms.HiddenInput())
profile.html
{% extends "base.html" %}
{% block content %}
<div class="row clearfix">
<div class="col-md-12 column">
<form method="post" action="post/">{% csrf_token %}
<div class="col-md-8 col-md-offset-2 fieldWrapper">
{{ form.text.errors }}
{{ form.text }}
</div>
{{ form.country.as_hidden }}
<div>
<input type="submit" value="post">
</div>
</form>
</div>
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
from tweets.views import Index, Profile, PostTweet
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', Index.as_view()),
url(r'^user/(\w+)/$', Profile.as_view()),
url(r'^admin/', include(admin.site.urls)),
url(r'^user/(\w+)/post/$', PostTweet.as_view())
)
Only the submit (post) button shows on the on when rendered in the browser. The text are is not there
You get nothing since you are not passing the form to the template. Write get function in PostTweet view and include form = TweetForm() in it as a param passed to the template.