Django – Implement textbox with autocomplete feature to enter multiple emails separated by comma

The JavaScript portion to implement an autocomplete was pretty straightforward. JQuery site for more the sample. The challenge was to get this implementation into Django and specially because the source data was dynamically being passed from the view. But once I figured out how to convert the python list to a javascript array, things were pretty straightforward.

View

def sendFulfillmentEmail(request, tenant_id):
    # Eliminating code before
    email_contacts = [str(u.email) for u in User.objects.all().filter(is_active=True)]
    email_contacts = simplejson.dumps(email_contacts)
    # Eliminating code after
    template = 'app/fulfillment_email_form.html'
    return render_to_response(template, locals(), context_instance=RequestContext(request))

Template
Now, the email_contacts need to be converted to a JavaScript array and used as a source.

{% extends "../../opportunities/templates/opportunities/base.html" %}
{% block extrahead %}
< link rel="stylesheet" href="/site-media/js/jquery-ui-themes-1.11.4/themes/smoothness/jquery-ui.css">
< script src="/site-media/js/jquery-ui-1.11.4/jquery-ui.js"></script>
<!-- Also requires the jquery.js file but that's already imported in base.html so not importing again -->


$(function() {
	//alert('{{email_contacts|safe}}');
	var email_contacts = {{email_contacts|safe}};   //-- The safe filter converts the JSON content to JavaScript array
	function split( val ) {
	  return val.split( /,\s*/ );
	}
	function extractLast( term ) {
	  return split( term ).pop();
	}
 
        $( "#id_email_cc" )
		// don't navigate away from the field on tab when selecting an item
		.bind( "keydown", function( event ) {
			if ( event.keyCode === $.ui.keyCode.TAB &&
			  $( this ).autocomplete( "instance" ).menu.active ) {
				event.preventDefault();
			}
		})
		.autocomplete({
			minLength: 0,
			autoFocus: true,     //- This will automatically select the first item from the menu. On hitting tab, selected item will be populated.
			source: function( request, response ) {
			// delegate back to autocomplete, but extract the last term
			response( $.ui.autocomplete.filter(
				email_contacts, extractLast( request.term ) ) );
        	        },
			focus: function() {
				// prevent value inserted on focus
				return false;
			},
			select: function( event, ui ) {
				var terms = split( this.value );
				// remove the current input
				terms.pop();
				// add the selected item
				terms.push( ui.item.value );
				// add placeholder to get the comma-and-space at the end
				terms.push( "" );
				this.value = terms.join( ", " );
				return false;
			}
		});
});

<meta name="robots" content="NONE,NOARCHIVE" />
{% endblock %}
</pre>
{% block content %}

<!-- Eliminating other parts of form -->

			{{fulfillment_email_form.email_cc}}
			
		        <!-- View the source of HTML file and the id of the field is set to id_email_cc and that's what is used in the JQuery function -->
<!-- Eliminating other parts of form -->
{% endblock %}

Logging activity on Django’s admin

Django has inbuilt capability to log actions into the database for auditing purposes. Here’s a sample how this can be implemented:

from django.contrib.admin.models import LogEntry, CHANGE
from django.contrib.contenttypes.models import ContentType

#Inside your view or class. The view should have this on top of the definition "@log_info"
LogEntry.objects.log_action(
    user_id         = request.user.id, 
    content_type_id = ContentType.objects.get_for_model(tenant).pk,
    object_id       = tenant.pk,
    object_repr     = force_unicode(tenant),  # or tenant.__unicode__()
    action_flag     = CHANGE
)

Serve and Consume API that requires a JSON and not POST Fields

In PHP, $_POST would give you the POST payload. I was trying to post a multi-dimentional JSON to an API but the $_POST wasn’t able to get data for a field that had a list of values within,

e.g. {“email_recipients”:[“recipient1@gmail.com”, “recipient2@gmail.com”]}

Therefore, I had to write the API so it accepts a JSON which can be read into the API and converted to an array as follows:

$post_json = file_get_contents("php://input");
$array_post_data = json_decode($post_json, true);

Now, the client CURL call would appear something like this:

$json = {"email_recipients":["recipient1@gmail.com", "recipient2@gmail.com"]};
$url = "http://api.domain.com/api/send_email_attachment.php";
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_HTTPHEADER, array("Content-type: application/json"));
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $json_data);
$response = curl_exec($curl);
$curl_error = curl_error($curl);
curl_close($curl);

.

Django custom form, custom template and custom field choices

It took a lot of time to create a completely custom form with a custom template (not using the form.as_table feature of Django) and also loading custom field choices. Here’s a detail of the url, view and template – done from scratch and done right.

Form

class FulfillmentEmailForm(forms.Form):
    # Note that I haven't provided the choices for the selects here and rather providing them in the form init. This way, this form can be a generic form and the values can be populated based on the uses. This is the right way of creating a custom form rather than feeding in the choices early and thereby having to create a new form if a choice value changes.
    email_from = forms.ChoiceField(required=True, widget=forms.Select())
    email_recipient = forms.MultipleChoiceField(required=True, widget=forms.CheckboxSelectMultiple())
    email_cc = forms.CharField(required=False, widget=forms.Textarea(), label=u'Cc', help_text='Enter multiple emails separated by comma(,)')
    email_subject = forms.CharField(max_length=300, required=True, widget=forms.TextInput())
    email_body = forms.CharField(required=True, widget=forms.Textarea())
    email_attachment = forms.MultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple())
    
    def __init__(self, *args, **kwargs):
        email_recipient_choices = None
        email_from_choices = None
        email_attachment_choices = None
        # Get the choice values first, before super is called
        if 'email_recipient_choices' in kwargs:
            email_recipient_choices = kwargs.pop('email_recipient_choices')
        if 'email_from_choices' in kwargs:
            email_from_choices = kwargs.pop('email_from_choices')
        if 'email_attachment_choices' in kwargs:
            email_attachment_choices = kwargs.pop('email_attachment_choices')
        # Call the super method on the form
        super(FulfillmentEmailForm, self).__init__(*args, **kwargs)
        # Modify the field properties after the super is called
        if email_recipient_choices:
            # The commented line here shows how a form can be generalized. Based on the inputs passed (if any), the required property of the field can be set accordingly therefore making the form generic for reuse. But here, I'm only modifying the choices property because that's what I need
            #self.fields['email_recipient'] = forms.ChoiceField(required=True, widget=forms.CheckboxSelectMultiple(), choices=email_recipient_choices)
            self.fields['email_recipient'].choices=email_recipient_choices
        if email_from_choices:
            #self.fields['email_from'] = forms.ChoiceField(required=True, widget=forms.Select(), choices=email_from_choices)
            self.fields['email_from'].choices=email_from_choices
        if email_attachment_choices:
            #self.fields['email_attachment'] = forms.MultipleChoiceField(required=False, widget=forms.CheckboxSelectMultiple(), choices=email_attachment_choices)
            self.fields['email_attachment'].choices=email_attachment_choices

    def clean_email_cc(self):
        '''
        should be a comma separated list of emails
        '''
        data = self.cleaned_data['email_cc']
        if data:
            for email in data.split(','):
                # Some function to validate a single email address
                validate_email(email.strip())
        return data

View

@log_info
@login_required
@any_group_required([ 'Support', 'Product Management', 'Sales Engineers',])
def sendProcessEmail(request, tenant_id):
    # tenant_id is just to get some information on deal and the default license recipients from the database. You can substitute this with your own code
    tenant = get_object_or_404(InstallationTenant, pk=tenant_id)
    deal = tenant.installation.getLastNonZeroDeal()
    email_recipient_choices = [(c.email, '%s %s (%s)' % (c.first_name, c.last_name, c.email)) for c in deal.getLRecipientContact()]
    email_from_choices = [('sender_email@domain.com','sender_email@domain.com')]
    email_attachment_choices = [('license', 'License file'),('deployment_guide','Deployment Guide'),('vmware','VMware'),
                                    ('hyper_v','Hyper-V (2008+2010)'),('ms_azure','Microsoft Azure')]
    # Getting email templates from the DB. Each template has a name, description, email_subject and email_body. Name and Description are used to select the appropriate template from a list. Subject and body are used to populate the form once the appropriate template is selected.
    fulfillment_email_templates = CommonEmailTemplate.objects.filter(type='kw-fulfillment')
    if request.method == 'POST':
        # Had to struggle to get this right. I was only using FulfillmentEmailForm(request.POST). As a result of that, the form after submission would lose the custom choices and would throw errors on page that the submitted value is not a valid choice. It's important to pass in the choices even after form POST which is being done below.
        fulfillment_email_form = FulfillmentEmailForm(request.POST, email_recipient_choices=email_recipient_choices,
                                                      email_from_choices=email_from_choices, email_attachment_choices=email_attachment_choices)
        if fulfillment_email_form.is_valid():
            email_from = fulfillment_email_form.cleaned_data['email_from'].encode('UTF-8')
            email_recipient = fulfillment_email_form.cleaned_data['email_recipient']
            email_cc = fulfillment_email_form.cleaned_data['email_cc'].encode('UTF-8')
            email_subject = fulfillment_email_form.cleaned_data['email_subject'].encode('UTF-8')
            email_body = fulfillment_email_form.cleaned_data['email_body'].encode('UTF-8')
            email_attachment = fulfillment_email_form.cleaned_data['email_attachment']
            # Now you can perform the required business logic based on the data
    else:
        initial_form_data = {'email_from': 'fulfillment@accellion.com',
                             'email_cc' : 'shipping@accellion.com', 
                             'email_attachment' : 'license'
                             }
        # Pass in the initial form data as well as choices for the fields
        fulfillment_email_form = FulfillmentEmailForm(initial=initial_form_data, email_recipient_choices=email_recipient_choices,
                                                      email_from_choices=email_from_choices, email_attachment_choices=email_attachment_choices)
    template = 'module/fulfillment_email_form.html'
    
    return render_to_response(template, locals(), context_instance=RequestContext(request))

Template
Note that you have to provide your own error notifications on form submit in a custom template. The {{fulfillment_email_form.field_name.errors}} is being used to show errors for each field. Also, there is some JavaSript and CSS capabilities in the form to populate the form that have been described in the previous article.

{% extends "../../opportunities/templates/opportunities/base.html" %}

{% block title %}Send fulfillment email{% endblock %}

{% block account %}

{% endblock %}

{% block navigation %}
{% endblock %}

{% block content %}

<fieldset class="module" style="width: 800px">
	<h2>Send fulfillment email</h2>
	<form action={% url sendFulfillmentEmail tenant_id %} method="post">{% csrf_token %}
	<div class="table-row">
		<div class="table-cell">
			Select fullfillment email template:
		</div> 
		<div class="table-cell">
			<input type="button" name="btn_fulfillment_template" value="Email templates" onclick="location.href='#kw_fulfillment_templates'">
		</div>
		<div class="table-cell">
			<span id="selected_template_name" style="color: RGB(0,0,100)"></span>
		</div>
	</div>
	<div id="kw_fulfillment_templates" class="modalDialog">
		<div>
		<a href="#close" title="Close" class="modalclose">X</a>
		<h2>Fulfillment email templates for kiteworks orders</h2>
		<table>
			{% for row in fulfillment_email_templates %}
				<tr class="{% cycle "row1" "row2" %}">
					<td><input type="button" id="tmp_sel_{{forloop.counter}}" onclick="setTemplateData({{forloop.counter}})" value="Select"></td>
					<td id="tmp_name_{{forloop.counter}}">{{row.name}}</td>
					<td id="tmp_desc_{{forloop.counter}}">{{row.description}}</td>
					<td id="tmp_subj_{{forloop.counter}}" style="display: none">{{row.email_subject}}</td>
					<td id="tmp_body_{{forloop.counter}}" style="display: none">{{row.email_body}}</td>
				</tr>
			{% endfor %}
		</table>
		</div>		
	</div>
	<div class="table-row">
		<div class="table-cell">
			Email from:
		</div> 
		<div class="table-cell">
			{{fulfillment_email_form.email_from}}	
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_from.errors}}</div>
	<div class="table-row">
		<div class="table-cell">
			Email recipient(s):
		</div>
		<div class="table-cell">
			{{fulfillment_email_form.email_recipient}}
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_recipient.errors}}</div>
	<div class="table-row">
		<div class="table-cell">
			Cc:
		</div>
		<div class="table-cell">
			{{fulfillment_email_form.email_cc}}
			<br/>Enter multiple email addresses separated by comma (,)
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_cc.errors}}</div>
	<div class="table-row">
		<div class="table-cell">
			Subject:
		</div>
		<div class="table-cell">
			{{fulfillment_email_form.email_subject}}
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_subject.errors}}</div>
	<div class="table-row">
		<div class="table-cell">
			Message:
		</div>
		<div class="table-cell">
			{{fulfillment_email_form.email_body}}
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_body.errors}}</div>
	<div class="table-row">
		<div class="table-cell">
			Attachments:
		</div>
		<div class="table-cell">
			{{fulfillment_email_form.email_attachment}}
		</div>
	</div>
	<div class="table-row">{{fulfillment_email_form.email_attachment.errors}}</div>	
	<input type="submit" id="id_submit" value="Submit" class="prevent_doubleclick"><br><br>
	</form>
</fieldset>

{% endblock %}



Django, HTML, JavaScript, CSS – ModalWindow with Scroll on Button Click

In the previous post, I displayed how a modal window can be used to show some basic information. But this time around, I had more challenges to deal with:
1. Open the Modal Window on click of button rather than a link. So the button had to be used like a link by using location.href
2. The data in the modal window was huge. So I had to use a scrolling modal window.
3. The CSS for the X(close) button had to be adjusted
4. The scroll borders had to be hidden on the modal window as they looked ugly
5. The modal window had a list of items to select from. After selecting the item, it should be loaded on the main page and the modal window had to be closed so the user doesn’t have to do that extra step.

Here’s the solution:
HTML code
The onclick=”location.href”#kw_fulfillment_templates’ opens up the modal window on click of the button and simulates a link behavior on a button.

<div class="table-row">
	<div class="table-cell">
		Select fullfillment email template:
	</div> 
	<div class="table-cell">
		<input type="button" name="btn_fulfillment_template" value="Email templates" onclick="location.href='#kw_fulfillment_templates'">
	</div>
	<div class="table-cell">
		<span id="selected_template_name" style="color: RGB(0,0,100)"></span>
	</div>
</div>
<div id="kw_fulfillment_templates" class="modalDialog">
	<div>
	<a href="#close" title="Close" class="modalclose">X</a>
	<h2>Fulfillment email templates for kiteworks orders</h2>
	<table>
		{% for row in fulfillment_email_templates %}
			<tr class="{% cycle "row1" "row2" %}">
				<td><input type="button" id="tmp_sel_{{forloop.counter}}" onclick="setTemplateData({{forloop.counter}})" value="Select"></td>
				<td id="tmp_name_{{forloop.counter}}">{{row.name}}</td>
				<td id="tmp_desc_{{forloop.counter}}">{{row.description}}</td>
				<td id="tmp_subj_{{forloop.counter}}" style="display: none">{{row.email_subject}}</td>
				<td id="tmp_body_{{forloop.counter}}" style="display: none">{{row.email_body}}</td>
			</tr>
		{% endfor %}
	</table>
	</div>		
</div>

CSS Code
The overflow:hidden would enable scrollbars but hide the physical scroll bars.


.modalDialog {
	position: fixed;
	font-family: Arial, Helvetica, sans-serif;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: rgba(0,0,0,0.8);
	z-index: 99999;
	opacity:0;
	-webkit-transition: opacity 100ms ease-in;
	-moz-transition: opacity 100ms ease-in;
	transition: opacity 100ms ease-in;
	pointer-events: none;
	overflow: scroll;
}

.modalDialog:target {
	opacity:1;
	pointer-events: auto;
}

.modalDialog > div {
	width: 800px;
	position: relative;
	margin: 2% auto;
	padding: 15px 15px 15px 15px;
	border-radius: 5px;
	background: #fff;
	background: -moz-linear-gradient(#fff, #999);
	background: -webkit-linear-gradient(#fff, #999);
	background: -o-linear-gradient(#fff, #999);
	overflow: hidden;
}

.modalclose {
	background: #606061;
	color: #FFFFFF;
	line-height: 25px;
	position: absolute;
	right: 12px;
	text-align: center;
	top: 10px;
	width: 24px;
	text-decoration: none;
	font-weight: bold;
	-webkit-border-radius: 12px;
	-moz-border-radius: 12px;
	border-radius: 12px;
	-moz-box-shadow: 1px 1px 3px #000;
	-webkit-box-shadow: 1px 1px 3px #000;
	box-shadow: 1px 1px 3px #000;
}

.modalclose:hover { background: #00d9ff; }

JavaScript code
The location.href=”#close” takes care of closing the modal window after the JavaScript implementation is complete.

function setTemplateData(index) {
	$td_name = $( "#tmp_name_"+index );
	$td_subj = $( "#tmp_subj_"+index );
	$td_body = $( "#tmp_body_"+index );

	$span_name = $( "#selected_template_name" );
	$input_subject = $( "#id_email_subject ");
	$txtarea_body = $( "#id_email_body ");

	$span_name.text($td_name.text());
	$input_subject.val($td_subj.text());
	$txtarea_body.val($td_body.text());

	location.href="#close";
}

Modal window with scroll

Modal window in Django with dynamic Id’s

I had multiple objects on a page and each one of them have independent information that needs to be displayed on the modal window. Here’s what I did for the same:

HTML Code


<p style="padding: 0px 20px 5px 20px"><a href="#openModal_{{installation.id}}">Show additional usage data</a></p>
<div id="openModal_{{installation.id}}" class="modalDialog">
	<div>
	<a href="#close" title="Close" class="close">X</a>
	<h2>Additional usage data metrics</h2>
	<p style="padding: 0px 20px 5px 20px">Software Version: {{installation.getKwSoftwareVersion}}</p>
		<p style="padding: 0px 20px 5px 20px">Active Licensed Users: {{installation.getActiveLicensedUsers}}</p>
		<p style="padding: 0px 20px 5px 20px">FIPS: Enabled</p>
		<p style="padding: 0px 20px 5px 20px">Files sent: 1,234</p>
		<p style="padding: 0px 20px 5px 20px">Emails sent: 5,678</p>
	</div>
</div>

CSS Code

.modalDialog {
	position: fixed;
	font-family: Arial, Helvetica, sans-serif;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background: rgba(0,0,0,0.8);
	z-index: 99999;
	opacity:0;
	-webkit-transition: opacity 400ms ease-in;
	-moz-transition: opacity 400ms ease-in;
	transition: opacity 400ms ease-in;
	pointer-events: none;
}

.modalDialog:target {
	opacity:1;
	pointer-events: auto;
}

.modalDialog > div {
	width: 400px;
	position: relative;
	margin: 10% auto;
	padding: 5px 20px 13px 20px;
	border-radius: 10px;
	background: #fff;
	background: -moz-linear-gradient(#fff, #999);
	background: -webkit-linear-gradient(#fff, #999);
	background: -o-linear-gradient(#fff, #999);
}

.close {
	background: #606061;
	color: #FFFFFF;
	line-height: 25px;
	position: absolute;
	right: -12px;
	text-align: center;
	top: -10px;
	width: 24px;
	text-decoration: none;
	font-weight: bold;
	-webkit-border-radius: 12px;
	-moz-border-radius: 12px;
	border-radius: 12px;
	-moz-box-shadow: 1px 1px 3px #000;
	-webkit-box-shadow: 1px 1px 3px #000;
	box-shadow: 1px 1px 3px #000;
}

.close:hover { background: #00d9ff; }

More description on the Modal window behavior is available in this tutorial.