简体   繁体   中英

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 eg 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)

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>

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM