user1935160 user1935160 - 9 days ago 3
HTML Question

Making a django html template request with multiple key values

I'm new to using Django or even creating a website, so please bear with me if I have provided too little/much detail about the issue I'm facing. Also, I've spent the better part of the last week trolling SO pages, blogs, Django tutorials, Django documentation, etc. trying to solve the issue myself. Maybe I've overlooked something or I'm just unlucky, but nothing I've found addresses my particular situation in its entirety. Most examples seem to focus on handling requests in views.py and not on how the original request is made in the Django template.

I have a Django template, view_table.html, that displays a Bootstrap DataTable table object to a user. The second column of this table is a BigIntegerField called MyRow_ID. I currently have code in MyCode.js that allows the user to highlight multiple rows, and when button modify_button is clicked, the MyRow_ID values for the selected rows (e.g. [2, 13, 14]) are captured into a JS dict called sent_data. After these values have been captured, I'd like for modify_button to create a GET request that sends sent_data along with it. After matching in urls.py and calling the modify_data function in views.py, modify_data should render a new page modify_table.html while passing back the model instances matching MyRow_ID in order to display only the selected rows' data. I think I'm really close, and perhaps only a tweak to the regex expression is what I need, but here are my questions:


  1. How do I create a GET request in the Django template view_table.html that passes sent_data along to Django? Currently I'm using a form with the method and action attributes set to "GET" and "{% url 'modify_data' sent_data=sent_data %}" respectively. I'm assuming GET and not POST should be used because the request isn't modifying the backend, it's more of a "filter the view" type of request. Is this a correct assumption? What would the requested url look like? Say MyRow_ID values are [2,13,14]. Would the get request look something like /modify_data/matched_row_1=2&matched_row_2=13&matched_row_3=14? Do I have to create this url string myself in the template by iterating over sent_data and attaching a "matched_row_n=" string, or is there a simpler way for the template to create this automatically in the request?

  2. What's the correct regex pattern that should be used in myapp/urls.py, given sent_data could have anywhere from 1 to n unique MyRow_ID values, assuming 1 to n rows are selected respectively? (Obviously robust code would include handling where 0 rows are selected and modify_button is clicked, but let's set that aside for now.) Currently I'm getting a NoReverseMatch Error at /myapp/view_data/: Reverse for 'modify_data' with arguments '()' and keyword arguments '{u'sent_data': ''}' not found. I'm new to using regex, and I know what I have in myapp/urls.py is wrong.

  3. Is the code in myapp/views.py correct to filter the matching model instances and render modify_table.html with the selected rows?



view_table.html:

<!DOCTYPE html>
<html>
<head>
## Bunch of code… ##
</head>
<body>
<div class="col col-xs-12 text-right">
<form style="display" method="get" action="{% url 'modify_data' sent_data=sent_data %}">
<button id="modify_button" type="button" class="btn btn-primary btn-create">Modify Data</button>
</form>
</div>
<br><br><br>
<table id="my_table">
## Code that displays my_table ##
</table>
<!-- Execute JS scripts -->
<script type="text/javascript" src="{% static "myapp/js/jquery-1.12.0.min.js" %}"></script>
<script type="text/javascript" src="{% static "myapp/js/jquery.dataTables.min.js" %}"></script>
<script type="text/javascript" src="{% static "myapp/js/bootstrap.min.js" %}"></script>
<script type="text/javascript" src="{% static "myapp/js/dataTables.bootstrap.min.js" %}"></script>
<script type="text/javascript">
var sent_data = [];
</script>
<script type="text/javascript" src="{% static "myapp/js/MyCode.js" %}"></script>
</body>
</html>


MyCode.js:

$(document).ready(function(){
var oTable = $('#my_table').DataTable();
var selected_data = [];

$('#my_table tbody').on('click','tr',function(){
$(this).toggleClass('active');
});

$('#modify_button').click(function(event){
selected_data = $.map(oTable.rows('.active').data(), function (item) {
return item[1]
});
sent_data = { 'modify_rows': selected_data };
});
});


I should note that I'm using MyRow_ID and not the native DataTable attribute rowID because I'm assuming DataTable's automatically-created rowID do not match the automatically-created primary keys (pk) that Django is using. Is this a correct assumption?

myapp/urls.py:

from django.conf.urls import url
from . import views
from .models import MyDataModel

urlpatterns = [
url(r'^view_data/$', views.view_data, name='view_data'),
url(r'^modify_data/(?P<sent_data>\d+)/$', views.modify_data, name='modify_data'),
]


myapp/views.py:

from django.forms import modelformset_factory
from django.shortcuts import render
from django.http import HttpResponse
from .models import MyDataModel

def view_data(request):
myData = MyDataModel.objects.all()
return render(request, 'myapp/view_table.html', {'myData': myData})

def modify_data(request, sent_data):
MyDataFormSet = modelformset_factory(MyDataModel, fields=('MyRow_ID','MyRow_Text'))
if request.method == 'GET':
selected_rows = sent_data['modify_rows']
## selected_rows = request.GET['modify_rows']
formset = MyDataFormSet(queryset=MyDataModel.objects.filter(MyRow_ID__in=selected_rows))
selected_data = MyDataModel.objects.filter(MyRow_ID__in=selected_rows)
return render(request, 'myapp/modify_data.html', {'formset': formset, 'selected_data': selected_data})
else:
return HttpResponse('A GET request was not received.')


Finally, modify_data.html:

<!DOCTYPE html>
<html>
<head>
## Bunch of code… ##
</head>
<body>
<div class="col col-xs-12 text-right">
<form method="post" action="">
{% csrf_token %}
{{ formset }}
<button id="submit_changes" type="button" class="btn btn-primary btn-create">Submit Changes</button>
</form>
</div>
<br><br><br>
<table id="selected_rows_table">
## Code that displays selected rows passed as selected_data ##
</table>
</body>
</html>


Any help is much appreciated, thank you in advance!

Answer

Through much trial and error and good old-fashioned googling I was able to work out the above issues and gain a better understanding of query strings, regex patterns and even how to use an AJAX request to accomplish my original goal.

My original goal had been to allow the user to select multiple rows of data, click a button, and then edit them simultaneously in a form, said form either being rendered on a new page or in a modal. The following posts/blogs were very helpful for each piece of the puzzle:

Django documentation on urls, request/responses and model formsets:

https://docs.djangoproject.com/en/1.10/topics/http/urls/

https://docs.djangoproject.com/en/1.10/ref/request-response/

https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#model-formsets

Guide to creating Bootstrap modals:

https://coolestguidesontheplanet.com/bootstrap/modal.php

Understanding of how urls and query strings work with multiple values for a single key:

http://meyerweb.com/eric/tools/dencoder/ (for testing)

How to pass multiple values for a single URL parameter?

Capturing url parameters in request.GET

Example of using Django AJAX Form:

http://schinckel.net/2013/06/13/django-ajax-forms/

Code for handling response from server of an AJAX request, and refreshing the page:

Update div with jQuery ajax response html

One important thing to note about using a query string with multiple values for a single key, e.g. say the url of my GET request is something like www.myurl.com/?page=1&page=2&page=3; when using an AJAX GET request, it will create a query string for this url with www.myurl.com/?page[]=1&page[]=2&page[]=3, i.e. it adds the "[]" brackets for any key with multiple values. The usual answers (as documented by Django and others) to retrieve all values of the "page" key in views.py when processing the request is to use request.GET.getlist('page'). THIS WILL NOT WORK. You need to use request.GET.getlist('page[]'). Add the brackets in request.method.getlist() or remove them from the original query string in the url requested.

Finally, here are some snippets of modified code that addressed my original questions:

In view_data.html, updated form:

<form id="modify_form" method="post" action="{% url 'modify_data' %}">
  {% csrf_token %}
  {{ formset }}
</form>

In myapp/urls.py, fixed url finder to handle any query string passed:

url(r'^modify_data/$', views.modify_data, name='modify_data'),

In myapp/views.py, change modify_data code:

def modify_data(request):
  MyDataFormSet = modelformset_factory(MyDataModel, fields=('MyRow_ID','MyRow_Text'))
  if request.is_ajax():
    template = 'myapp/view_data.html'
  else:
    template = 'myapp/modify_data.html'

  if request.method == 'GET':
    selected_rows = request.GET.getlist['modify_rows[]']
    formset = MyDataFormSet(queryset=MyDataModel.objects.filter(MyRow_ID__in=selected_rows))
    selected_data = MyDataModel.objects.filter(MyRow_ID__in=selected_rows)
    return render(request, template, {'formset': formset, 'selected_data': selected_data})
  else:
    return HttpResponse('A GET request was not received.')

In MyCode.js, code has been updated to reflect using modal form, AJAX GET request and refreshing the DOM with Django view.py's response:

$("#modify_button").click(function(){
  selected_data = $.map(oTable.rows('.active').data(), function (item) {
   return item[1]
  });

  $.ajax({
    // The URL for the request
    url: URL,

    // The data to send (will be converted to a query string)
    data: {
      modify_rows: selected_data,
    },

    // Whether this is a POST or GET request
    type: "GET",
  })
    // Code to run if the request succeeds (is done);
    // The response is passed to the function
    .done(function( json ) {
      var forms_result = $('<div />').append(json).find('#modify_form').html();
      $('#modify_form').html(forms_result);
      var table_result = $('<div />').append(json).find('#my_table').html();
      $('#my_table').html(table_result);
    })
    // Code to run if the request fails; the raw request
    // and status codes are passed to the function
    .fail(function( xhr, status, errorThrown ) {
      alert( "Sorry, there was a problem!" );
      console.log( "Error: " + errorThrown );
      console.log( "Status: " + status );
      console.dir( xhr );
    })
});

Hope all of this is somewhat helpful to someone else, if not, this whole process has been a great learning experience for me!