Alex Kehayias's Blog

  • Archive
  • RSS

How to make an interactive bookmarklet part 3

In the last two parts of this series we learned about the framework for building bookmarklets and how to use javascript with django templates for optimal maintainability. Now let’s look at how to make interactions work without needing any external javascript libraries. 

Why not include Jquery

My guess is that all you really want are event bindings and for that you don’t need an entire library. Granted, lots of user’s have jquery cached in their browser already, but if not you have to include that which will make your bookmarklet slower. Every library you add will add more “weight” to the overall bookmarklet. Read the next paragraph before you decide you need the whole jquery library or you can make do with 6 lines of pure javascript.

Binding Events to Handlers

So you want to be fancy and make the bookmarklet change when the user clicks on something? Tough shit. Just kidding. The problem with event handling is that making it work cross-browser is tough. Jquery helps make writing js more cross-browser compatible and that’s a huge part of it’s overall value.

Here’s how we can add event handlers that will work across all browsers without Jquery. I can’t find where I originally found this from, but when I do I’ll add an attribution link.

    var bindEvent = function(elem, evt, cb) {
        //see if the addEventListener function exists on the element
        if ( elem.addEventListener ) {
            elem.addEventListener(evt,cb,false);
        //if addEventListener is not present, see if this is an IE browser
        } else if ( elem.attachEvent ) {
            //prefix the event type with "on"
            elem.attachEvent('on' + evt, function(){
                //use call to simulate addEventListener
                cb.call(event.srcElement,event);
            });
        }
    };

Now we have a simple way to make interactions happen within the bookmarklet itself. For example:

    // Remove the bookmarklet from the document assuming b is body and c is the bookmarklet
    var cancelDialog = function(e){
        b.removeChild(c);
    };
    bindEvent(cancelButton,"click", function(e){
        cancelDialog(this);
    });

All that without including the whole jquery library! That will keep your bookmarklet lightweight and fast. 

Submitting a Form

Using our event binding snippet above we can create a simple handler for submitting a form free from cross-domain errors. Those of you familiar with jsonp will see that this is very similar, but done manually and in a simpler way.

    // Assuming we have the following in the script
    var d=document,
    s=d.createElement("scr"+"ipt");
    b.appendChild(s);
    
    // Bind a submit event to a button click
    var submitArticle = function(e){
        // get the values of any form data you need
        // URI encode the variables as GET parameters
        s.src = "http://www.urltoyourservice.com?var1=" + encodeURIComponent(variable1) + "&var2=" + encodeURIComponent(variable2);
    };
    
    bindEvent(submitButton,"click", function(e){
        submitArticle(this);
    });

This will make a GET request to your your server with the parameters of your form. We can now process that request and return a response just like we did to initialize the bookmarklet in the first place. All we need to do is respond back with javascript. You can do this numerous times. All your doing is manipulating the bookmarklet that is already a part of the document. Make sure that you clean up after yourself (see my earlier example of binding a cancel button to removing the bookmarklet from the document). 

There you have it. An interactive bookmarklet that’s fast, maintainable, and lightweight. You can make changes to it at any time since the initialization happens server-side with familiar templates. You can make cross-domain requests without any errors. You can even bind events and handle them all without needing Jquery. 

Enjoy!

    • #javascript
    • #bookmarklet
    • #python
    • #django
  • 1 month ago
  • Permalink
  • Share
    Tweet

How to make an interactive bookmarklet part 2

Let’s talk about how to generate a bookmarklet using server-side templating and pure javascript (no libraries!). In part 1 we discussed the basic framework for making a bookmarklet and performing requests directly from the client’s page. Now we’ll see how to set up the foundation for a bookmarklet UI and make it look good.

Building a Bookmarklet Template in Django

Let’s create a file called bookmarklet.js. In it we are going to add a self executing piece of javascript and leverage django templates to fill in the information we need. In here we are going to build the response using nothing but javascript and send it back to the user who requested the bookmarklet (remember all they did was request it to be initialized, see part 1 for more info). With django templates we can do some pretty handy things so that you don’t need to manually write out the template as a string in javascript. For example:

javascript:(function(){
    var d=document,
    {% comment %}Create the elements we need {% endcomment %}
    c=d.createElement("div"),
    b=d.body,
    l=d.location;
    {% comment %} server_scheme_and_netlog is my own tag which returns the base url of the website{% endcomment %}
    c.id="bookmarkletArticleAlertBox";
    {% comment %}This is where django templates get's awesome{% endcomment %}    
    {% comment %}Make sure the html rendered has no breaks{% endcomment %}    
    c.innerHTML = '{% spaceless %}{% include "bookmarklet_body.html" %}{% endspaceless %}';
    {% comment %}Add the elements to the dom{% endcomment %}
    b.appendChild(t);
    b.appendChild(c);

....snip....

What we did was construct a div that will hold our bookmarklet UI and using django templates (the {%include … %} part) we can template the html easily, inheriting the context of this view. Please make sure to add the “spaceless” tags here because the innerHTML will fail if there are line breaks and I’ll be damned if I have to add linebreaks to my template. Also be extremely picky with your quotes as that can also lead to disaster.

Now in our “bookmarklet_body.html” we can create our UI just as if it were any other django template (almost). For example:

{% load static %}
{% load url_tags %}
{% comment %}NOTE Make sure you ONLY use double quotes!{% endcomment %}    

<div id="bookmarkletAssistant">
    <img src="{% server_scheme_and_netloc %}{% get_static_prefix %}images/assistant.png" style="border: 2px solid #666666;"/><br/>    
    <img id="bookmarkletLoadingImage" src="{% server_scheme_and_netloc %}{% get_static_prefix %}images/bookmarklet-ajax-loader.gif" style="display:none;"/>
</div>
<div id="bookmarkletResponse">
    <p>Hey Chief, almost ready!</p>
    <div id="bookmarkletPictureSelector">
        <p>
            <label>Select Picture:</label><br/>
            <div id="bookmarkletImageContainer">
                <img id="bookmarkletCurrentImage" />
                <a id="bookmarkletSelectPrev">&laquo;</a><a id="bookmarkletSelectNext" >&raquo;</a>
            </div>
        </p>
    </div>
    <p id="bookmarkletImageCheckboxContainer"><input id="bookmarkletImageCheckbox" type="checkbox" style="margin-right: 5px;" />
    <label>No Picture</label></p>
    <p>
        <label>Editor&rsquo;s Note:</label><br/>
        <textarea id="bookmarkletTextArea" placeholder="Add your commentary on the article here. (optional)"></textarea>
    </p>
    <input id="bookmarkletSubmit" type="submit" value="Publish Article" />
    <a id="bookmarkletCancel">Cancel</a>
</div>

Notice how all the elements are contained within elements with IDs. This is extremely important so you can clean up after yourself and not pollute the dom. You will also need to do this so that subsequent interactions can find your bookmarklet and update the contents. BTW this is exactly what is used in Edorati. 

Make sure that you have a view set up so that you know it is from your bookmarklet and can respond with your javascript template. When the user initializes the bookmarklet (see part 1 for reference) they will be requesting a url that you control. All you need to do is map that url to a view function and do the following:

    variables = {'some_var': 'blah blah'}
    resp = render_to_string('bookmarklet.js', variables)
    return HttpResponse(resp, mimetype="text/javascript")

This will make sure we are responding with javascript and not html. The javascript will be executed as soon as it is received by the user. It’s actually very fast and should feel close to instantaneous. 

Making it Look Pretty

Plain unstyled html would be a terrible idea for a bookmarklet. Keep in mind that you are injecting onto another page where their stylesheet is unknown. That means we need to style and reset the css for the bookmarklet or it will not look consistent across sites. Here’s how:

    // in bookmarklet.js
    t=d.createElement("link"), 
    t.rel="stylesheet";
    t.media="screen, projections";
    t.type="text/css";
    t.href="{% server_scheme_and_netloc %}{% get_static_prefix %}stylesheets/bookmarklet.css";

Whoa, dynamically loading css just for our bookmarklet! Nothing fancy going on here, just using plain ol’ css to beautify our bookmarklet. Just make sure that you do not inadvertently style the rest of the elements on the page with your stylesheet. As mentioned before, add an ID for all major elements and when it comes to css, use the ID selector to style the element instead of the element selector or class. If you have to use classes just obfuscate the name so there is little possibility of you impacting the page i.e bookmarklet-textarea-field vs. textarea.  

Next Week

Next week we’ll go over how to make the bookmarklet come alive with interaction through event binding and requests using about 15 lines of pure javascript and no libraries.

    • #django
    • #bookmarklet
    • #javascript
  • 1 month ago
  • 2
  • Permalink
  • Share
    Tweet

Adding a UUID Field to an Existing Django Model

It’s not immediately obvious how to add a UUID field to an existing Django model. Let’s say you have a database with some data in it about certain accounts, but you realize you need a way of obfuscating the pk of a model instance. UUID is a perfect candidate and a pretty popular implementation for Django comes from dcramer called django-uuidfield. Everything is fine and dandy until you try to add that field to an existing model that already has data, then try to auto migrate it using south. 

How to add a UUID field to an existing model

The problem here is that we need to add a UUID value to all existing records, not just new ones. Peaking through the source code you will see what uuid method they are using and what the data should look like.

  1. Add the UUID field to your model with blank=True, null=True, max_length=32, auto=True in models.py.
  2. Run the schemamigration command with south and then open up the resulting migration file. 
  3. Edit your migration file with the following:
# You'll need to import the following
import uuid

# At the end of the forwards() function add the following
def forwards(self, orm):
    ...
    for a in MyModel.objects.all():
        a.uuid = u'' + str(uuid.uuid1().hex)
        a.save()

That will loop through all the existing instances of that model in your database and add a uuid to it as part of the migration. Make sure you test it first!

    • #django
    • #uuid
    • #python
  • 1 month ago
  • 1
  • Permalink
  • Share
    Tweet

How to make an interactive bookmarklet part 1

Here’s how to put together a non-trivial interactive bookmarklet. For some reason there are not too many resources on the subject, but it has become a much more popular way of user’s interacting with a service without having to be on the site. Pinterest, Evernote, etc. all rely heavily on the ease of use of bookmarklets. 

For Edorati, I needed to create a simple, fast way for user’s to publish an article to their online newspaper whenever they came across something they liked. It also had to look nice and allow them to select a picture thumbnail and add an editor’s note. Oh yeah it also needs to be upgradeable from the server side so that the user doesn’t have to reinstall it every time you want to add something new. 

Why is it so hard?

Bookmarklets are especially hard for a couple reasons:

  1. You don’t own the page the bookmarklet will deploy on
  2. Cross-domain requests
  3. Maintaining some level of security
  4. Everything needs to be manipulated through javascript
  5. Avoid using full js libraries like jquery

What’s the plan?

I ended up building a simple framework for bookmarklets and that uses django to process the request, handle errors, and provide responses. The bookmarklet is actually just a small piece of javascript code that gets executed as soon as it is clicked. It will tell my server that it’s ready to be initialized for a particular user. Then the server will make a response with ONLY javascript that is contained in a self executing function which will manipulate the target page the user is on. From there any interactions are a matter of building a request on the client side or returning a javascript response from the server-side. Sound simple? Not really…

How do I make cross-browser requests, do I use Jquery?

No Jquery! We’re going to avoid all libraries and roll our own. Adding big libraries will make your bookmarklet slow and you probably only need a couple of things from it. All we need in this case is a small workaround to make cross-browser requests right from the user’s page. This is the pattern:

javascript:(function(){
    var d=document,
    // Create a script element
    s=d.createElement("scr"+"ipt"),
    // Set the src to an endpoint on your server
    s.src = "http://www.urltoyourapi.com/?param1=something&param2=somethingelse";
})()

Now your server can respond with javascript to fill in that script element that was just created on the user’s page. By adding some GET parameters to the end of the URL you also have a way of communicating information about the user or the page. For example you may want to construct the url in a way that it passes the users obfuscated ID number so you can save information to that user’s account. This is all a bookmarklet is. 

Making it future proof

To prevent your users from having to install the bookmarklet every time you want to make a change, you should not include any presentation or interactive logic in the bookmarklet. For Edorati, all I need to know from the bookmarklet is who is the user. All the bookmarklet needs to do is essentially tell the server that it wants to be initialized. Everything else should happen on the server side. That way you can make any changes you want to the look or the functionality without ever having to change the user’s bookmarklet that they installed. 

Next post will discuss constructing templated javascript on the server side to make a bookmarklet interactive, clean, and safe…

    • #bookmarklet
    • #django
    • #development
    • #javascript
  • 2 months ago
  • 2
  • Permalink
  • Share
    Tweet

Add fields to a Django serializer

Here’s a quick and dirty way to add fields to a serializer. I wanted to add in the absolute url to the json data that was being sent back during an ajax request. I wouldn’t recommend it for large querysets since you are essentially adding a json load and another dump, but for a single model instance I think it’s ok. The alternative would be to use a library like wadofstuff-django-serializer, but I really want to avoid a dependency for just a single purpose.

import simplejson as json
from django.core import serialize
# Somewhere in your view
# Note that I'm only serializing a single instance hence the tup
resp = serializers.serialize("json", (article,))
# Add absolute url to
resp_obj = json.loads(resp)
resp_obj[0]['fields']['get_absolute_url'] = article.get_absolute_url()
r = json.dumps(resp_obj)
# Then respond back as usual with your updated json object
    • #django
  • 2 months ago
  • Permalink
  • Share
    Tweet

Cohort Analysis in Django

I wrote a simple reusable app for doing cohort analysis in Django. Cohort analysis is an incredible tool for deflating your ego. Since it compares apples to apples you will see if you are actually helping, hurting or, more than likely, making no impact at all on your users with the iterations you’ve been making on you’re awesome web app. 

What is Cohort Analysis?

Cohort analysis involves segmenting your users into smaller groups so you can compare measurements on them. For example, a cohort could be the user’s that signed up for your app in a week. So all the people that joined last week is a group, everyone who joined two weeks ago are in a group, etc. Now we can analyze actions that the cohort has taken in relation to the week they joined. For example “Users who joined the week of 2/1 performed 10% more action x in their first week than the users who joined in 1/25.” You can then think about the changes you made and correlate it back to the results. This prevents things like traffic spikes or that PR you did from messing up your numbers. You’ll be able to see over time if you are actually getting any better rather than looking at aggregate statistics of the activity of all your users. If you try hard enough with any aggregate stat you can make it look like a hockey stick. But that’s bullshit because you have no idea if the changes you made are the cause of that improvement when you look in the aggregate. 

Cohort Analysis in Django

So how do we do this in django? I wrote a very simple, reausable app called django-cohorts. The objective is to set up a simple framework for running cohort analysis defined by the segments (cohorts) you want and the metrics you want in a dashboard that lives in the admin section. It’s raw right now, but it’s a start. 

How does it work?

I’ve done the base cohorts by week, which is pretty common, but you can override that. In metrics.py, create a class with functions that will return a percentage. It will send you the users and you do some db lookup for certain actions the user took. After you’ve hooked it up to your url conf, you can then see the analysis with a dropdown of the metrics that are available.

Looking for Contributors

If anyone wants to help make this more sophisticated please let me know. Feel free to fork it on GitHub.

A Note about Cohort Analysis

Since cohort analysis typically looks at usage over time, you need to have some data for it to be useful. You can’t identify patterns that are significant without at least a few weeks of data. Just saying.

    • #cohort
    • #cohort analysis
    • #django
    • #python
    • #statistics
  • 3 months ago
  • 3
  • Permalink
  • Share
    Tweet

How to overwrite the save of a Django form like a pro

Use the “super” function to use the save method built into the form plus the extra things you need. Example:

class DebateMediaForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(DebateMediaForm, self).__init__(*args, **kwargs)
        self.fields['image'].required = True
        self.fields['source'].label = "Image Source"
        
    class Meta:
        model = MediaContent
        fields = (
            'image',
            'source',
        )
    def save(self, user, debate):
        media = super(DebateMediaForm, self).save(commit=False)
        media.added_by = user
        media.related_debate = debate
        media.content_type = "P"
        media.save()
        return debate

This is really handy for dealing with a ModelForm where you only want to have the user fill out a couple fields, but additional background information is required. In this case I need to specify who added the media. That’s something you don’t want your users to have to fill out, but you still want the luxury of using a model form because of the beautiful built in save function. Just super that shit and move on :)

    • #django
  • 4 months ago
  • 6
  • Permalink
  • Share
    Tweet

A/B Split Testing with Django

Dear internet startups, there’s no excuse not to A/B test your shit. You will learn horrible things about yourself. For example, that “really cool feature that you added that is so awesome people are going to be glued to my app,” doesn’t actually do anything to improve your core metrics. In fact they may be hurting them. Since it’s hard to test apples to apples you are flying blind. 

Don’t worry, I’ve done that too. It’s dumb now that I look back and I should have started testing everything sooner. I thought it would be too hard to do, to hard to implement, and slow down my lightning development skills (ha!). It’s worth the up front investment because you can help correlate the things you do to lasting results.

A/B test with django-lean

I don’t use Google’s wonderful website optimizer. It doesn’t help me do feature a/b tests. If you want to test the copy on your homepage then fine use GWO, but if you want to test that sweet feature you just added good luck trying to do that with optimizer. I use django-lean on just about all my projects to do real A/B tests that use a chi squared analysis to measure for lasting improvement changes. It makes it very easy to make experiments out of chunks of functionality you are using and figure out if what you are doing is moving the needle. Oh and you can run multiple experiments at the same time (that’s not multivariate testing, that just means multiple unique experiments).

Modifications

I only use the “experiments” app that’s in django-lean and remove everything else. I haven’t needed the other stuff in there and it’s not documented.

Setup

I put my modified version of django-lean into my project folder rather than installing it as a dependency in site-packages. I’ve made changes to it and probably will make more so I’d rather treat it like a project app. Follow their wiki to add the urls to your url conf and syncdb/migrate. 

It was really tricky figuring out just how to enroll visitors/users into an experiment. Once I figured it out I made a wrapper for it that can be added to any view that you want people to be added to an experiment. For me that’s just about every view. Here’s my decorator that wraps views to enroll the user in an experiment if they are not already.

# Note that I modified the paths from django_lean.experiments to experiments
from experiments.utils import WebUser

def set_experiment_user(target):
    '''Decorator for setting the WebUser for use with ab split testing
    assumes the first argument is the request object'''
    def wrapper(*args, **kwargs):
        request = args[0]
        WebUser(request).confirm_human()
        return target(*args, **kwargs)

    return wrapper

Now we can use this in our views like so:

# My wrapper is in lib.utils, but put it wherever you want
from lib.utils import set_experiment_user

@set_experiment_user
def home(request):
    '''Your view here as per usual'''
    return direct_to_template(request, 'index.html', locals())

Now anyone that goes to home will be enrolled in whatever active experiments I have going on. 

We also need to add the following to our templates. A script that attempts to figure out if you are a real visitor and not a bot and boilerplate stuff for the experiments:

<script language="javascript" src="{% get_static_prefix %}javascripts/experiments.js" type="text/javascript">
{% include "experiments/include/experiment_enrollment.html" %}

Creating and experiment

Experiments have two paths, control and test. In your templates you use the experiments tags to show chunks of code that are for the test or for the control in the experiment. 

{% load experiments %}
{% experiment experiment_name control %}
This is shown when the visitor is in the control group
{% endexperiment %}
This is shown when the visitor is in the test group
{% experiment experiment_name test %}

In your views you can route to the right code like this:

    if Experiment.test("experiment_name", WebUser(request)):
        # do some stuff knowing they are in the test group

Recording goals

We need to record the key actions the user makes so that we can compare them in the A/B test. These should be aligned with the core measurements you need to make to know what your users are doing. Don’t track goals that make no difference in figuring out if you are better engaging your customers. Focus on actions, for TimeoutDebate that means votes, opinions, and number of debate views. You add goals through the admin and can call them whatever you like. To record that a user has performed the action you do this:

from experiments.models import GoalRecord, Experiment

# In your view somewhere
GoalRecord.record("vote create", WebUser(request))
# This records the "vote create" goal for that experiment participant

Also you can do this using a tracking pixel that points to your django-lean url route (not the admin one). Simply add an img tag with the source of a url that points to your goal and it will record it just like the above. This is useful for putting it inside ajax interactions or other places that are not controlled by your django views. 

Engagement Calculator

This is an interesting calculation that gets added to the A/B report that is generated by django-lean. You basically write a calculation that returns an arbitrary number based on actions a user has taken that measures overall engagement with your system. I don’t like the example on the django-lean wiki because it doesn’t reuse the goals recorded to calculate the user’s engagement score. This example reuses it and awards points accordingly:

# Defines the engagement score for experiments
from experiments.models import GoalRecord

class EngagementScoreCalculator(object):
    def calculate_user_engagement_score(self, anonymous_visitor, start_date, end_date):
        """
        Defines the a user's engagement based on the actions they take
        Points awarded for certain actions.

        """
        # Sets the weighting for the engagement score
        weighting = {
            "signup": 10,
            "debate create": 0,
            "debate view": 1,            
            "opinion create": 10,
            "vote create": 5,
        }
        # Get all the tracking goals completed by them over the time period
        goals_completed_set = GoalRecord.objects.filter(created__range=(start_date, end_date), anonymous_visitor=anonymous_visitor)
        # Get a list of all the GoalRecord Names
        tracking_goals = {}
        for i in GoalRecord.objects.all():
            tracking_goals[i.goal_type.name] = 0
        # Count all the goals
        for i in goals_completed_set:
            if i.goal_type.name in tracking_goals.keys():
                try:
                    tracking_goals[i.goal_type.name] = tracking_goals[i.goal_type.name] + 1 * weighting[i.goal_type.name] # multiply by the weighting
                except:
                    tracking_goals[i.goal_type.name] = 0
        days_in_period = (end_date - start_date).days + 1
        # Sum up the total
        total = 0
        for key in tracking_goals.keys():
            total = tracking_goals[key] + total
        engagement_score = (float(total)/days_in_period)
        return engagement_score

Generate reports and make decisions

Update your experiment reports using the django-lean management command “python manage.py update_experiment_reports” Now when you look in the admin (mine is /admin/django-lean) you will see a list of experiments. When you click on it you will see the report with all the lovely statistical analysis. It even makes a check mark next to items that have a confidence interval above 95% (that means that it is 95% confident that the test version will have a lasting improvement). 

To make decisions, make sure you have a large enough sample size (number of people enrolled in your experiment in both the test and control group) and look at the confidence intervals, improvement, and engagement score. If it’s not conclusive just keep measuring it. Green is generally good, red is generally bad when it comes to the experiment report. The key here is that you’re making the decision based on real data, not your gut. You can disable your experiment at any time in the admin and “promote” the winning version. Just make sure you clean up your templates and views so it doesn’t get super cluttered with experiments. 

    • #django
    • #django-lean
    • #lean startups
    • #a/b testing
    • #split testing
  • 4 months ago
  • 3
  • Permalink
  • Share
    Tweet

Infinite Scroll with Django

Twitter style infinite scroll using Django as your backend is actually pretty simple. I did it for the TimeoutDebate archive page. You can break it down into these simple steps:

  1. Scrolling past a certain point triggers a jquery ajax event
  2. Django responds with a json object
  3. Insert the new data into the document, reload the scroll handler

That’s really not much different than any other ajax type of interaction. There’s no black magic (there rarely is) although I did need to look up how to actually capture this event. I found a good article about infinite scroll with django here, but ended up rolling my own backend handler. 

This is how you capture the event of scrolling past a certain point (via Palewire, the article I mentioned):

$(document).ready(function(){     
   $(window).bind('scroll', loadOnScroll);
});

// Scroll globals
var pageNum = 1; // The latest page loaded
var hasNextPage = true; // Indicates whether to expect another page after this one

// loadOnScroll handler
var loadOnScroll = function() {
   // If the current scroll position is past out cutoff point...
    if ($(window).scrollTop() > $(document).height() - ($(window).height()*2)) {
        // temporarily unhook the scroll event watcher so we don't call a bunch of times in a row
        $(window).unbind(); 
        // execute the load function below that will visit the JSON feed and stuff data into the HTML
        loadItems();
    }
};

var loadItems = function() {
    // If the next page doesn't exist, just quit now 
    if (hasNextPage === false) {
        return false
    }
    // Update the page number
    pageNum = pageNum + 1;
    // Configure the url we're about to hit
    $.ajax({
        url: '',
        data: {page_number: pageNum},
        dataType: 'json',
        success: function(data) {
            // Update global next page variable
            hasNextPage = true;//.hasNext;
            // Loop through all items
            for (i in data) {
                $("#newItems").before(
                // Do something with your json object response
            }
        },
        error: function(data) {
            // When I get a 400 back, fail safely
            hasNextPage = false
        },
        complete: function(data, textStatus){
            // Turn the scroll monitor back on
            $(window).bind('scroll', loadOnScroll);
        }
    });
};

For you html markup all I need to add is an anchor div with an id of “newItems” and include the infinite scroll script above. 

And this is my view that handles the initial page and the ajax infinite loading:

def debate_archive(request):
    user = request.user
    debates = [i for i in Debate.objects.all().order_by("-id") if i.opinion_set.count() >= 2]
    paginator = Paginator(debates, 10)
    if request.method == 'GET':
        if request.is_ajax():
            if request.GET.get('page_number'):
                # Paginate based on the page number in the GET request
                page_number = request.GET.get('page_number');
                try:
                    page_objects = paginator.page(page_number).object_list
                except InvalidPage:
                    return HttpResponseBadRequest(mimetype="json")
                # Serialize the paginated objects
                resp = serialize_debates(page_objects)
                return HttpResponse(json.dumps(resp), mimetype='json')
    debates = paginator.page(1).object_list
    return direct_to_template(request, 'debate_archive.html', locals())

I reuse Django’s built in Paginator class to know that I am serving back a certain sub set of the entire list of objects. It’s like pagination without pagination that happens automatically as you scroll! When I’m out of items I respond with a 400 which calls the error function in the ajax request which turns off the infinite scroll.

A couple things I noticed:

  • If you only add a small piece of additional content, this method may result in a ton of ajax requests. This has to do with the event being bound to the user reaching a certain area by scrolling. Since not much additional length was added it keeps triggering the event when scrolling. I’m sure you can find away around that.
  • You don’t need a separate url conf to set this up, just handle it in the same view using the is_ajax() function to determine if it’s from the infinite scroll. Add a some data to the GET ajax request and parse it in the django view. Keeps it all very tidy in my opinion.

This is what powers the infinite scroll found here: http://www.timeoutdebate.com/debate/all/

    • #django
    • #jquery
    • #infinite scroll
  • 4 months ago
  • 21
  • Permalink
  • Share
    Tweet

This is why I love Flask

from flask import Flask
from flask import render_template, url_for
app = Flask(__name__)

@app.route('/')
def homepage():
    return render_template('index.html')

if __name__ == '__main__':
    app.run(debug=True)

url_for('static', filename='/stylesheets/compass_foundation.css')

Cool! This is a single page app using Flask. Oh and you can run a test server with “python <name_of_file>.py”. Don’t need no full-blown feature heavy framework, no stinkin ORM, no form handling, authentication, etc for this simple project. Somehow I feel cleaner using Flask… How much of Django am I actually utilizing? I’m beginning to see the light about python web microframeworks.

    • #flask
    • #python
    • #microframework
    • #django
  • 4 months ago
  • 14
  • Permalink
  • Share
    Tweet
← Newer • Older →
Page 1 of 2

About

Blogging about hacking code, life, and music.

Me, Elsewhere

  • @alexkehayias on Twitter
  • Facebook Profile
  • Linkedin Profile

Twitter

loading tweets…

I Dig These Posts

  • Photo via marksbirch

    Every so often in my web ramblings, I come across an interesting tidbit. While not meant to be an endorsement of Automattic, I find it interesting...

    Photo via marksbirch
  • Post via franciscohui
    Some thoughts on pacing your life

    We live with contradictions. Which are okay for the most part, except when we’re not aware of them. It is a source...

    Post via franciscohui
  • Photoset via lifeandcode

    hasablog:

    GitHub stickers! Awesome gift :]

    Photoset via lifeandcode
  • Photo via franciscohui

    skewed. (via Porca Miseria)

    Photo via franciscohui
See more →
  • RSS
  • Random
  • Archive
  • Mobile

Effector Theme by Carlo Franco.

Powered by Tumblr