Extensible Calendar
This page documents an example of using the Extensible Calendar Pro API with Django as the back end.
Contents
REST
The Extensible Calendar is going to connect to your server via AJAX calls. These calls conform to a web application paradigm known as REST, or REpresentational State Transfer. In particular, the application will automatically send requests with the following HTTP methods:
- GET when it wants to read something
- POST when it wants to create something
- PUT when it wants to change something
- DELETE when it wants to remove something
You recognize GET and POST from Module 2. All along, there were actually more than just these two request methods. REST simply takes advantage of all of them.
Note: In practice, since older browsers do not support the additional HTTP request methods required by REST, the actual implementation of PUT and DELETE will most likely be POST with an additional header defining the request type. The frameworks takes care of this difference under the hood.
The Back End
First, we will set up the back end.
Creating the App with Source Control
Start by making a new Django project and app.
$ django-admin.py startproject cse330calendar
$ cd cse330calendar
$ python manage.py startapp cal
Right now would be a good time to set up your IDE and source control. Create a new Komodo project in the "cse330calendar" directory, and set up a repo on the "cse330calendar" in SourceTree. Make frequent commits after each step of the process.
Initial Configuration
Go into settings.py and configure your database.
Enable the admin panel, which requires editing both settings.py and urls.py. Add the cal application to the admin panel by creating cal/admin.py with the following content:
from django.contrib import admin
# We will create these models in the next step
from cal.models import Cal, Event
admin.site.register(Cal)
admin.site.register(Event)
Finally, add "cal" to the INSTALLED_APPS list in settings.py.
Creating the Models
Set up the models required for the Extensible Calendar. We have already done this work for you. Copy the following code into your cal/models.py file.
from django.db import models
# Extensible wants two models: a "calendar" model and an "event" model.
# The "calendar" model represents a collection of events. Note that the end
# application supports multiple calendars in the same view, with the events
# in each calendar having a different background color.
#
# First we will define our calendar model, `Cal`, and then we will define
# our event model, `Event`.
class Cal(models.Model):
# Title of the calendar
title = models.CharField(max_length=50)
# Text description of the calendar
description = models.CharField(max_length=200)
# Color ID (1-32)
color = models.IntegerField(default=1)
# Boolean for whether the calendar is hidden by default
hidden = models.BooleanField(default=False)
class Event(models.Model):
# Declare a one-to-many relationship with Cal
cal = models.ForeignKey(Cal)
# Title of the event
title = models.CharField(max_length=50)
# DateTime of event start
start = models.DateTimeField()
# DateTime of event end
end = models.DateTimeField()
# Additional information that can be associated with an event:
loc = models.CharField(max_length=50) # Location
notes = models.CharField(max_length=200) # Notes
url = models.CharField(max_length=100) # URL
ad = models.BooleanField(default=False) # Is this an all-day event
rem = models.CharField(max_length=200) # Reminder
Finally, sync the db. After you do this, run the Django server and play around with the admin panel to make sure everything is in working order.
$ python manage.py syncdb
Tastypie
Django does not come with REST support out of the box, so we need to install an additional framework. I have so far had success with Tastypie, and I will be using Tastypie throughout this tutorial.
Start by installing Tastypie. There are instructions on the above link.
Your cal/api.py file can contain simply:
from tastypie.resources import ModelResource
from tastypie.paginator import Paginator
from cal.models import Cal, Event
from tastypie.fields import IntegerField
class CalResource(ModelResource):
class Meta:
queryset = Cal.objects.all()
paginator_class = Paginator
class EventResource(ModelResource):
cal_id = IntegerField(attribute="cal__id") # load the calendar ID as an integer
class Meta:
queryset = Event.objects.all()
paginator_class = Paginator
In the above snippet, we also are installing the Paginator utility. The Extensible Calendar requires that your data have pagination.
I also recommend making JSON the default output format for Tastypie. To do this, add the following line to your settings.py file:
TASTYPIE_DEFAULT_FORMATS = ['json']
Routing
Let's create an empty index.html for our site. We will be modifying it later. Create the file in cal/templates/cal/index.html. Then add it to cal/views.py:
from django.shortcuts import render
def index(request):
return render(request, 'cal/index.html', {})
Also add it to cal/urls.py:
from django.conf.urls import patterns, url
from cal import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index')
)
Finally, add cal to your core urls.py file, which by now should look something like this:
from django.conf.urls import patterns, include, url
from cal.api import CalResource, EventResource
# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()
# RESTful setup:
cal_resource = CalResource()
event_resource = EventResource()
urlpatterns = patterns('',
# Admin page
url(r'^admin/', include(admin.site.urls)),
# Templates
url(r'^', include('cal.urls')),
# RESTful URLs
url(r'^', include(cal_resource.urls)),
url(r'^', include(event_resource.urls)),
)
Try launching http://localhost:8000/cal/ in your browser. If everything is working properly, you should see a JSON representation of the data in your calendar.
Additional Work on the Back End
In the above example, we have implemented a read-only API for your calendar. You will need to use Django Auth to add users and to only reveal those calendars associated with a user. You will also need to implement the writing site of Tastypie. Almost all of these steps are in the Tastypie documentation.
The Front End
So far, all of our work has been setting up our database, models, and server API. Now, we need to set up the front end that takes the JSON data and displays it in a GUI for the end user.
Installing Extensible
Start by downloading the Extensible library from the official web site. Save the download in cal/static/vendor/extensible.
The Core Extensible Code
The examples on the Extensible documentation are helpful, but if you have never used Ext.JS before, it is easy to get lost. We have therefore written an index.html file and an app.js file to get you started.
Put the following code in cal/templates/cal/index.html:
<!DOCTYPE html>
{% load staticfiles %}
<html>
<head>
<title>Calendar</title>
<script type="text/javascript" src="{% static 'vendor/extensible/Extensible-config.js' %}"></script>
<script type="text/javascript" src="{% static 'cal/app.js' %}"></script>
</head>
<body>
<div id="calbox" style="width:600px; height:400px;"></div>
<span id="app-msg" class="x-hidden"></span>
</body>
</html>
Put the following code in cal/static/cal/app.js:
// Demo Extensible Calendar App
// Created for CSE 330S by Shane Carr
// Washington University in St. Louis
// Ext.JS wants you to use `Ext.define` when making a custom component.
Ext.define("CSE330.Calendar", {
// List the classes we use inside this component. This enables Ext.JS to load
// the files containing the definitions for these classes more efficiently.
requires: [],
// Main code for our component
constructor: function(){
// Set up the calendar store
this.calendarStore = Ext.create("Extensible.calendar.data.MemoryCalendarStore", {
autoLoad: true,
storeId: "Calendars",
proxy: {
type: "rest",
url: "/cal/",
startParam: "offset",
reader: {
type: "json",
root: "objects"
}
}
});
// Set up the event store
this.eventStore = Ext.create("Extensible.calendar.data.MemoryEventStore", {
autoLoad: true,
storeId: "Events",
proxy: {
type: "rest",
url: "/event/",
startParam: "offset",
reader: {
type: "json",
root: "objects",
}
}
});
// Set up the panel (view)
this.container = Ext.create('Extensible.calendar.CalendarPanel', {
renderTo: Ext.get("calbox"),
title: "My CSE330 Calendar",
width: "100%",
height: "100%",
id: "ext-calendar-main",
eventStore: this.eventStore,
calendarStore: this.calendarStore,
monthViewCfg: {
showHeader: true // show the days of the week
}
});
}
});
Ext.onReady(function(){
Ext.create("CSE330.Calendar");
});
You should be able to load http://localhost:8000/ and see an empty calendar with all events you have thusfar put in your database. Exciting!
Additional Work on the Front End
Again, right now you have a read-only calendar without any sort of authentication. You need to tie these two pieces together.
If you look at the web inspector, you can see the requests that Extensible sends to your server when you try to create or modify an event using the front-end GUI. You need to make sure that your server and Extensible work together, both sending and receiving compatible formats.
You can implement log in and log out over AJAX via the Tastypie API. There are examples of how to do this in the documentation.
More Tips
After you finish the above two steps, you need to make a few more changes to save you hours worth of headaches.
Add the following line to your settings.py file:
APPEND_SLASH=False
In Extensible-config.js (inside cal/static/vendor/extensible), change the Ext.JS version on line 59 from 4.2.0 to 4.1.1.
Look at this page in the Tastypie documentation for a lot of great information: http://django-tastypie.readthedocs.org/en/latest/resources.html
More tips coming soon.