Building a Brightkite using django-Part-2

Wednesday, 20th August, 2008 // 8 a.m.

In Building a Brightkite using django-Part-1, I concentrated on the app backend where models, forms & views are created. Since most of the readers were more interested in the templating part, I will be concentrating on the templating part of the app. This session picks up where the initial part left off. In part-1 I queried the Yahoo geo coding service to retrieve location data of a particular location. Now lets display the result of the search performed.

The checkin.html template displays results of the search performed. We can use simple map embed methods to show the location on a map. I used Yahoo maps in this case. Here is the checkin.html

<script type="text/javascript" src="http://api.maps.yahoo.com/
 ajaxymap?v=3.8&appid=enter_your_yahoo_map_id"></script>
<body onload="initialize_ymap()">
 <div id="ymap"></div><h2>{{ location.place }}</h2>
<script type="text/javascript">
function initialize_ymap()                  
{
   var map = new YMap(document.getElementById('ymap'));
   var yPoint = new YGeoPoint({{ location.latitude }},
                             {{ location.longitude }});
   map.drawZoomAndCenter(yPoint, 15);
   var myMarker = new YMarker(yPoint);
   var myMarkerContent = "This is {{location.place}}";
   YEvent.Capture(myMarker, EventsList.MouseClick,
      function() {  
          myMarker.openSmartWindow(myMarkerContent);   
       }
   map.addOverlay(myMarker);
 }
 </script></body>

So when you search for a location you can see that location on a map and proper name of the location. This data is returned by yahoo geo data search. Now our next objective would be to to make a user checkin in to that location. So how can we do that? Pretty simple all we need to do is add a checkin button to the page which would send all the location data and logged in user to a view. Hmm sounds pretty simple, but how would you send the data to a view?

1. Through the url as arguments to a view.
2. Through a form

The 2nd option sounds great. But adding a form and making user fill in the data sounds stupid. So we need to populate the form with data while rendering the form. How about make the form invisible too? Yay!! that even sounds better. Ok a quick search on Django hidden forms showed up a djangosnippet. I could have used a widget with hidden input field. But I chose to use this method. Now lets open the forms.py and create a checkin form which inherits the 'HiddenBaseForm'.

class HiddenBaseForm(forms.BaseForm):
    def as_hidden(self):
        output = []
        for name, field in self.fields.items():
            bf = BoundField(self, field, name)
            output.append(bf.as_hidden())
        return u'\n'.join(output)

class CheckinForm(HiddenBaseForm, forms.Form):
    place = forms.CharField()
    latitude = forms.FloatField()
    longitude = forms.FloatField()

Now add the following snippet to the checkin.html file. This form is going to be invisible to the user except for the 'Checkin' button. When a user clicks checkin the form data is sent to a view. In this case it is 'checkin' method. Observe the url in form action, it decides which view is going to handle this.

 <form action="/locations/checkin/" method="POST">
 <input type="hidden" name="place" value="{{ location.place }}" id="id_place" /> 
 <input type="hidden" name="latitude" value="{{ location.latitude }}"  id="id_latitude" /> 
 <input type="hidden" name="longitude" value="{{ location.longitude }}" id="id_longitude" />
 <p><input type="submit" value="Checkin &rarr;"></p></form>

Ok now open up the urls.py and make an entry for '/checkin/' url. Route this url to the view 'checkin'.

 (r'checkin/$', 'locations.views.checkin'),

Now fire up the views.py and create the checkin view.

@login_required
def checkin(request):
    if request.method == 'POST':
        checkin_form = CheckinForm(request.POST)
        if checkin_form.is_valid():
              c = Location(place=checkin_form.cleaned_data['place'], 
                  latitude=checkin_form.cleaned_data['latitude'],
                  longitude=checkin_form.cleaned_data['longitude'], 
                  user=request.user, time_checkin= datetime.datetime.now())
              c.save()
              return HttpResponseRedirect(reverse('locations.views.your_locations'))
    else:
         return HttpResponseRedirect(reverse('locations.views.new'))

The view is pretty self-explanatory. I am protecting this view to logged in users. I am extracting the data from request.POST and then checking if the form.is_valid(). If it is valid I am creating an object which is an instance of the Location model. Then I populate the instance with userid, location name, latitude, longitude and current datatime. I save the object and redirect the user to show all the checkins of that user.

Now create a view 'your_locations' which shows all the checkins of the user.

def your_locations(request):
    user = request.user
    locations = Location.objects.filter(user=user)
    return render_to_response("locations/your_locations.html", {"locations": locations},  
      context_instance=RequestContext(request))

I am filtering all the location objects by the logged in user and then passing all the location objects of that user to the template your_locations.html. Do create an entry in urls.py to serve this view.

(r'^$', 'locations.views.your_locations'),

Now the your_locations.html will iterate over the set of locations and print the attributes. If you want to display the current checkin of the user. You can create if loop in the parent for loop and check if it is the first iteration of the for loop.

{% for location in locations %}
{% if forloop.first %}
     // Put all the Yahoo map javascript code here with 
     // {{ location.latitude }} and {{ location.longitude }}

I guess that's it you now have a working skeleton of brightkite. You can enhance this app and submit it to me. In case you want to check out my code or submit issues please use the issues list at django-locations. Discussions are welcome.

Comment Form

MarkDown syntax enabled

7 comments:

  1. 001// Yashh// Wednesday, 27th August, 2008, 3:42 p.m.

    Since no one is commenting, I decieded to add one up.

  2. 002// sean// Saturday, 30th August, 2008, 6:27 a.m.

    can it support the feature like djangopeole.net that visitor could navigator the map with mouse and get the particular location from the map?

  3. 003// Yashh// Saturday, 30th August, 2008, 8:18 a.m.

    @Sean

    Right now the answer is "no". But I had this feature on mind. Need to figure out the javascript behind the scene. If you know about on how to achieve this , do please share.

  4. 004// yashh// Wednesday, 8th October, 2008, 3:18 p.m.

    I decided not to implement the djangopeople.net feature as clicking on a map will not be able to give a precise location of the user. I prefer it to go through the search process only.

  5. 005// Alexander Mikhalev// Saturday, 14th March, 2009, 8:29 a.m.

    I think there is an error in javascript location.js, openSmartWindow works only for one marker. I had to wrap it in separate function as on yahoo examples in order to have separate notes for each marker.

  6. 006// Yashh// Saturday, 14th March, 2009, 9:13 p.m.

    @Alexander Mikhalev. Thank you for pointing out the error in javascript. I am not very good at javascript, I tried to get it work, but never really looked in depth. Thanks for pointing out it would be great if you can create a ticket at http://code.google.com/p/django-locations issues and add the patch. Thanks

  7. 007// kunti// Friday, 5th June, 2009, 3:40 p.m.

    nice buddy

thnknsblvng