RkBlog

Hardware, programming and astronomy tutorials and reviews.

Facebook wall like application in Django - part 1

Creating Facebook Wall-like or Twitter-like Django application for small social, intranet or collaboration sites. Part 1 - playing with models and queries.

Facebook is the leading social website and it's functionality or user interface is well known. Many smaller websites try to reuse ideas from Facebook to provide users with UI that they know and which is easy to use and functional. Designing a social site, collaboration web application or company intranet sites we can think about adding a microblog Twitter-like or Facebook Wall-like application. Lets do some initial implementation in Django.

djang-wall

So let say we want to make a company user Wall application. Users are grouped in departments/categories and we want to let users post messages to friends, department workers, public and private messages. Also multimedia handling is required - post a URL and the wall will generate for example YT clip player. Lets start with user profile models:

class Department(models.Model):
	"""
	Department model
	"""
	title = models.CharField(max_length=255, verbose_name=_('Name'), blank=True)
	slug = models.SlugField(max_length=255, unique=True, verbose_name=_('Slug'))
	def __str__(self):
		return self.title
	def __unicode__(self):
		return self.title
	class Meta:
		verbose_name = _('Department')
		verbose_name_plural = _('Departments')


class Profile(models.Model):
	"""
	User Profile
	"""
	user = models.ForeignKey(User, unique=True, verbose_name=_('User'), related_name='user')
	image = models.ImageField(upload_to='user_images', verbose_name=_('Avatar'), blank=True, null=True)
	dep = models.ForeignKey(Department, verbose_name=_('Department'))
	friends = models.ManyToManyField(User, verbose_name=_('Friends'), blank=True, null=True, related_name='friends')
	
	last_visit = models.DateTimeField(blank=True, auto_now=True, verbose_name=_('Last visit'))
	
	# random profile stuff
	public_email = models.EmailField(max_length=100, verbose_name=_('Public email'), blank=True, null=True)
	facebook = models.URLField(max_length=100, verbose_name=_('Facebook profile'), blank=True, null=True)
	goldenline = models.URLField(max_length=100, verbose_name=_('Goldenline profile'), blank=True, null=True)
	jabber = models.CharField(max_length=100, verbose_name=_('Jabber/GTalk'), blank=True, null=True)
	skype = models.CharField(max_length=100, verbose_name=_('Skype'), blank=True, null=True)
	gg = models.IntegerField(verbose_name=_('Gadu Gadu'), blank=True, null=True)
	bio = models.TextField(verbose_name=_('Bio'), blank=True, null=True)
	
	def __str__(self):
		return str(self.user)
	def __unicode__(self):
		return unicode(self.user)
	class Meta:
		verbose_name = _('User Profile')
		verbose_name_plural = _('User Profiles')

We have two models - Department model and the true profile model - "Profile". Most important fields are "Department" and "Friends". Also the profiles have to be generated, updated, but this isn't the point of this article.

As for content models for the wall, lest look at simple multimedia model:
class Media(models.Model):
    """
    storage for Blip attached medias
    """
    TYPES = [('url', _('URL')), ('image', _('Image')), ('video', _('Video Clip'))]
    type = models.CharField(max_length=100, verbose_name=_('Type'), choices=TYPES)
    url = models.CharField(max_length=255, verbose_name=_('Media URL'))
    text = models.TextField(verbose_name=_('Parsed media text'))
    class Meta:
        verbose_name = _('Media')
        verbose_name_plural = _('Media')
    def __str__(self):
        return self.url
    def __unicode__(self):
        return self.url

We have tree types of multimedias - links, images, and video clips from YouTube, Vimeo etc. The "text" field will contain ready to display HTML code for given media. For finished application user would enter the URL and by AJAX request the django app would parse the URL (fetch the url) and returned required data (player code, website title, image thumbnail etc.).

Main element of a wall or microblog is the message. We need a message model like this one:
class Blip(models.Model):
    """
    storage for user wall messages
    """
    TYPES = [('f', _('Friends')), ('d', _('All from my department')), ('a', _('All users')), ('p', _('Private message'))]
    
    message = models.TextField(verbose_name=_('Message'))
    author = models.ForeignKey(User, verbose_name=_('Author'), related_name='author')
    # user department
    dep = models.ForeignKey(Department, verbose_name=_('Department'))
    
    privacy_type = models.CharField(max_length=100, verbose_name=_('Privacy'), choices=TYPES)
    # set this if private msg
    private_recipients = models.ManyToManyField(User, verbose_name=_('Recipients of PM'), blank=True, null=True, related_name='private_recipients')
    # copy from profile if message for friends
    message_friends = models.ManyToManyField(User, verbose_name=_('Friends at the time of creation'), blank=True, null=True, related_name='message_friends')
	# if this is a reply to another Blip
    in_reply_to = models.ForeignKey('self', verbose_name=_('Reply to blip'), blank=True, null=True)
    
    date = models.DateTimeField(auto_now_add=True)
    
    media = models.ManyToManyField(Media, verbose_name=_('Media'), blank=True, null=True)
    class Meta:
        verbose_name = _('Blip')
        verbose_name_plural = _('Blips')
    def __str__(self):
        return self.message
    def __unicode__(self):
        return self.message

So we have "message" field for the text users submits, "author" field to identify him and some fields - "dep" and "message_friends" copied at the time of creation. Those fields will help getting proper messages on a user wall. "privacy_type" fields sets the message privacy. If the message is private then it has to have "private_recipients". Using "in_reply_to" field we can reuse this model for commenting other messages. Main messages will have null for this field.

So how do we show proper set of messages on a user wall? User can see his own messages, public messages, messages from his department or from his friends and also private messages sent to him. This can be done easily using Q class from django.db.models:

@login_required
def show_wall(request):
    """
    Show the wall page
    """
    p = request.user.get_profile()
    
    blips = Blip.objects.filter(
                        # user blips
                        Q(author=request.user, in_reply_to__isnull=True) |
                        # blips for user department
                        Q(privacy_type='d', dep=p.dep, in_reply_to__isnull=True) |
                        # private blips to this user
                        Q(privacy_type='p', private_recipients=request.user, in_reply_to__isnull=True) |
                        # blips from user friends
                        Q(privacy_type='f', message_friends=request.user, in_reply_to__isnull=True) |
                        # public blips
                        Q(privacy_type='a', in_reply_to__isnull=True)
                    ).order_by('-date')
    
    return render_to_response('wall/show_wall.html', {'blips': blips}, context_instance=RequestContext(request))
Python quick-run for such application is short, but the most effort has to be putted in the user interface including JavaScript and AJAX calls to supporting django views. There isn't any ready full blown UI set for such applications, but there are bits and pieces that we can reuse: In the next part I'll show some UI examples for this application.
djang-wall
RkBlog

Django web framework tutorials, 20 May 2010,

Comment article