1

I'm implementing a feature of a Django app where I have a form that submits a user's input making an api call that returns a JSON response that I parse and display back on the page. The query results are displayed in a list that I create by iterating through each of the objects. In each list element, I have a button that when clicked opens a modal form. This form has fields for an email recipient, a full name, and a message that the user can specify before clicking submit and sending the email. I need to append some extra data to this form once the form is submitted so it can be sent to the server side and added to the email.

I've seen posts like this which seem to be doing something similar; however, the problem is that the event handler in jquery needs to know which list element it needs to get data from meaning that it has to know the current iteration of the loop in order to get the correct information.

How should I go about implementing this?

Here is the code to make the list based on the data gotten back from the server-side:

var venues = {{venues|safe}};
var dates = {{raw_dts|safe}};
var ticket_statuses = {{ticket_statuses|safe}};
var ticket_urls = {{ticket_urls|safe}};
console.log("length of artist list" + venues.length);
var $list = $("<ul  class='list-group'>");
for(var i = 0; i < venues.length; i++){

    $list.append("<li class='list-group-item'>Artist: {{form_artistSelect}}  Location: " + venues[i].city + ', ' + venues[i].region +' Venue: ' + venues[i].name + 
       "Date: " + dates[i] + "tickets status: " + ticket_statuses[i] + "<br><a href = '" + ticket_urls[i] +"'" + "> ticket link</a>  "+
       "{% if user.is_authenticated %}"+
    "<button id ='invite'type='button' class='btn btn-info btn-lg' data-toggle='modal' data-target='#myModal' venue= " +venues[i] +" date = "+ dates[i] +
    "ticket_url = "+ticket_urls[i]+" artist = {{form_artistSelect}} >Invite a friend</button>  <button id = 'save' type='button' class='btn btn-" + 
    "primary-outline'> Save concert</button> "+
        "{% endif %}"+
            "</li>"); 
}

Here is the shell of the jquery function to append the data :

$list.appendTo($("#container"));
$("#mform").submit(function()){
    $('<input />').attr('type','hidden')
        .attr('')
}

EDIT:

If it's not clear, I specifically need to essentially send the data that the user enters inside of the modal form bundled up with the values of ticket_urls, venues, dates in the position of each array that corresponds to the position at which the button is in the list. Each of these arrays are structured with the same data ordering as it had when the JSON was parsed on the server side. For the dates array, you have this structure var dates = ["2016-03-18T12:00:00", "2016-03-19T12:00:00", "2016-03-20T12:00:00"] where the first date is displayed in the first list element, the second in the second list element, and so on. So for example, if the button in the second list element is clicked, I need the form data to be sent along with each of the values in the second position of each of the arrays.

I was thinking of somehow doing this in the original loop, but I don't think that is possible. How would I go about doing this?

view from views.py

 def search(request):
    queryform = SearchForm(request.POST or None)
    modalform = ModalForm(request.POST or None)
    #print "query form is valid = " + str(modalform.is_valid())
    if queryform.is_valid():
        form_artistSelect = urllib2.quote(queryform.cleaned_data.get("artist_select"))
        form_city =   urllib2.quote(queryform.cleaned_data.get("city"))
        form_state = urllib2.quote(queryform.cleaned_data.get("state"))
        mile_radius = urllib2.quote(queryform.cleaned_data.get("radius"))

        #print "testing"
        url = "http://api.bandsintown.com/artists/" + form_artistSelect + "/events/search.json?api_version=2.0&app_id=YOUR_APP_ID&location=" +form_city+","+ form_state+"&radius="+ mile_radius
        data = json.load(urllib2.urlopen(url))

        #url = "http://api.bandsintown.com/events/search?artists[]=" + form_artistSelect + "&location=" +form_city+","+ form_state+"&radius="+ mile_radius + "&format=json&app_id=YOUR_APP_ID"

        context = {
            "queryform" : queryform,
            "modalform" : modalform,
            "data": data
        }
    else:
        context = {
            "queryform" : queryform 

        }

    if modalform.is_valid():
        form_recipient = modalform.cleaned_data.get("rec_email")
        form_message = modalform.cleaned_data.get("message")
        form_recname = modalform.cleaned_data.get("rec_name")
        print form_recipient
        print form_message
        print form_recname
        concert_venue = modalform.cleaned_data.get("additionalValues[venue]")
        concert_date= modalform.cleaned_data.get("additionalValues[uf_date]")
        concert_url = modalform.cleaned_data.get("additionalValues[ticket_url]")
        artist = modalform.cleaned_data.get("additionalValues[artist]")
        print "concert venue"
        print concert_venue
        print "concert date"
        print concert_date
        print "concert_url"
        print concert_url
        print "artist"
        print artist

    return render(request,"searchform.html" , context)

model where the invite will be stored

class Invite(models.Model):
    sender = models.ForeignKey(User, related_name= "invite_sender", on_delete = models.CASCADE)
    #recipient = models.ForeignKey(User, related_name= "invite_recipient", on_delete = models.CASCADE)
    recipient = models.EmailField()
    concert = models.ForeignKey(Concert, on_delete = models.CASCADE)
    artist = models.ForeignKey(Artist, on_delete = models.CASCADE)
    message = models.CharField(max_length = 120, blank = True, null = True)
    date_sent = models.DateTimeField(auto_now_add = True, auto_now = False)

Relevant forms from forms.py

class SearchForm(forms.Form):
    artist_select = forms.CharField()
    city = forms.CharField()
    state = forms.CharField()
    radius = forms.CharField()


class ModalForm(forms.Form):
    rec_email = forms.CharField()
    message = forms.CharField()
    rec_name = forms.CharField()

Ajax call in template

$("#mform").submit(function(){
    var c = getCookie('csrftoken');
    //var data1 = $().attr("");
    var extraData = [];
    extraData['venue'] = $("invite").attr("venue");
    extraData['artist'] = $("invite").attr("artist");
    extraData['f_date'] = $("invite").attr("formatted_date");
    extraData['uf_date'] = $("invite").attr("date");
    extraData['ticket_url'] =  $("invite").attr("ticket_url");
    extraData['city'] = $("invite").attr("city");
    extraData['region'] = $("invite").attr("region");
    extraData['artist'] = $("invite").attr("artist");
    $ajax({
        context:this,
        type : 'POST',
        dataType: 'json',
        url: '/artistsearch/',
        data: {
            csrfmiddlewaretoken: c,
            //data_form: data1,
            additionalValues: extraData

        },
        success: function(response){}
    });
});
Community
  • 1
  • 1
loremIpsum1771
  • 2,497
  • 5
  • 40
  • 87
  • 1
    It's not really clear what are you asking about. And why you should append such block html when you can simply use django `include` tag or send a template from the backend ? – Dhia Dec 24 '15 at 09:37
  • Sorry, I meant that in addition to the fields of the form that will be sent to the sever when the submit button is clicked, I also wanted to send the current elements in each of the arrays for ```dates[i]```, ```ticket_statuses[i]```, and ```ticket_urls[i]``` which are iterated through in the for loop. Also, what do you mean by sending a template from the backend?? Would that help me to do this? – loremIpsum1771 Dec 24 '15 at 15:46
  • @DhiaTN I just updated the post with a more direct explanation of the problem? – loremIpsum1771 Dec 24 '15 at 16:06
  • Use an Ajax request to Python as the on submit for each list item – rassa45 Jan 03 '16 at 03:03

2 Answers2

3

The code you have provided above seems to be a weird amalgamation of Django and JQuery.

As far as I have experienced:

  1. Django and JQuery work at different stages. Django renders the HTML first, and then JQuery comes into effect.

  2. It's more effective to render repeating HTML code via Django than doing it via JQuery (which I believe you are attempting to do).

Here's what you can use within your Django template:

xyz_template.html

{% load staticfiles %}
<html>
    <head>
        <script src="path_to/ajaxpostcsrf.js"></script>
    </head>
    <body>
        <ul  class='list-group'>
            {% for i in range(0,len_venues) %}
               <li class='list-group-item'>
                   Artist: {{form_artistSelect}} 
                   Location: {{venues.i.city}}, {{venues.i.region}} 
                   Venue: {{venues.i.name}} 
                   Date: {{dates.i}} 
                   tickets status: {{ticket_statuses.i}} <br>
                   <a href = {{ticket_urls.i}}> ticket link </a>

                   {% if user.is_authenticated %}
                      <button id ='invite'type='button' class='btn btn-info btn-lg' data-toggle='modal' data-target='#myModal' venue={{venues.i}} date={{dates.i}} ticket_url={{ticket_urls.i}} artist={{form_artistSelect}}> Invite a friend </button>  
                      <button id = 'save' type='button' class='btn btn-primary-outline'> Save concert </button>
                   {% endif %}
               </li> 
            {% endfor %}
        </ul>
    </body>
</html>

Render the template (on the lines as given above) by passing the relevant context variables, as below in your view:

views.py

def abc_view(request):
    template = 'xyz_template.html'
    return render_to_response(template,
                             {'user': <user_object>,
                              'len_venues': len(venues.objects.all()),
                              'form_artistSelect': <artist>,
                              'venues': venues.objects.all(),
                              'dates': <dates_array>,
                              'ticket_statuses': <ticket_status_array>,
                              'ticket_urls': <ticket_urls_array>
                             },
                             context_instance=RequestContext(request))

Once the template has been rendered, you can send the extra data along with the form submission using an AJAX (on the lines given below):

javascript_file.js

$("#mform").submit(function()){
    var c = getCookie('csrftoken');    
    //Need to add an ajaxpostcsrf.js and call in your html file to use this    
    //--> basically use your way of sending the csrf_token 

    var data1 = $("<pick relevant tag>").attr("<pick relevant data-attr>");
    //save additional data to be added in a variable
    var data2 = $("<pick relevant tag>").attr("<pick relevant data-attr>");
    $.ajax({
        context: this,
        type: 'POST',
        dataType: 'json',
        url: '/your_specific_URL/',
        data: {
              csrfmiddlewaretoken: c,
              data_form: data1,
              data_additional: data2            
        },
        success: function(response) {}
    });
});

ajaxpostcsrf.js

function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

$.ajaxSetup({       
    headers: { "X-CSRFToken": getCookie("csrftoken") }

});
Utkarsh Sinha
  • 941
  • 2
  • 11
  • 30
  • Thanks for the answer, it seems like it would work! I just have a couple of questions, first, since the modal form already handles the submission of its own data itself, why would I need to be sending it in the ajax call? Since Django crispy forms is handling the data, it wouldn't seem like I could send it here by extracting data from the tag. Also, would the url just be the name of the view, in this case "search"? – loremIpsum1771 Dec 26 '15 at 05:21
  • I'm now getting a template syntax error for the for loop: ```Could not parse the remainder: '(0,len_values)' from 'range(0,len_values)'``` referring to this line: ``` {% for i in range(0,len_values) %}``` What could be causing the problem? The code was copied directly as it was added here? – loremIpsum1771 Dec 26 '15 at 06:30
  • Please don't copy the code as it is... There might be a couple of changes here and there.. try to work around the errors.. as the above seems like the syntax for range might be wrong... I'm providing a reference here: http://stackoverflow.com/questions/1107737/numeric-for-loop-in-django-templates – Utkarsh Sinha Dec 26 '15 at 07:16
  • Hey, I got the template loop to work. The problem now is that the data from the additional data isn't being sent to the server, only the input data is being sent. I tried adding all of the extra data into an associative array and then sending that over but its not working. I've updated the post with the new view and the new ajax call. – loremIpsum1771 Dec 27 '15 at 03:03
  • is the ajax request going to the server? try to open Google-Developer Tools > Network and check if your AJAX Post request is being made to the server. If the Ajax Post is not going, it might be due to the CSRF token not being available. I'm adding a JS code to my answer, that you can save in a new file and call in all your templates to get the csrf token. See if that works! – Utkarsh Sinha Dec 27 '15 at 09:36
1

If I correctly get your question:

  • If the elements you want to add should be displayed, you can defined them inside you ModalForm class in addition to the models attributes.

  • In case you need to just send the data with the form without displaying it to the users. I think it's not necessary if you can retrieve the data as the view level. I mean in your view you do as follow:

if form.is_valid():
    form_data = form.cleaned_data
    additional_data = # here you get your data, eventually through Queryset
    # ... continue processing your data here
    # 
Dhia
  • 10,119
  • 11
  • 58
  • 69
  • Thanks for the answer. If I was to do the second option though, how would I keep track which data needs to be sent (i.e. how would the data specific to the list element (
  • ) where the button is pressed) be sent over? It would seem that if I just try to get all of the additional data in the view that I wouldn't be able to know specifically which index of the data to get. I think the first solution might enable this to work, though I'm not exactly sure how I would append each data point to the form field within the template.
  • – loremIpsum1771 Dec 24 '15 at 17:58
  • I guessed that you have in the submitted data, something that will help to retrieve the wanted data from the db. You should share your form view and models otherwise I can't give you an exact answer. – Dhia Dec 24 '15 at 19:47
  • Thanks, I just added the relevant view, model, and forms to the post. The form that I'm trying to send data from here is called ModalForm. Also, I'm not really trying to retrieve data from the DB. When the request is sent, I need to save the data from the form as well as the additional data (being appended) to the invites table in models.py. – loremIpsum1771 Dec 24 '15 at 20:26