/jchat/models.py

http://django-jchat.googlecode.com/ · Python · 129 lines · 112 code · 3 blank · 14 comment · 9 complexity · d35edd473331cdaef513715a21a41e41 MD5 · raw file

  1. # -*- encoding: UTF-8 -*-
  2. '''
  3. Models for jchat application.
  4. Let's try to make this as modular as possible, no strings attached
  5. THIS IMPLEMENTATION HAS A TERRIBLE PROBLEM, NO VALIDATION IS DONE WETHER THE USER CAN OR NOT SAY SOMETHING HERE, SO 'GHOST' USERS COULD BE SENDING MESSAGES TO A CHAT ROOM...
  6. THE OPTIMAL SOLUTION IS TO BIND THE CHAT ROOM WITH THE DIFFERENT OBJECTS THAT CAN USE IT, THAT IS, CREATE A FK FROM THE OBJECTS TO THE CHATROOM AND USE THE OBJECT ITSELF AS A VALIDATOR AND GATEWAY TO THE CHATROOM.
  7. IN OTHER WAYS... IMPLEMENT A MODEL FOR CONNECTED USERS ASAP (I accept suggestions)
  8. For more hardcore uses... a dedicated and specialized application should be better:
  9. @see: http://en.wikipedia.org/wiki/Comet_(programming)
  10. @author: Federico C??ceres <fede.caceres@gmail.com>
  11. '''
  12. from django.db import models
  13. from django.contrib.auth.models import User
  14. from django.contrib.contenttypes.models import ContentType, ContentTypeManager
  15. from django.contrib.contenttypes import generic
  16. from datetime import datetime
  17. class RoomManager(models.Manager):
  18. '''Custom model manager for rooms, this is used for "table-level" operations.
  19. All methods defined here can be invoked through the Room.objects class.
  20. @see: http://docs.djangoproject.com/en/1.0/topics/db/managers/#topics-db-managers
  21. Also see GenericTypes from the contenttypes django app!
  22. @see: http://docs.djangoproject.com/en/1.0/ref/contrib/contenttypes/'''
  23. def create(self, object):
  24. '''Creates a new chat room and registers it to the calling object'''
  25. r = self.model(content_object=object)
  26. r.save()
  27. return r
  28. def get_for_object(self, object):
  29. '''Try to get a room related to the object passed.'''
  30. return self.get(content_type=ContentType.objects.get_for_model(object), object_id=object.pk)
  31. def get_or_create(self, object):
  32. '''Save us from the hassle of validating the return value of get_for_object and create a room if none exists'''
  33. try:
  34. return self.get_for_object(object)
  35. except Room.DoesNotExist:
  36. return self.create(object)
  37. class Room(models.Model):
  38. '''Representation of a generic chat room'''
  39. content_type = models.ForeignKey(ContentType, blank=True, null=True) # to what kind of object is this related
  40. object_id = models.PositiveIntegerField(blank=True, null=True) # to which instace of the aforementioned object is this related
  41. content_object = generic.GenericForeignKey('content_type','object_id') # use both up, USE THIS WHEN INSTANCING THE MODEL
  42. created = models.DateTimeField(default=datetime.now())
  43. comment = models.TextField(blank=True, null=True)
  44. objects = RoomManager() # custom manager
  45. def __add_message(self, type, sender, message=None):
  46. '''Generic function for adding a message to the chat room'''
  47. m = Message(room=self, type=type, author=sender, message=message)
  48. m.save()
  49. return m
  50. def say(self, sender, message):
  51. '''Say something in to the chat room'''
  52. return self.__add_message('m', sender, message)
  53. def join(self, user):
  54. '''A user has joined'''
  55. return self.__add_message('j', user)
  56. def leave(self, user):
  57. '''A user has leaved'''
  58. return self.__add_message('l', user)
  59. def messages(self, after_pk=None, after_date=None):
  60. '''List messages, after the given id or date'''
  61. m = Message.objects.filter(room=self)
  62. if after_pk:
  63. m = m.filter(pk__gt=after_pk)
  64. if after_date:
  65. m = m.filter(timestamp__gte=after_date)
  66. return m.order_by('pk')
  67. def last_message_id(self):
  68. '''Return last message sent to room'''
  69. m = Message.objects.filter(room=self).order_by('-pk')
  70. if m:
  71. return m[0].id
  72. else:
  73. return 0
  74. def __unicode__(self):
  75. return 'Chat for %s %d' % (self.content_type, self.object_id)
  76. class Meta:
  77. unique_together = (("content_type", "object_id"),)
  78. MESSAGE_TYPE_CHOICES = (
  79. ('s','system'),
  80. ('a','action'),
  81. ('m', 'message'),
  82. ('j','join'),
  83. ('l','leave'),
  84. ('n','notification')
  85. )
  86. class Message(models.Model):
  87. '''A message that belongs to a chat room'''
  88. room = models.ForeignKey(Room)
  89. type = models.CharField(max_length=1, choices=MESSAGE_TYPE_CHOICES)
  90. author = models.ForeignKey(User, related_name='author', blank=True, null=True)
  91. message = models.CharField(max_length=255, blank=True, null=True)
  92. timestamp = models.DateTimeField(auto_now=True)
  93. def __unicode__(self):
  94. '''Each message type has a special representation, return that representation.
  95. This will also be translator AKA i18l friendly.'''
  96. if self.type == 's':
  97. return u'SYSTEM: %s' % self.message
  98. if self.type == 'n':
  99. return u'NOTIFICATION: %s' % self.message
  100. elif self.type == 'j':
  101. return 'JOIN: %s' % self.author
  102. elif self.type == 'l':
  103. return 'LEAVE: %s' % self.author
  104. elif self.type == 'a':
  105. return 'ACTION: %s > %s' % (self.author, self.message)
  106. return self.message