How to create a custom feed in Django using the Syndication Feed Framework
Hello everyone.
In this article I will show you how to create a custom feed in Django, using the Django Syndication Feed Framework 1.2 or higher.
I’ve had to create an XML with some more tags for each item in the feed.
In particular, in addition to the usual tags present in an RSS feed, I’ve added the
<short_description/>
and
<image/>
tags.
Reading the official documentation of the framework, it is recommended to create a custom feed generator, but the process is not very clear.
Let’s see step by step how to fix everything.
First we create a custom feed generator.
This generator must create the same XML that would produce the framework’s default generator and for each item in the list it will add the new tags.
def add_item_elements(self, handler, item):
super(CustomFeedGenerator, self).add_item_elements(handler, item)
handler.addQuickElement(u"image", item['image'])
handler.addQuickElement(u"short_description", item['short_description'])
We see that the generator calls the method add_item_elements(handler, item) of its superclass and then adds new XML tags for each item.
Note that the values that will be inserted in the image and short_description tags must be present in the item dictionary passed as argument.
So let’s create our CustomFeed class that inherits from Feed class and see how to tell it to use the CustomFeedGenerator and how to insert the new values in the item dictionary.
feed_type = CustomFeedGenerator
def title(self, obj):
return u"My amazing feed"
def description(self, obj):
return u"My feed description"
def link(self, obj):
return "http://%s" % obj.get_absolute_url()
def item_extra_kwargs(self, obj):
"""
Returns an extra keyword arguments dictionary that is used with
the `add_item` call of the feed generator.
Add the 'content' field of the 'Entry' item, to be used by the custom feed generator.
"""
return { 'image': obj.get_picture().get_medium_url() if obj.get_picture() else "",
'short_description' : obj.get_preview(),}
CustomFeed is very similar to the various feeds that we are accustomed. The only difference is represented by the statement of the type of feed
feed_type = CustomFeedGenerator
and by the method
item_extra_kwargs
able to append any new values in the item dictionary, obtaining the desired result.
How to create multiple Django environments using virtualenv
Often we must work on various Django applications, each one dependent on different set of libraries.
For examples, we are working on a Django 1.1 project and and we need to start a new one with the latest version of the framework.
The best solution is to keep the various environments separate, in order to ensure that each project accesses only the libraries which it depends.
Python offers us virtualenv, a tool to create multiple isolated environments. Each environment is completely independent from the others and from the installed packages in system’s site-packages folder.
Let’s then see how to create a virtualenv and install the latest version of Django (today 1.2.1).
1- Install python-setuptools
The first thing to do is to install python-setuptools in order to have access to the command easy_install and to easly download packages. Alternatively, you can get the same results using the package pip.
Let us open a console and type the following commands:
Reading package lists... Done
Building dependency tree
Reading state information... Done
python-setuptools is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 132 not upgraded.
stefano@stefano-laptop:~$
2- Install virtualenv
The second step is to install the package virtualenv using easy_install:
[sudo] password for stefano:
Searching for virtualenv
Reading http://pypi.python.org/simple/virtualenv/
Reading http://virtualenv.openplans.org
Best match: virtualenv 1.4.9
Downloading http://pypi.python.org/packages/source/v/virtualenv/virtualenv-1.4.9.tar.gz#md5=c49067cab242b5ff8c7b681a5a99533a
Processing virtualenv-1.4.9.tar.gz
Running virtualenv-1.4.9/setup.py -q bdist_egg --dist-dir /tmp/easy_install-3D0IWT/virtualenv-1.4.9/egg-dist-tmp-xe4LRm
warning: no previously-included files matching '*.*' found under directory 'docs/_templates'
Adding virtualenv 1.4.9 to easy-install.pth file
Installing virtualenv script to /usr/local/bin
Installed /usr/local/lib/python2.6/dist-packages/virtualenv-1.4.9-py2.6.egg
Processing dependencies for virtualenv
Finished processing dependencies for virtualenv
3- Create the virtual environment
Once installed the package virtualenv we will need to create a space to host multiple environments. In my case, I’ve chose to create a directory in my home rather than in another writable folder in the filesystem.
stefano@stefano-laptop:~/Progetti/Python$
stefano@stefano-laptop:~/Progetti/Python$ mkdir virtualenvs
stefano@stefano-laptop:~/Progetti/Python$ cd virtualenvs/
stefano@stefano-laptop:~/Progetti/Python/virtualenvs$
Now we create the real virtualenv, called “Django-1.2-env. Note the argument added to the command virtualenv, –no-site-packages: so our virtual environment will have only a minimal set of libraries in his site-packages folder. We also note that all next commands will not need access as super user via sudo.
New python executable in django-1.2-env/bin/python
Installing setuptools............done.
4- Download Django in /tmp
Open a new console and download the tarball with the latest version of the framework Django. For simplicity, I’ve downloaded the package to /tmp.
stefano@stefano-laptop:/tmp$ wget http://www.djangoproject.com/download/1.2.1/tarball/
--2010-08-28 11:20:58-- http://www.djangoproject.com/download/1.2.1/tarball/
Resolving www.djangoproject.com... 64.207.133.18
Connecting to www.djangoproject.com|64.207.133.18|:80... connected.
HTTP request sent, awaiting response... 301 Moved Permanently
Location: http://media.djangoproject.com/releases/1.2/Django-1.2.1.tar.gz [following]
--2010-08-28 11:20:59-- http://media.djangoproject.com/releases/1.2/Django-1.2.1.tar.gz
Resolving media.djangoproject.com... 64.207.133.30
Connecting to media.djangoproject.com|64.207.133.30|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6248006 (6.0M) [application/octet-stream]
Saving to: `Django-1.2.1.tar.gz'
100%[======================================>] 6,248,006 590K/s in 12s
2010-08-28 11:21:11 (523 KB/s) - `Django-1.2.1.tar.gz' saved [6248006/6248006]
5- Installing Django in virtualenv
At this point, unpack Django and activate the virtualenv (via the command “source virtualenvdir/bin/activate”). Once activated virtualenv, we’ll find his name in the shell: (django-1.2-env)stefano@stefano-laptop
stefano@stefano-laptop:/tmp/Django-1.2.1$ source /home/stefano/Progetti/Python/virtualenvs/django-1.2-env/bin/activate
(django-1.2-env)stefano@stefano-laptop:/tmp/Django-1.2.1$
(django-1.2-env)stefano@stefano-laptop:/tmp/Django-1.2.1$ python setup.py install
We verify here that Django is installed properly in the virtual site-packages:
django easy-install.pth setuptools-0.6c11-py2.6.egg
Django-1.2.1-py2.6.egg-info pip-0.7.2-py2.6.egg setuptools.pth
6- Create a new project
At this point, we can create a new project using the command django-admin.py as required by the framework.
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$ django-admin.py startproject newdjangoapp
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python$ cd newdjangoapp/
(django-1.2-env)stefano@stefano-laptop:~/Progetti/Python/newdjangoapp$ ls
__init__.py manage.py settings.py urls.py
In conclusion, we can say that virtualenv is an excellent solution to create environments fully independent and designed to accommodate each individual project. In this way, we avoid conflicts of libraries and have the opportunity to try new configurations without creating problems for other applications.
Ajax update using Django and Prototype
This tutorial will show how to update a list of objects using Ajax and jQuery.
Suppose to have a page that shows all snippets for a given language (for example python).
Then we want to be able to load all ruby snippets without refreshing the page.
Let’s see the html for the “list” page:
{% for snippet in snippet_list %}
<div id="snippet-detail-{{snippet.id}}">
<!-- snippet content here -->
</div>
{% endfor %}
</div>
Suppose to have a button for the python language and an other for the ruby language.
We want to load all sippets for the currently selected languages: we only need to associate following javascript function on “onclick” event of the button:
<input type="button" id="rb-input" onclick="loadSnippets('rb');"></input>
<script>
function loadSnippets(language)
{
new Ajax.Updater('snippetListContainer', '/snippets/update_index/'+language, {asynchronous:true});
}
</script>
This javascript function calls asynchronously the server; then it will updates the “snippetListContainer” with the call’s response.
Now let’s see the code for the view:
"""
Renders the list of snippets basing on selected button.
This method is called by an Ajax request that starts every time a user clicks on a lanuage language button
Context::
snippet_list
the list of snippets to display in the page
Template::
myapp/snippets/index_included.html
"""
snippet_list = db.GqlQuery("SELECT * FROM Snippet WHERE language_code = :1 ORDER BY pub_date DESC", language)
return render_to_response('myapp/snippets/index_included.html',
{'snippet_list' : snippet_list},
context_instance=RequestContext(request))
And finally this is the code to place in the “url.py” file:
urlpatterns += patterns('',
(r'^snippets/update_index/(?P<language>[\w-]+)$', snippets.update_index),
)
Django gravatar tag
This short tutorial shows how to create a gravatar tag in a Django application.
Gravatar is an useful application that lets you to associate an avatar to your email address.
So you can show the same avatar in a large number of web sites without configure it every time.
First of all, we need to write the python code an register the “gravatar” tag:
import urllib, hashlib
register = template.Library()
def gravatar(email, size=80):
gravatar_url = "http://www.gravatar.com/avatar.php?"
gravatar_url += urllib.urlencode({
'gravatar_id':hashlib.md5(email).hexdigest(),
'size':str(size)})
return """<img src="%s" alt="gravatar for %s" />""" % (gravatar_url, email)
register.simple_tag(gravatar)
Then we can use the new gravatar tag in all templates page, by simply typing:
{% gravatar user.email 70 %}
How to manage Tags and Tag clouds with AppEngine and Django
Hi.
Today I would like to show you how to model a tag cloud with AppEngine and Django.
Suppose that we have a relationship n-n between a Post model and a Tag model and we would like to add a list of tags when creating a Post and to be able to modify that list when updating the Post.
Finally, we would like to display a tag cloud in the homepage by highlighting the most used tags.
Let’s start from the Tag model:
class Tag(db.Model):
"""
A descriptive tag to be applied to a Post.
label::
the Tag label
slug::
the Tag slug to be used in urls
post_count::
the sum of all post that have been tagged with this tag
"""
label = db.StringProperty(required=True)
slug = db.StringProperty()
post_count = db.IntegerProperty(default=1)
def put(self):
try:
self.key()
except NotSavedError:
self.slug = slugify(self.label)
super(Tag, self).put()
@permalink
def get_absolute_url(self):
return ('myapp.views.posts.posts_by_tag', (), {'tag_label': self.slug})
def __str__(self):
return self.label
and then the Post model:
class Post(db.Model):
"""
A post of code in some Language.
title::
the title of the Post
slug::
the post slug to be used in urls
body::
the body of the Post
tags::
the list of tag labels associated to this Post. Useful to show post's tags without loading them from DataStore.
"""
title = db.StringProperty(required=True)
slug = db.StringProperty(required=False)
body = db.TextProperty()
tags = db.StringListProperty()
def put(self):
try:
self.key()
except NotSavedError:
self.slug = slugify(self.title)
super(Post, self).put()
def __str__(self):
return self.title
@permalink
def get_absolute_url(self):
return ('myapp.views.posts.post_detail', (), { 'post_id': str(self.key().id()) + "-" + self.slug})
def set_tags_from_list(self, new_tags):
"""
deletes the current tag list from the Post and creates a new one.
creates a new Tag if not yet in DataStore or increase/decrease it's counter if necessary.
"""
old_tags = self.tags[:] # copy the list of tag
self.tags = [] # empty the list of tag associated to current post
tags_to_set = [] # the list of tag to associate to current post
same_tag_list = [item for item in old_tags if item in new_tags] # the list of tags that are the same after editing the post
removed_tag_list = [item for item in old_tags if item not in new_tags] # the list of tags that have been removed after editing the post
for new_tag in new_tags:
new_tag = new_tag.strip()
tag = Tag.get_by_key_name(new_tag)
if tag is None:
tags_to_set = Tag(key_name=new_tag, label=new_tag)
tags_to_set.put()
self.tags.append(new_tag)
else:
self.tags.append(new_tag)
self.put()
if new_tag not in same_tag_list:
tag.post_count += 1
tag.put()
for tag_to_remove in removed_tag_list:
tag = Tag.get_by_key_name(tag_to_remove.strip())
tag.post_count -= 1
tag.put()
def set_tags_from_string(self, tag_labels):
"""
gets the list of tags (comma separated) to be associated to current post
"""
new_tags = string.split(tag_labels, ',')
self.set_tags_from_list(new_tags)
As you can see, each post is able to manage by itself its associated tags, by creating a new one if necessary or updating the counter of an existent one.
Let’s write views code for creating/editing a post.
@login_required
def add_post(request):
"""
Allows a user to add a Post to the database.
Context::
form
The form to add the Post.
Template::
myapp/posts/add_post_form.html
"""
if request.method == 'POST':
form = forms.AddPostForm(request.POST)
if form.is_valid():
new_post = Post(title=form.cleaned_data['title'],
body=form.cleaned_data['body'])
new_post.set_tags_from_string(form.cleaned_data['tags'])
new_post.put()
return HttpResponseRedirect(new_post.get_absolute_url())
else:
form = forms.AddPostForm()
return render_to_response('myapp/posts/add_post_form.html',
{ 'form': form },
context_instance=RequestContext(request))
@login_required
def edit_post(request, post_id):
"""
Allows a user to edit an existing Post.
Context::
form
The form to add the Post.
Template::
myapp/posts/edit_post_form.html
"""
post = get_object_or_404(Post, id=int(post_id.split('-')[0]))
if request.method == 'POST':
form = forms.EditPostForm(data=request.POST)
if form.is_valid():
for field in ['body']:
setattr(post, field, form.cleaned_data[field])
post.set_tags_from_string(form.cleaned_data['tags'])
post.put()
return HttpResponseRedirect(post.get_absolute_url())
else:
form = forms.EditPostForm(instance=post)
return render_to_response('myapp/posts/edit_post_form.html',
{ 'form': form,
'original': post },
context_instance=RequestContext(request))
As you can see, both in add and edit post we call the
method by passing the tag string that comes from the Form. Infact, in the
or
the tags field is defined as
.
Now we have all required code to handle taggings in our application.
We can easly show post’s tag list in our template by calling:
{{post.title}} is tagged with: {% for tag in post.tags %}{{ tag }} {% endfor %}
</p>
Notice that we don’t make any DataStore lookup because we have stored all post’s tag labels in the
property.
Let’s create a tag cloud that takes as input the total number of tags that we want to display in the page:
from django.template import Library, Node
from myapp.models import Tag
from google.appengine.ext import db
register = Library()
class TagsCloudNode(Node):
def __init__(self, num, context_var):
self.context_var = context_var
self.num = int(num)
def sort_by_attr(self,seq,attr):
intermed = [ (getattr(seq[i],attr), i, seq[i]) for i in xrange(len(seq)) ]
intermed.sort()
return [ tup[-1] for tup in intermed ]
def gen_cloud(self, num):
query=Tag.all().filter("post_count >", 0).order('-post_count') # only retrieve tags that have at least one post associated
p = query.fetch(num)
if p:
max1=max([int(p_item.post_count) for p_item in p])
for i in range(len(p)):
size =int(round(int(p[i].post_count)*maxsize/max1))
if size<minsize:
size=minsize
cloudsize =str(size) +"%"
p[i].cloudsize=cloudsize
return self.sort_by_attr(p, "label")
def render(self, context):
context[self.context_var] = self.gen_cloud(self.num)
return ''
def get_tags_cloud(parser, token):
"""
Returns the tag cloud.
Example::
{% get_tags_cloud 5 as tag_cloud %}
"""
bits = token.contents.split()
if len(bits) != 4:
raise template.TemplateSyntaxError("'%s' tag takes exactly three arguments" % bits[0])
if bits[2] != 'as':
raise template.TemplateSyntaxError("second argument to '%s' tag must be 'as'" % bits[0])
return TagsCloudNode(bits[1], bits[3])
get_tags_cloud= register.tag(get_tags_cloud)
And in our template we can call this templatetag using this code:
<h2>
Tag Cloud
</h2>
{% load tags %}
{% get_tags_cloud 20 as tag_cloud %}
{% for tag in tag_cloud %}
<a href="{{ tag.get_absolute_url }}" style="font-size:{{ tag.cloudsize }}; text-align:left;"> {{ tag.label }}</a>
{% endfor %}
</div>
I hope you will find this tutorial useful for managing Tags in your AppEngine application.
I don’t know if this is the best way to follow, but it works for me.
Please send me feedbacks about this tutorial.
Django tag cloud
Ciao a tutti.
In questo post vorrei mostrare uno snippet veramente utile per visualizzare una tag cloud in una applicazione Django.
Supponiamo di avere nella nostra applicazione una relazione n-n tra un modello Post e un modello Tag.
Vogliamio dunque visualizzare nel nostro layout tutta la lista dei tag associati ai vari post, evidenziando quelli maggiormente utilizzati.
Per prima cosa e’ necessario registrare un nuovo templatetag:
from myapp.models import Tag, Post
import random
register = Library()
maxsize =180 # maximum size of the most popular tag
minsize = 75 # minimum size of the least popular tag
class LatestTagsNode(Node):
def gen_clouds(self):
tag_list=Tag.objects.all()
if tag_list:
max1=max([int(tag_item.posts.count()) for tag_item in tag_list])
for i in range(tag_list.count()):
size =int(round(int(tag_list[i].posts.count())*maxsize/max1))
if size<minsize:
size=minsize
cloudsize =str(size) +"%"
tag_list[i].cloudsize=cloudsize
return tag_list
def render(self, context):
self.__init__()
context['content_tagclouds'] = self.gen_clouds()
return ''
def get_latest_cloudtag(parser, token):
return LatestTagsNode()
get_latest_cloudtag= register.tag(get_latest_cloudtag)
A questo punto, nel nostro template, bastera’ richiamare il templatetag in questo modo:
{% load tags %}
{% get_latest_cloudtag %}
{% for tag in content_tagclouds %}
< a href="{{ tag.get_absolute_url }}"
style="font-size:{{ tag.cloudsize }};
text-align:left;"> {{ tag.name }}</a>
{% endfor %}
</div>