kdubs
kdubs

Reputation: 946

Add Calendar Widget to Django Form

I have a view on my website that uses a couple of Django forms to allow the user to specify a date range. I was able to get it so that one Django form creates a start and end field and that when the user clicks on those fields a calendar widget (from here) pops up that allows the user to select a date range. However, when the user selects the date range and hits "apply" the form fields aren't updated.

EDIT

The form I'm using looks like this:

class DateRangeForm(forms.Form):
  def __init__(self, *args, **kwargs):
    initial_start_date = kwargs.pop('initial_start_date')
    initial_end_date = kwargs.pop('initial_end_date')
    required_val = kwargs.pop('required')

    super(DateRangeForm,self).__init__(*args,**kwargs)
    self.fields['start_date'].initial = initial_start_date
    self.fields['start_date'].required = required_val
    self.fields['end_date'].initial = initial_end_date
    self.fields['end_date'].required = required_val

  start_date = forms.DateField()
  end_date = forms.DateField()

The view they are used in looks like this:

def table_search(request):
  initial_start = "2015/2"
  initial_end = "2015/222"
  message = {'last_url':'table_search'}

  if request.method == "POST":
    daterange_form = DateRangeForm(request.POST,required=True,initial_start_date=initial_start,initial_end_date=initial_end)

  else:
    daterange_form = DateRangeForm(required=True,initial_start_date=initial_start,initial_end_date=initial_end)
    search_dict.update({'daterange_form':daterange_form})

  return render(request, 'InterfaceApp/table_search.html', search_dict)

The Django template here:

<div class="container">
  <form action="/InterfaceApp/home/" method="post" class="form">
  {% csrf_token %}
    <div class="daterangepicker-container mcast-search-filter">
        <div class="daterangepicker-label">Date range:</div>
        <div id="daterange" class="daterangepicker-content">
            {% bootstrap_form daterange_form %}
            <i class="icon-calendar icon-large"></i>
        </div>
    </div>
  </form>
</div>
<script>
    // the start_date and end_date are the ids that django form fields created
    $("#daterange").daterangepicker({
        locale: {
          format: 'YYYY-MM-DD'
        },
        startDate: '{{daterange_form.start_date.value}}',
        endDate: '{{daterange_form.end_date.value}}'
    });
</script>

EDIT 2

And the forms currently look like this (after @ShangWang suggestion) rendered:

enter image description here enter image description here

Is there a way to display it so the start and end date fields show up? I tried changing the div class so it wasn't hidden, and then they showed up but seemed superfluous.

Upvotes: 3

Views: 5874

Answers (1)

Shang Wang
Shang Wang

Reputation: 25539

I use bootstrap-daterangepicker: https://github.com/dangrossman/bootstrap-daterangepicker. It would bind the widget's change to your django form field, so you don't need to manipulate the data once it comes to the views.py.

To get more details you should download and play with it, but here's a rough idea:

Your form.py:

class DateRangeForm(forms.Form):
    start_date = forms.DateField()
    end_date = forms.DateField()

    def __init__(self, *args, **kwargs):
        # initialize the start and end with some dates

Your template:

<div class="daterangepicker-container mcast-search-filter">                                                                             
    <div class="daterangepicker-label">Date range:</div>                                                                                
    <div id="daterange" class="daterangepicker-content">                                                                                
        <i class="icon-calendar icon-large"></i>                                                                                        
        <span></span> <b class="caret"></b>                                                                                             
    </div>                                                                                                                              
</div>                                                                                                                                  

<!-- This is a hidden div that holds your form fields -->                                                                                                                                           
<div class="hide">From {{ daterange_form.start_date }} to {{ daterange_form.end_date }}</div>

To trigger the widget you need a javascript binding:

// the id_start_date and id_end_date are the ids that django form fields created
$("#daterange").initDateRangePicker("#id_start_date", "#id_end_date");

I created a datepicker wrapper, and defined the initDateRangePicker function. You should put following code in a file called daterangepicker.js and import that in your template as well(or simply copy it into your template):

(function($) {                                                                                                                                      
    $.fn.initDateRangePicker = function(start_date_el, end_date_el, future) {                                                                       
        return this.each(function() {                                                                                                               
            var start = moment($(start_date_el).val());                                                                                             
            var end = moment($(end_date_el).val());                                                                                                 

            var display_date = function(start, end) {                                                                                               
                var str = ""                                                                                                                        
                str += start.format('MMMM Do, YYYY');                                                                                               
                str += " - ";                                                                                                                       
                str += end.format('MMMM Do, YYYY');                                                                                                 

                return str;                                                                                                                         
            };                                                                                                                                      

            $(this).find("span").html(display_date(start, end));                                                                                    
            var self = this;                                                                                                                        

            if(!future) {                                                                                                                           
                $(this).daterangepicker({                                                                                                           
                    format: 'YYYY-MM-DD',                                                                                                           
                    timePicker: false,                                                                                                              
                    ranges: {                                                                                                                       
                        'Last 7 days': [moment().subtract('days', 6), moment()],                                                                    
                        'Month to date': [                                                                                                          
                            moment().startOf('month'),                                                                                              
                            moment(),                                                                                                               
                        ],                                                                                                                          
                        'Last Month': [                                                                                                             
                            moment().subtract('month', 1).startOf('month'),                                                                         
                            moment().subtract('month', 1).endOf('month'),                                                                           
                        ]                                                                                                                           
                    },                                                                                                                              
                }, function(start, end) {                                                                                                           
                    $(start_date_el).val(start.format('YYYY-MM-DD'));                                                                               
                    $(end_date_el).val(end.format('YYYY-MM-DD'));                                                                                   

                    $(self).find("span").html(display_date(start, end));                                                                            
                });                                                                                                                                 
            }                                                                                                                                       
            else {                                                                                                                                  
                 $(this).daterangepicker({                                                                                                          
                    format: 'YYYY-MM-DD',                                                                                                           
                    timePicker: false,                                                                                                              
                    ranges: {                                                                                                                       
                        'Next 7 days': [moment().add('days', 1), moment().add('days', 7)],                                                          
                        'Next month': [                                                                                                             
                            moment().add('month', 1).startOf('month'),                                                                              
                            moment().add('month', 1).endOf('month'),                                                                                
                        ],                                                                                                                          
                    },                                                                                                                              
                }, function(start, end) {                                                                                                           
                    $(start_date_el).val(start.format('YYYY-MM-DD'));                                                                               
                    $(end_date_el).val(end.format('YYYY-MM-DD'));                                                                                   

                    $(self).find("span").html(display_date(start, end));                                                                            
                });                                                                                                                                 

            }                                                                                                                                       
        });                                                                                                                                         
    };                                                                                                                                              
}).call(this, jQuery);

Upvotes: 3

Related Questions