Tworzymy mikroblog, ścianę użytkownika w Django - część 1
Facebook jest światowym liderem w branży portali społecznościowych. Zastosowany w nim interfejs użytkownika jest powszechnie znany i powielany w innych aplikach. Projektując nowe aplikacje społecznościowe, czy też intranety, aplikacje do pracy grupowej możemy wykorzystać zawarte w Facebooku pomysły - takie jakie "Ściana" użytkownika. Łącząc możliwości mikrobloga z rozbudowaną obsługą multimediów i obsługą znajomości pomiędzy użytkownikami otrzymamy własną aplikację tego typu.
Załóżmy że chcemy stworzyć aplikację dla firmy, lub organizacji złożonej z kilku działów. Chcemy dać możliwość publikowania wiadomości dla znajomych, pracowników z działu, czy też wiadomości publicznych i prywatnych. Do tego proste załączanie multimediów - link do strony, grafiki, czy strony z klipem video i "gotowe" dla użytkownika. Zacznijmy od profilu użytkownika:
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')
Mamy dwa modele - model działów (Department) oraz właściwy model będący profilem użytkownika. Ważne pola to przypisanie do działu oraz lista znajomych. Należy zadbać o generowanie profilu użytkownika przy rejestracji bądź logowaniu, ale to już sprawa poza obszarem tego artykułu.
Przejdźmy teraz do modelu wiadomości. Na początek prosty model multimediów: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
Mamy prosty model zawierający pole "type", które określa nam typ treści dla danego obiektu, url wprowadzony przez użytkownika i pole "text" zawierające gotowy do wyświetlenia na stronie kod HTML (np. tytuł i opis strony www, kod HTML wyświetlający klip video itd.). Generowanie gotowego kodu odbywać powinno się po wprowadzeniu przez użytkownika odnośnika - AJAXem przesyłamy adres URL do widoku parsującego, który zwróci gotowy tekst i stworzy wstępnie obiekt "Media", który dołączony zostanie do wiadomości.
Główny element aplikacji to model wiadomości. Na chwilę obecną model wiadomości wygląda tak: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
Mamy pole "message" na tekst wprowadzony przez użytkownika, pole "author" określające autora wpisu oraz pola kopiujące dane z jego profilu: "dep" i "message_friends". Kopiowanie tych danych ułatwi później wybieranie wiadomości na ścianie użytkownika. Pole "privacy_type" określa poziom prywatności wpisu. Jeżeli użytkownik wybierze wiadomość prywatną to trzeba będzie wyświetlić listę znajomych tak by mógł wybrać odbiorców wiadomości - zapisanych następnie w "private_recipients". Pole "in_reply_to" pozwala nam zastosować ten model do komentarzy pod wiadomościami. Główne wiadomości nie będące odpowiedziami będą miały wartość null.
Mając model pomyślmy o podstawowej operacji - wyświetlaniu "pasując" wiadomości. Użytkownik może widzieć swoje wiadomości, a także wiadomości publiczne, wiadomości z jego działu, wiadomości od jego przyjaciół, oraz wiadomości prywatne skierowane do niego. Mając powyższy model łatwo można napisać odpowiednie zapytanie z pomocą operatora Q z 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))
- Wybieracz znajomych
- przykład rozsuwającego się pola tekstowego
- okno modalne w stylu facebooka
- implementacja "dodaj na ścianę" w PHP i jQuery
Comment article