skip to Main Content

I have a dynamically generatyed HTML file that displays quizzes for a class that was gathered from an API request. Within this page, I have a scan button that will scan the selected quiz and kick off a python script on the backend. When the scan button is clicked, I need that specific class data sent back to my views file so I can kick off an API request on the back end for the scan.

The issue I am having is that when the scan button is pressed, only the last element in the list (of the dynamically generated html page) is sent back to my views.py file.

How do I set up my POST reuquest on button click to handle the dynamic data so the data that gets sent back is correct as.

Images below will show what is happening.

Image of html page
enter image description here

Dynamic HTML page code

<div class="container">
        <form method="post">
            {% csrf_token %}
        {% for quiz in assignment_data %}
            <div class="assignment hide-content">
                <div class="row assignment-header">
                    <div class="col">{{quiz.title}}</div>
                    <div class="col">published: {{quiz.published}}</div>
                    <div class="col">Questions: {{quiz.question_count}}</div>
                    <div class="col-sm toggle-col">
                        <i class="toggle-drop-icon drop-icon-down fa-sharp fa-solid fa-caret-down"></i>
                        <i class="toggle-drop-icon drop-icon-up fa-sharp fa-solid fa-caret-up"></i>
                    </div>
                </div>
                <div class="row assignment-content">
                    <div class="col">
                        <button class="button" type="submit" id="scan-quiz" name="run_webscrape" onclick="this.classList.toggle('button--loading')">
                            <span class="button-text">Scan</span>
                            <input type="hidden" name="quiz_id" value="{{quiz.id}}">
                            <input type="hidden" name="quiz_name" value="{{quiz.title}}">
                        </button>
                    </div>
                    <div class="col">
                        Cheatability
                        <br>
                        <span class="grey">0/{{quiz.question_count}}</span>
                    </div>
                    <div class="col">
                        Last Scan
                        <br>
                        <span class="grey">Not Scanned</span>
                    </div>
                    <div class="col">
                        <a href="#" disabled>View Results</a>
                    </div>
                </div>
            </div>
        {% endfor %}
        </form>
    </div>

Post request in views.py

    def post(self, request, class_id):
        if request.method == 'POST' and 'run_webscrape' in request.POST:
            # get user profile form
            # base information for authentication to canvas
            # grab canvas token out of database for canvas auth
            user_token = Profile.objects.filter(user=request.user).values_list('canvas_api_token', flat=True)
            api_token = str(user_token[0])

            user_quiz_data = Profile.objects.filter(user=request.user).values_list('teacher_data', flat=True)
            teacher_data_string = user_quiz_data[0]

            if not teacher_data_string:
                print("No data yet for this teacher")
                teacher_data = list(teacher_data_string)
            else:
                teacher_data_string_edit = [teacher_data_string]
                teacher_data_list = [json.loads(idx.replace("'", '"'), strict=False) for idx in teacher_data_string_edit]
                teacher_data = teacher_data_list[0]



            # set the quiz id of the selected quiz to scan
# THIS DATA RIGHT HERE NEEDS TO BE DYNAMIC
********************************************************************************
            quiz_id = int(request.POST['quiz_id'])
            quiz_name = request.POST['quiz_name']
#*******************************************************************************

            # Create data block entry to save quiz information into database

            # vars for Canvas authentication
            canvas_api_url = "https://canvas.instructure.com/api/v1"
            header = {
                "Authorization": f"Bearer {api_token}"
            }
            webscrape(canvas_api_url, header, quiz_id, class_id, quiz_name, teacher_data, request)

            return redirect(to="home-view")

Terminal data that gets printed out to double check I am getting the correct data
**
5640376
13368555
Test Science Quiz 2 Multiple Variations
**

For reference, I clicked the scan button for Science Test Quiz 1 but got data for Test Science Quiz 2 Multiple Variations. This also happens for every class page. It only ever sends back the last interation within the for loop on the HTML page (for loop in shown within HTML page code)

2

Answers


  1. submit button always send all data in form, use ‘id"{{quiz.id}}"’ and give dynamic id and dynamic name to your submit button and then in BackEnd check wich submit button clicked then do some action

    or
    you should create button from anything other than ‘type="submit" ‘ and use JS to send data to API or backend

    for dynamic submit button
    this code is simple

    <form action="test.php" method="post">
        <input type="hidden" id="data1" name="data1" value="data1">
        <input type="hidden" id="data2" name="data2" value="data2">
        <button class="button" type="submit" id="submit1" name="submit1">send</button>
            <button class="button" type="submit" id="submit2" name="submit2">send</button>
    </form>
    
    Login or Signup to reply.
  2. I suggest using Javascript & Ajax to submit the values instead of an HTML form.
    This also comes with the added benefit of not having to reload the page on every button click!

    This is generally what I would do:

    • Overview:
      • The view will return a Json dictonary with the keys:
        • status = boolean, did it work?
        • msg = string, any potential message
      • Frontend script is mostly JQuery
        • flow: button click, pack data, post data, handle server reply
      • Updated the Button’s HTML

    View

    def post(self, request, class_id):
        if request.method == 'POST':
    
            return_data = {'status': False}  # Assume it'll fail
    
            # POST.get('key') will get the value or `None` (by default)
            # POST['key'] will crash if key doesn't exist
            if request.POST.get('quiz_name') and request.POST.get('quiz_name'):
    
                # Do things
    
                #########################################################################
                quiz_id = int(request.POST.get('quiz_id'))
                quiz_name = request.POST.get('quiz_name')
    
                # Create data block entry to save quiz information into database
    
                # vars for Canvas authentication
                canvas_api_url = "https://canvas.instructure.com/api/v1"
                header = {
                    "Authorization": f"Bearer {api_token}"
                }
                webscrape(canvas_api_url, header, quiz_id, class_id, quiz_name, teacher_data, request)
    
                # Return Success!
                return_data['status'] = True
                return_data['msg'] = f'Webscrap for {quiz_name} started!'
            else:
                # Controlled Error. Return message
                return_data['msg'] = 'Missing quiz_id and/or quiz_name'
    
            from django.http import JsonResponse
            return JsonResponse(return_data)
    

    Template

    <script>
    $(document).ready(function(){
      $('.scan_quiz').click(function(){
    
        $(this).toggle('button--loading');
    
        var submitData = {
          // pack attibutes
          'quiz_id': $(this).attr('quiz_id'),
          'quiz_name': $(this).attr('quiz_title'),
    
          // Required for any @login_required views
          'csrfmiddlewaretoken': $('[name='csrfmiddlewaretoken']').val(),
        };
    
        // Send the Request
        $.ajax({
          method: 'post',
          url: 'my/view/url',  // <---- needs correct URL
          data: submitData,
          success: function(data){
            // handle response
            // -Colored Popup Banner?
            if (data['status']){
    
              // Success!
              console.log(data['msg']);
    
              $(this)
                .removeClass('button--loading')
                .addClass('button--success');
    
            }else{
              // Django controlled error
              console.log('Controlled Error');
              console.log(data['msg']);
            }
          },
          error: function(xhr){
            // My Django `xhr.status` key:
            // 0    = Server didn't Reply (server down)
            // 400  = Bad Request         (Syntax Error?)
            // 403  = Forbidden           (Login Token Expired)
            // 403  = Not Found           (Invalid Url)
            // 500  = Server Error        (Django Crash)
    
            console.log('Uncontrolled Error');
          }
        });
      });
    });
    </script>
    
    <div class="container">
      {% csrf_token %}
      {% for quiz in assignment_data %}
      <div class="assignment hide-content">
        <div class="row assignment-header">
          <div class="col">{{quiz.title}}</div>
          <div class="col">published: {{quiz.published}}</div>
          <div class="col">Questions: {{quiz.question_count}}</div>
          <div class="col-sm toggle-col">
            <i class="toggle-drop-icon drop-icon-down fa-sharp fa-solid fa-caret-down"></i>
            <i class="toggle-drop-icon drop-icon-up fa-sharp fa-solid fa-caret-up"></i>
          </div>
        </div>
        <div class="row assignment-content">
          <div class="col">
            <!--
              Changes:
              Moved `scan_quiz` to a class instead of id (because there will be multiple)
              Moved hidden fields into button attibutes (easier to handle in javascript)
            -->
            <button class="scan_quiz button" type="submit" quiz_id="{{quiz.id}}" quiz_title="{{quiz.title}}">
              <span class="button-text">Scan</span>
            </button>
          </div>
          <div class="col">
            Cheatability
            <br>
            <span class="grey">0/{{quiz.question_count}}</span>
          </div>
          <div class="col">
            Last Scan
            <br>
            <span class="grey">Not Scanned</span>
          </div>
          <div class="col">
            <a href="#" disabled>View Results</a>
          </div>
        </div>
      </div>
      {% endfor %}
    </div>
    

    Generally you would throw the javascript into an external file and source it in the html, but for simplicity I decided not to do that

    I included the status and msg response, as I find it very robust. It allows you to handle two types of errors in a consistent manner

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search