Search code examples
javascriptdjangodjango-viewsdjango-formsdjango-templates

How do I pass a Javascript variable to my Django framework to be used in template or via get_context?


I have been working to pass form values to a modal using Django. I have gotten most of the way there, but I can't quite make out how to connect the two....

Here is my AJAX...

     $("#avail").on("click", function(event) {
       var modalavail = document.getElementById("myModalavail");
       var markedCheckbox = document.querySelectorAll('input[type="checkbox"]:checked');
       event.preventDefault();
       $(window).unbind('beforeunload');
       $.ajax({
           method: 'get',
           headers: { "X-CSRFToken": token },
           url: "",
           processData: false,
           contentType: false,
           cache: false,
           enctype: 'multipart/form-data',
           success: function(resp) {
             modalavail.style.display = "block";
             for (var checkbox of markedCheckbox) {
             console.log(checkbox.value + ' ');
           }
           },
           error: function (request, status, error) {
             console.log(request.responseText);
             showAjaxFormErrors(JSON.parse(request.responseText));
             $('html, body').animate({ scrollTop: 0 }, 50);
             },
          });
       });

Here is my HTML...

  <button class="button209" id="avail">Check Availability</button>

  <div id="myModalavail" class="modal">
    <div class="modal-content">
      <span class="close"></span>
      <img class="logo4" src="/static/images/threecircles.svg">

      <div class="table56">

        {% for entry in form.approvers %}

          <table class="table57">
            <thead>
              <tr>
                <th class="title156">Name</th>
                <th class="title118">Available?</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td class="title156">{{ entry.name }}</td>
                <td class="title117">Yes</td>
              </tr>
            </tbody>
          </table>
        {% endfor %}
      </div>
      <input type="hidden" id="modal-approvers">
      <button type="button" class="button210" id="modalcloseavail">Close</button>
    </div>
  </div>

Obviously, form.approvers is going to give me all of the eligible approvers in my form....However I'm just trying to pass the selected approvers...

This line of code is working flawlessly....

  var markedCheckbox = document.querySelectorAll('input[type="checkbox"]:checked');

It is getting the approvers that are selected in the form....

Here's my form....

class DailyPlannerForm(forms.ModelForm):
    class Meta:
    model = NewDailyPlannerEvent
    exclude = [ 'created_by','summary']

def __init__(self, user, *args, **kwargs):
    start_time = kwargs.pop('start_time', None)
    User = get_user_model()
    super(DailyPlannerForm, self).__init__(*args, **kwargs)
    self.fields['approvers'] = forms.ModelMultipleChoiceField(
                            widget=forms.CheckboxSelectMultiple(),
                            queryset=User.objects.exclude(Q(is_active=False)).order_by('last_name','first_name'),required=False)

def clean(self):
    cleaned_data = super(DailyPlannerForm, self).clean()
    approvers = cleaned_data.get('approvers')

Here's the model....

class NewDailyPlannerEvent(models.Model):

    STATUS_CHOICES = (
    ("AwaitingResponse","AwaitingResponse"),
    ("Cancelled","Cancelled"),
    ("Declined","Declined"),
    ("Delete","Delete"),
    ("Saved","Saved"),
    ("Scheduled","Scheduled"),
    ("Submitted","Submitted"),
)

STATUS_CHOICES1 = (
    ("Cancel","Cancel"),
)

STATUS_CHOICES3 = (
    ("True","True"),
    ("False","False"),
    ("N/A","N/A")
)

approver_by_department = models.ManyToManyField(Department,related_name="new_daily_planner_event_approver_by_department")
approvers = models.ManyToManyField(User)
created_by = models.ForeignKey(User,null=True,on_delete=models.CASCADE,related_name='new_daily_planner_event_created_by')
creation_date = models.DateTimeField(editable=False,null=True)
daily_planner_event_creation_date = models.DateTimeField(editable=False,null=True)
daily_planner_event_name = models.CharField(max_length=255,blank=True)
last_update = models.DateTimeField(null=True)        
request_type = models.CharField(choices=REQUEST_TYPE,default="New Daily Planner Event Request",max_length=35)
start_time = models.DateTimeField(null=True,blank=True)
status = models.CharField(choices=STATUS_CHOICES,default="Submitted",max_length=20)
submitted = models.CharField(choices=STATUS_CHOICES3,default="False",null=True,blank=True,max_length=20)
    
class Meta:
    ordering = ["daily_planner_event_name"]
    
def __str__(self):
    return self.daily_planner_event_name
    
def get_absolute_url(self):
    return reverse("DailyPlannerEvents:create_daily_planner_event_detail",kwargs={'pk':self.pk})

But I can't figure out how to pass this information back to the HTML so that I can loop through the users who are selected and do more querying and I need to do all of this before the form is submitted.

Thanks in advance for any thoughts.


Solution

  • It's quite easy to display the checked values in the HTML without any AJAX request.

    First, your HTML. Leave everythis as is but make the following changes:

    1. Remove the for loop in the html table.
    2. Empty the table's tbody.

    Then, add the script:

    let modalElement = $("#myModalavail")
    modalElement.hide() // hide the modal by default
    $("#avail").on("click", ()=>{
        let checkedValues = $("input[type='checkbox']:checked") // They will only be the input tags, without labels
        $("table tbody").html("") // Clear the tbody contents
        for (const element of checkedValues) {
            let label = $(`[for=${element.getAttribute("id")}]`).text()
            let bodyRow = $("<tr>").append($("<td>").text(label)).append($("<td>").text("Yes"))
            $("table tbody").append(bodyRow)
        }
        modalElement.show() // Now show the modal
    })
    

    This should work as you expect.

    But I noticed in your ModelForm, you're manually defineing a queryset to the ManyToMany field. That's unnecessary. Instead, you can do it in the model itself by setting blank=True and making use of limit_choices_to.

    class NewDailyPlannerEvent(models.Model):
        ...
        approvers = models.ManyToManyField(User, limit_choices_to=Q(active=True), blank=True)
        ...
    

    Then in the ModelForm, you can simply change the widget in the Meta class:

    class DailyPlannerForm(forms.ModelForm):
        class Meta:
           model = NewDailyPlannerEvent
           exclude = [ 'created_by','summary']
           widgets = {"approvers": forms.CheckboxSelectMultiple()}
    
       def __init__(self, user, *args, **kwargs):
           start_time = kwargs.pop('start_time', None)
           super(DailyPlannerForm, self).__init__(*args, **kwargs)
    

    I don't know why you need to override the init method though. And you don't need the clean method.