Thanks for all the contributions! My startup (Fashiolista) depends on a reliable Facebook integration and maintaining it would not be possible without all the pull requests from the community. Contributions are strongly appreciated. Seriously, give Github a try, fork and get started :)
Django Facebook enables your users to easily register using the Facebook API. It converts the Facebook user data and creates regular User and Profile objects. This makes it easy to integrate with your existing Django application.
I’ve built it for my startup Fashiolista.com and it’s currently used in production with thousands of signups per day. For a demo of the signup flow have a look at Fashiolista’s landing page (fashiolista.com)
After registration Django Facebook gives you access to user’s graph. Allowing for applications such as:
Django Facebook helps you quickly develop Facebook applications using Django.
Let me know what features or issues you are encountering!
This change broke the caching for the convert code step for Django Facebook, breaking login, connect and registration functionality. Fortunately this was quickly reported by developers in a country where there was no Queensday yesterday.
I encourage everybody to update to 5.0.13 to make sure your Facebook integration keeps on working.
Share and Enjoy:
Fashiolista has built up quite some experience with tweaking this and today we’ll be sharing some tips. The actual code we’re using has been included in the latest version of Django Facebook.
There are several advantages of storing your open graph shares in the database. You get:
Especially the ability to retry shares was important to us. Django Facebook provides a convenient OpenGraphShare model to store all your shares in. Have a look at the example below:
class DjangoModel:
def share_to_facebook(self, graph=None):
from django_facebook.models import OpenGraphShare
#this is where the magic happens
share = OpenGraphShare.objects.create(
user_id=self.user_id,
action_domain='fashiolista:love',
content_type=content_type,
object_id=self.id,
)
share.set_share_dict(kwargs)
share.save()
result = share.send()
Note that we store all info required for sharing to Facebook in the OpenGraphShare model. The actual Facebook API request is sent when you call share.send().
Using this flow has the benefit that Django Facebook will retry your open graph shares if a user’s token updates. By doing this more of your open graph shares will actually reach Facebook.
Facebook’s has deprecated offline access. By default you will now get a short lived access token (usually 1-2 hours). Often you will want longer lived tokens though. For example when your app keeps a user logged in for 30 days, storing a token once with a duration of 2 hours doesn’t get you very far. If you need longer lived tokens, you can now extend your token via a Facebook API call. These long lived tokens will keep working for 60 days.
The high level API for extending tokens is located in:
user.get_profile().extend_access_token()
In the new version of Django Facebook your token will be automatically extended upon connecting with Facebook. There is however a performance overhead to this extra API call. That’s where the following point comes in:
Calling the Facebook API is something which takes quite a bit of time. If you’re unlucky a single API call can easily take up to 2000 ms. Celery is a tool which allows you to easily run tasks in the background, ensuring your pages stay fast for the users. At Fashiolista we use tasks for checking comment spam, adding loves, clearing cache, posting to Facebook and extending your Facebook access tokens. You can learn more about Celery in this guide.
Setting up Django Facebook to use Celery is trivial. Simply set the following settings to True.
#Use celery for storing friends or likes FACEBOOK_CELERY_STORE = True #Use celery for extending access tokens in the background FACEBOOK_CELERY_TOKEN_EXTEND = True
Using Celery speeds things up enormously for the end user, so I recommend using it.
Upgrading to the latest version of Django Facebook and implementing these tips will get you a very solid open graph implementation. Let me know in the comments what steps you’ve taken to get these numbers up.
Share and Enjoy:
The presentation starts by introducing Fashiolista.
It still amazes me and 3 other guys started Fashiolista and grew it to the 2nd largest online Fashion community.
I guess Marc Andreesen had a point in his “Why Software Is Eating The World” article.
The talk focuses on how we use metrics to drive optimization at Fashiolista.
And narrows down on tools like:
If you’re looking up the links from the presentation, these are the most notable ones:
Share and Enjoy:
Cheers,
Thierry Schellenbach
CTO/ Founder
Fashiolista
Posting these actions on Facebook can greatly enhance the viral growth of your company. Spotify, one of the launch partners, has grown by more than 4mil active users. This blogpost explains how to use Django Facebook to integrate your Django based site with Facebook.
Before going into the code though we need to understand the basic concepts of the open graph: actions and objects. Facebook’s getting started interface explains it best:
Actions are the verb, objects are what the action is applied to. You submit the data for these actions and objects to Facebook. An example would be watching (action) the movie Pan’s Labyrinth (object). Every object should be represented by a dedicated URL. For instance if you look at Chiara’s profile (object) on Fashiolista you will see the following open graph data:
<meta property="og:title" content="ChiaraFerragni" /> <meta property="og:image" content="http://bit.ly/rso3ig" /> <meta property="og:type" content="fashiolista:fashiolista" /> <meta property="og:site_name" content="Fashiolista" />
To post to the open graph you specify an action and the url for the object. You can read more about it in Facebook’s open graph docs.
Users only need to give you permission to the open graph once. You specifically need the publish_actions permission. When asking for this permission a pretty permission interface as shown below will appear:
Notice the tiny add to timeline box displayed at the bottom right corner of the interface.
The code below will show you how to post to the open graph. There are however a few requirements which you’ll need to go through.
After receiving this permission we can post to the user’s timeline. Below is the most basic example:
@facebook_required(scope='publish_actions')
def open_graph_beta(request):
'''
Simple example view on how to do open graph postings
'''
fb = get_persistent_graph(request)
entity_url = 'http://www.fashiolista.com/item/2081202/'
result = fb.set('me/fashiolista:love', item=entity_url)
messages.info(request, 'The item has been shared to fashiolista:love')
This posts the action love to the users timeline with the object data found at the url http://www.fashiolista.com/item/2081202/.
The syntax me/fashiolista:love translates to post to me in the namespace fashiolista with the action love.
While the above example works, you often won’t want to keep your users waiting for the Facebook API request. Especially since they are quite sluggish at the moment.
Below there’s a version of the code using celery. Celery is a task queuing system which enables you to run tasks asynchronously.
@task.task(ignore_result=True)
def open_graph_beta(user):
'''
Example posting to open graph using a celery task
'''
profile = user.get_profile()
fb = profile.get_offline_graph()
entity_url = 'http://www.fashiolista.com/item/2081202/'
result = fb.set('me/fashiolista:love', item=entity_url)
Integrating with open graph is easy. Head over to the github page of Django Facebook to get started. Contributions are more than welcome.
Do you also see the beauty in clean code? Are you experienced with high scalability web apps? Currently we’re looking for additional talent over at our Amsterdam office. Feel free to drop me a line at my personal email for more information: thierryschellenbach[át]gmail.com
Share and Enjoy:
Much of the new found simplicity comes from the facebook_required decorator. This decorator is similar to login_required, but instead checks the Facebook permissions given to you. Writing the input of a form to someone’s wall is now as simple as this:
@facebook_required(scope='publish_stream')
def wall_post(request):
fb = get_persistent_graph(request)
message = request.POST.get('message')
fb.set('me/feed', message=message)
messages.info(request, 'Posted the message to your wall')
return next_redirect(request)
Another example would be uploading some photos to a user’s timeline:
@facebook_required(scope='publish_stream,user_photos')
def image_upload(request):
fb = get_persistent_graph(request)
pictures = request.POST.getlist('pictures')
for picture in pictures:
fb.set('me/photos', url=picture, message='the writing is one The '
'wall image %s' % picture)
messages.info(request, 'The images have been added to your profile!')
return next_redirect(request)
As you can see the syntax is very straightforward. You no longer have any technical excuse against integrating Facebook.
More examples and installation instructions can be found on Django Facebook’s github.
In the last few years the landscape for online marketing has shifted towards Facebook. The most obvious point is Facebook’s own ad platform, which currently exceeds 2 billion dollar in revenue. However the more fundamental changes are caused by Facebook’s social platform. This article outlines the 5 ways in which Facebook is redefining online marketing.
About Thierry Schellenbach: Co-Founder of Fashiolista.com, author of Django Facebook. Follow me on Twitter or Facebook
Firstly Facebook allows you to target your advertising to specific interest groups. Especially for community driven sites it’s now possible to reach your target audience with almost scary precision. A marketeer can target all single girls living in Amsterdam, which like snowboarding and work at Google. This improved targeting enables you to attract visitors which are more likely to use your product.
The Facebook effect continues when users reach your landing page. Instead of filling out a form with annoying questions, users just need to find the big blue button on your site and they are good to go.
Having a Facebook login flow on your site drastically increases your conversion to members. Also the Facepile social plugin shows you which of your friends are using the site you’re visiting. This creates another incentive for users to join your site.

A good example of a zero friction signup flow is the Fashiolista landing page as shown above.
The first two points allow you to acquire more users at a substantially lower member acquisition cost than previously possible. In addition the user accounts created by the Facebook connect flow immediately contain very rich information. You know their birthday, their age, their email, their gender and more importantly their real name. Using real names on online platforms substantially reduces spam and trolling. Furthermore since you know the user’s Facebook id you can connect them to their friends.

Pinterest for instance makes it extremely easy to follow your Facebook friends on Pinterest.
Facebook started focusing on likes about a year and a half ago when they released the open graph protocol and like buttons. Currently many users have indicated which sports, movies and brands they like. You can utilize this data to immediately connect the users to topics they care about.

Quora is the leading example on how to personalize your content to new users. When you join Quora topics will be suggested to you based on your interests. So someone interested in Entrepreneurship will see entirely different content than someone interested in gardening.
Some of these users you just acquired will absolutely love your product. (Stop marketing and improve your product if they don’t). Previously these users would maybe email a friend or two and if you’re lucky tell their colleagues about it. Facebook however empowers them to easily share your product to all of their friends. Evangelists which would maybe recruit two new members are enabled by Facebook to recruit dozens. It is very common for user’s of Fashiolista to invite more than 30 of their friends, many of which become members.

The invite flow on Fashiolista smartly shows your female friends, which are not yet on Fashiolista and enables you to quickly send an invite message to their wall.
Similar, albeit less pretty invite flows are a strong component of games like CityVille and Farmville.
Facebook has so far reduced the cost of getting visitors to your site. In addition it has become easier for these visitors to become members and receive a personalized experience.
Finally Facebook empowers your evangelists to reach more of their friends than was previously possible. Throughout these steps the cost and friction involved with recruiting new members have been substantially reduced.
On September 22nd Facebook will make several announcements at their F8 conference.
Have a look at the livestream to find on how they will change online marketing again.
About Thierry Schellenbach: Co-Founder of Fashiolista.com, author of Django Facebook. Follow me on Twitter or Facebook
Share and Enjoy:
Over the past months Fashiolista has grown to be one of the largest fashion communities worldwide. Dividing attention between scaling the site and creating new features has been quite a challenge. We are looking for a very experienced django developer to join our team.
Fashiolista is best described as “twitter for fashion”. Girls follow each other’s fashion finds and indicate which items they love. We are funded by Atomico ventures and several high profile angel investors.
- Young team, startup culture
- Developers in the founding team
- The team has been working with Django for more than 4 years
- Modern stack (Django 1.3, postgres, redis, memcached, jquery, celery, solr, GIT)
- Learning culture
Did I mention we are located in the centre of Amsterdam?
We are looking for a talented Django developer with experience in building highly scalable applications. Since our team is small, strong experience with Linux or Freebsd system administration would be a big plus.
Bonus points for:
For this position you need to be willing to work in The Netherlands/ Amsterdam. Furthermore the position is full time.
We are looking for someone with an excellent work attitude and considerable experience. It doesn’t matter if you are a student, cum laude graduate, or drop out, as long as you’re extremely passionate about what you do. You will be working in a startup and we expect your input on a wide range of challenges.
If you recognize yourself in the above description or would like more information please contact me at:
thierry…fashiolista.com
In addition to the required migrations, this version also includes several new features. The largest change is the underlying Facebook API client. You can read about the improvements here or skip down to the upgrade instructions.
Open Facebook is a python client to the Facebook graph API. Currently it’s included in Django Facebook, but in time it will be spun of as a separate project. The client is supported, tested and up to date with the current version of Facebook’s graph API.
It handles connection issues cleanly and raises separate error classes for various Facebook error scenarios.
The syntax is very straightforward to use:
open_facebook = OpenFacebook(token)
#info about me
open_facebook.me()
#retrieving a page
open_facebook.get('fashiolista')
#retrieving your albums
open_facebook.get('me/albums')
#posting a message on your wall
open_facebook.set('me/feed', message='Check out Fashiolista', picture=image_url)
#setting a like
open_facebook.set('fashiolista/likes')
#fql example
result = open_facebook.fql('SELECT name FROM user WHERE uid = me()')
#authorization is handled separately from the API class
token = FacebookAuthorization.get_app_access_token()
test_user = FacebookAuthorization.create_test_user(token)
Uploading pictures to Facebook is as simple as this:
open_facebook = get_facebook_graph(request)
photo_urls = [
'http://d.fashiocdn.com/images/entities/0/6/t/p/d/0.365x365.jpg',
'http://e.fashiocdn.com/images/entities/0/5/E/b/Q/0.365x365.jpg',
]
for photo in photo_urls:
uploaded = open_facebook.set('me/photos', url=photo,
message='Fashiolista is awesome', name='FashiolistaTest'
)
So there are good reasons to upgrade even if Facebook didn’t force us to :)
First Step – Install Django Facebook 3.0
Follow the installation instructions.
Javascript changes
In your Facebook initialization code enable OAuth.
FB.init({appId: facebookAppId, status: false, cookie: true, xfbml: true, oauth: true});
Perms has been renamed to scope.
FB.login(function() {}, {'scope' : 'offline_access'});
Django Facebook changes
The get_facebook_graph function is no longer persistent by default, if you want the old behavior use get_persistent_graph.
Furthermore the FacebookUserConverter class and the core Facebook API client are now separated. get_facebook_graph returns the core facebook API client. To go from the API class to the user converter class use something like
graph = get_facebook_graph(request) facebook = FacebookUserConverter(graph)
That should be all.
If you are encountering problems feel free to let me know in the comments.
Last Step – Enable the new Facebook Migrations
Go to the Facebook Apps page and enable the OAuth migration.
Select your app -> About -> Advanced -> Migrations
Enable Force OAuth
Enable OAuth Migration
Moving to OAuth 2.0 + HTTPS
Updated JavaScript SDK and OAuth 2.0 Roadmap
New Facebook Authentication Docs
PHP SDK v.3.1.1
PHP SDK v3.0.0
Do you also see the beauty in clean code? Are you experienced with high scalability web apps? Currently we’re looking for additional talent over at our Amsterdam office. Feel free to drop me a line at my personal email for more information: thierryschellenbach[at]gmail.com
Share and Enjoy:
One of the strong points of Django Facebook is the ability to register users using Facebook. It ports all of Facebook’s user data to the Django user and profile models. This allows you to have a secure register/connect/login flow using Facebook, greatly reducing the barriers to start using your application. Below an example of me registering for Fashiolista using the Facebook register flow.
Me on Facebook
Me on Fashiolista
In this blog post I will explain how to get started implementing a Facebook connect flow. Django Facebook can however do quite a bit more, as you can see in the feature list below. Development over at the github repo is very active. I strongly appreciate help on improving the functionality so please fork and contribute.
Features
Now let’s get started with building a Facebook login/connect/register flow. This post will guide you through it step by step.
Have django registration installed (other registration systems will require some small code changes)
pip install django_facebook :)
Define these settings in your settings file.
FACEBOOK_API_KEY
FACEBOOK_APP_ID
FACEBOOK_APP_SECRET
add django facebook to your installed apps
‘django_facebook’,
add this line to your url config
(r’^facebook/’, include(‘django_facebook.urls’)),
add this line to your context processors
‘django_facebook.context_processors.facebook’,
add this to your AUTHENTICATION_BACKENDS
‘django_facebook.auth_backends.FacebookBackend’,
Secondly we need to be able to store the Facebook data on your user profile.
The easiest way to do this is to add the abstract model in django_facebook/models.py called FacebookProfileModel to your profile model.
After your profile is ready to store Facebook data you should have a working example at /facebook/connect/.
Let me know in the comments if something went wrong up to this point :)
You can style the facebook form and button anyway you see fit. Over at Fashiolista we added a nice facepile for example. The basic markup is located in the example file connect.html.
We use the facebook javascript SDK for a smooth user integration. You can load the facebook JS like this:
<script src="{{ MEDIA_URL }}js/original/facebook.js" type="text/javascript"></script>
<script>
facebookAppId = '{{ FACEBOOK_APP_ID }}';
function facebookJSLoaded(){
FB.init({appId: facebookAppId, status: false, cookie: true, xfbml: true});
}
window.fbAsyncInit = facebookJSLoaded;
F = new facebookClass(facebookAppId);
F.load();
</script>
Subsequently implement a form which calls Facebook via javascript. Note that you can control which page to go to after connect using the next input field.
<form action="{% url facebook_connect %}?facebook_login=1" method="post">
<a href="javascript:void(0);" style="font-size: 20px;" onclick="F.connect(this.parentNode);">Register, login or connect with facebook</a>
<input type="hidden" value="{{ request.path }}" name="next" />
</form>
<div id="fb-root"></div>
That was all, you should now have a working registration flow using Facebook. Let me know in the comments if you encounter any difficulties.
If you want to go one step further and understand the facebook API, you can find the Facebook documentation here.
Django Facebook received tons of improvements from the python community. I’m certainly missing a few authors, but I would like to thank a few specifically:
Many thanks to (amongst many others)
Do you also see the beauty in clean code? Are you experienced with high scalability web apps? Currently we’re looking for additional talent over at our Amsterdam office. Feel free to drop me a line at my personal email for more information: thierryschellenbach[at]gmail.com
Share and Enjoy:
Django has a class included for this purpose called RequestFactory. However it doesn’t fake the session object, breaking most of my test code. To fix this I wrote a tiny snippet implementing the RequestFactory with session and user support. Hope it helps :)
from django.core.handlers.base import BaseHandler
from django.test.client import RequestFactory
class RequestMock(RequestFactory):
def request(self, **request):
"Construct a generic request object."
request = RequestFactory.request(self, **request)
handler = BaseHandler()
handler.load_middleware()
for middleware_method in handler._request_middleware:
if middleware_method(request):
raise Exception("Couldn't create request mock object - "
"request middleware returned a response")
return request
Share and Enjoy:
TechCrunch – Fashiolista raises funding from Skype founders
Share and Enjoy:
There are a lot of reasons for choosing Jinja2 over Django for us. Better performance (atleast… it was a lot better with previous Django versions), way more options (named arguments, multiple arguments for filters, etc), macros and simply easier to extend. Writing custom tags is simply not needed anymore since you can just make any function callable from the templates.
But… during the conversion there are always moments when you need a Django function in a Jinja template or vice versa. So… I created a few template tags to allow for Jinja code in Django templates (I’ve also created code to run Django code from Jinja, but I haven’t seen the need for it so I omitted it here).
A Jinja Include tag to include a template and let it be parsed by Jinja from a Django template:
from django import template
from coffin import shortcuts as jinja_shortcuts
register = template.Library()
class JinjaInclude(template.Node):
def __init__(self, filename):
self.filename = filename
def render(self, context):
return jinja_shortcuts.render_to_string(self.filename, context)
@register.tag
def jinja_include(parser, token):
bits = token.contents.split()
'''Check if a filename was given'''
if len(bits) != 2:
raise template.TemplateSyntaxError('%r tag requires the name of the '
'template to be included included ' % bits[0])
filename = bits[1]
'''Remove quotes if used'''
if filename[0] in ('"', "'") and filename[-1] == filename[0]:
filename = bits[1:-1]
return JinjaInclude(filename)
Usage:
{% jinja_include "some_template.html" %}
A couple of noop nodes to make sure that when you convert your Jinja templates to be executed from Django, they won’t break because of the missing Django tag.
from django import template
class Empty(template.Node):
def render(self, context):
return ''
@register.tag
def django(parser, token):
return Empty()
@register.tag
def end_django(parser, token):
return Empty()
And the Jinja tag to allow Jinja blocks in Django templates.
from django import template
from coffin.template import Template
register = template.Library()
class Jinja(template.Node):
def __init__(self, template):
self.template = template
def render(self, context):
return self.template.render(context)
@register.tag
def jinja(parser, token):
'''Create a Jinja template block
Usage:
{% jinja %}
Although you're in a Django template, code here will be executed by Jinja
{% end_jinja %}
'''
'''Generate the end tag from the currently used tag name'''
end_tag = 'end_%s' % token.contents.split()[0]
tokens = []
'''Convert all tokens to the string representation of them
That way we can keep Django template debugging with Jinja and feed the
entire string to Jinja'''
while parser.tokens:
token = parser.next_token()
if token.token_type == template.TOKEN_TEXT:
tokens.append(token.contents)
elif token.token_type == template.TOKEN_VAR:
tokens.append(' '.join((
template.VARIABLE_TAG_START,
token.contents,
template.VARIABLE_TAG_END,
)))
elif token.token_type == template.TOKEN_BLOCK:
if token.contents == end_tag:
break
tokens.append(' '.join((
template.BLOCK_TAG_START,
token.contents,
template.BLOCK_TAG_END,
)))
elif token.token_type == template.TOKEN_COMMENT:
pass
else:
raise template.TemplateSyntaxError('Unknown token type: "%s"' % token.token_type)
'''If our token has a `source` attribute than template_debugging is
enabled. If it's enabled create a valid source attribute for the Django
template debugger'''
if hasattr(token, 'source'):
source = token.source[0], (token.source[1][0], token.source[1][1])
else:
source = None
return Jinja(Template(''.join(tokens), source=source))
Do note that I have modified the “coffin.template.Template” to enable debugging completely. Just replace the “Template” class in “coffin/template/__init__.py” to make it work.
def _generate_django_exception(e, source=None):
'''Generate a Django exception from a Jinja source'''
from django.views.debug import linebreak_iter
if source:
exception = DjangoTemplateSyntaxError(e.message)
exception_dict = e.__dict__
del exception_dict['source']
'''Fetch the entire template in a string'''
template_string = source[0].reload()
'''Get the line number from the error message, if available'''
match = re.match('.* at (\d+)$', e.message)
start_index = 0
stop_index = 0
if match:
'''Convert the position found in the stacktrace to a position
the Django template debug system can use'''
position = int(match.group(1)) + source[1][0] + 1
for index in linebreak_iter(template_string):
if index >= position:
stop_index = min(index, position + 3)
start_index = min(index, position - 2)
break
start_index = index
else:
'''So there wasn't a matching error message, in that case we
simply have to highlight the entire line instead of the specific
words'''
ignore_lines = 0
for i, index in enumerate(linebreak_iter(template_string)):
if source[1][0] > index:
ignore_lines += 1
if i - ignore_lines == e.lineno:
stop_index = index
break
start_index = index
'''Convert the positions to a source that is compatible with the
Django template debugger'''
source = source[0], (
start_index,
stop_index,
)
else:
'''No source available so we let Django fetch it for us'''
lineno = e.lineno - 1
template_string, source = django_loader.find_template_source(e.name)
exception = DjangoTemplateSyntaxError(e.message)
'''Find the positions by the line number given in the exception'''
start_index = 0
for i in range(lineno):
start_index = template_string.index('\n', start_index + 1)
source = source, (
start_index + 1,
template_string.index('\n', start_index + 1) + 1,
)
exception.source = source
return exception
class Template(_Jinja2Template):
"""Fixes the incompabilites between Jinja2's template class and
Django's.
The end result should be a class that renders Jinja2 templates but
is compatible with the interface specfied by Django.
This includes flattening a ``Context`` instance passed to render
and making sure that this class will automatically use the global
coffin environment.
"""
def __new__(cls, template_string, origin=None, name=None, source=None):
# We accept the "origin" and "name" arguments, but discard them
# right away - Jinja's Template class (apparently) stores no
# equivalent information.
from coffin.common import env
try:
return env.from_string(template_string, template_class=cls)
except JinjaTemplateSyntaxError, e:
raise _generate_django_exception(e, source)
def __iter__(self):
# TODO: Django allows iterating over the templates nodes. Should
# be parse ourself and iterate over the AST?
raise NotImplementedError()
def render(self, context=None):
"""Differs from Django's own render() slightly in that makes the
``context`` parameter optional. We try to strike a middle ground
here between implementing Django's interface while still supporting
Jinja's own call syntax as well.
"""
if not context:
context = {}
else:
context = dict_from_django_context(context)
try:
return super(Template, self).render(context)
except JinjaTemplateSyntaxError, e:
raise _generate_django_exception(e)
def dict_from_django_context(context):
"""Flattens a Django :class:`django.template.context.Context` object.
"""
if isinstance(context, DjangoContext):
dict_ = {}
# Newest dicts are up front, so update from oldest to newest.
for subcontext in reversed(list(context)):
dict_.update(dict_from_django_context(subcontext))
return dict_
else:
return context
And you’re done, now you can just mix your Django and Jinja templates like this:
{% ifequal foo bar %}
Django style if...
{% endif %}
{% jinja %}
{% if foo == bar %}
Jinja style if...
{% endif %}
{% end_jinja %}
Share and Enjoy:
For Fashiolista.com we have been writing a lot about the various techniques for implementing these type of buttons. Especially important here is the way the javascript is loaded. Twitter uses a simple blocking script approach, where as digg, facebook and fashiolista use the async dynamic script approach. There are two large differences:
In this example we are faking slow twitter servers. (By routing it through google’s app engine and delaying the response). You can see the difference for yourself (be patient and be sure to clear your browser cache using CTRL F5).
Default twitter version
(note that site content is not loading until the twitter button is loaded)
Async twitter script
(everything loads and then we wait for twitter)
Solving this is quite simple. Simply change the way the twitter javascript is loaded from the first example to the second version.
Twitter version
<script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script>
Async twitter implementation
<script type="text/javascript">
//async script, twitter button fashiolista.com style
(function() {
var s = document.createElement('SCRIPT');
var c = document.getElementsByTagName('script')[0];
s.type = 'text/javascript';
s.async = true;
s.src = 'http://platform.twitter.com/widgets.js';
c.parentNode.insertBefore(s, c);
})();
</script>
Hope the difference won’t matter and Twitter will stay up :)
Follow me at:
@tschellenbach
Thanks for spreading the word:
NextWeb