'Auto-submit django form without causing infinite loop
I have an issue on my app that I can't work out the best way to approach the solution. I'm learning Django and have little to no experience in Javascript so any help will be appreciated. This may be a design flaw, so am open to other ideas.
Problem When a user reaches their dashboard page for the first time, they would need to select the currency rate on a form they want to view their data in e.g USD, AUD, GBP etc. Once they submit their currency, their dashboard will become active and viewable. I would like this selection to remain submitted on refresh/load or auto submit on refresh/load. Hence, that currency will remain, unless the user submits another currency.
See before/after images for context (ignore values as they are inaccurate):
Attempted solution
I have tried using Javascript to auto submit on load which causes and infinite loop.
I have read to use action
to redirect to another page but i need it to stay on the same page.
Unless this can be configured to only submit once after refresh/load I don't think this will work
<script type="text/javascript">
$(document).ready(function() {
window.document.forms['currency_choice'].submit();
});
</script>
<div class="container">
<div>
<span>
<h1>My Dashboard</h1>
</span>
<span>
<form method="post" name="currency_choice">
{% csrf_token %}
{{ form_c.as_p }}
<button class="btn btn-outline-success btn-sm">Submit</button>
</form>
</span>
</div>
</div>
FORM
class CurrencyForm(forms.ModelForm):
currency = forms.ChoiceField(choices=currencies, label='Choose Currency:',)
class Meta:
model = UserCurrency
fields = (
'currency',
)
MODEL
class UserCurrency(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
currency = models.CharField(max_length=10)
VIEW
class MyDashboardView(TemplateView):
template_name = 'coinprices/my-dashboard.html'
def get(self, request, **kwargs):
form_c = CurrencyForm(prefix='form_c')
return render(request, self.template_name, {
'form_c': form_c,
})
def post(self, request):
currency = None
form_c = CurrencyForm(request.POST, prefix='form_c')
if request.method == 'POST':
if form_c.is_valid():
cur = UserCurrency.objects.filter(user_id=request.user.id)
cur.delete()
post = form_c.save(commit=False)
post.user = request.user # Registers the entry by the current user
post.save()
currency = UserCurrency.objects.filter(user_id=request.user.id).values('currency')
currency = currency[0]['currency']
else:
form_c = CurrencyForm()
rates = {'USD': 1.0, 'AUD': 1.321, 'GBP': 0.764, 'CAD': 1.249}
deposit = 10000 / rates[currency]
context = {
'currency': currency,
'deposit': dep,
'form_c': form_c,
}
return render(request, 'coinprices/my-dashboard.html', context)
Solution 1:[1]
make a new temporary template with this only content, make a view to render and add view caller in your urls.py
{% csrf_token %}
{{ form_c.as_p }}
update your current template and insert ajax (i am assuming you have idea of jquery ajax)
<form method="post" name="currency_choice">
<div id="currency-choice">
{% csrf_token %}
{{ form_c.as_p }}
</div>
<button class="btn btn-outline-success btn-sm" id="btn-submit">Submit</button>
</form>
<script>
$( "#btn-submit" ).click(function() {
$.ajax({
url : '{% url 'url-of-new-template' %}',
success : function(response){
$('#currency-choice').html(response)
},
});
});
</script>
Solution 2:[2]
The general practice for this type of situation is to only use the post
method for validating and saving the submission, and then automatically reloading the page.
Javascript isn't required, so you can remove it from your template.
I have modified your UserCurrency
model to use a OneToOneField
relationship with the User
. This means that there can be no more than one of these records per User
. It also means that you can access the saved currency directly from the user instance.
I have also set a default currency of 'USD'. In the view, the try
/except
block creates a temporary UserCurrency
instance when one doesn't already exist.
MODEL
class UserCurrency(models.Model):
user = models.OneToOneField(
User,
primary_key=True,
related_name="user_currency",
on_delete=models.CASCADE)
currency = models.CharField(max_length=10, default="USD")
VIEW
from django.http import HttpResponseRedirect
class MyDashboardView(TemplateView):
template_name = 'coinprices/my-dashboard.html'
def get_context_data(self, **kwargs):
"""
Add the saved currency, deposit and form to the context.
This is then available for both get and post methods.
"""
try:
user_currency = self.request.user.user_currency
except UserCurrency.DoesNotExist:
user_currency = UserCurrency(user=self.request.user)
rates = {'USD': 1.0, 'AUD': 1.321, 'GBP': 0.764, 'CAD': 1.249}
context = super().get_context_data(**kwargs)
context['user_currency'] = user_currency
context['deposit'] = 10000 / rates[user_currency.currency]
context['form_c'] = CurrencyForm(instance=user_currency, prefix='form_c')
return context
def post(self, request, *args, **kwargs):
"""
If valid, save/update the model instance and reload the page.
Otherwise, display the form with errors.
"""
context = self.get_context_data(**kwargs)
form_c = CurrencyForm(request.POST, instance=context['user_currency'], prefix='form_c')
if form_c.is_valid():
form_c.save()
return HttpResponseRedirect(request.path_info)
context['form_c'] = form_c
return self.render_to_response(context)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 |