Implement a basic forum

This commit is contained in:
Trever Fischer
2012-02-18 15:31:13 -05:00
parent d5a4b83a74
commit db6d0c29d8
20 changed files with 334 additions and 0 deletions

0
forums/__init__.py Normal file
View File

5
forums/admin.py Normal file
View File

@@ -0,0 +1,5 @@
import models
from django.contrib import admin
admin.site.register(models.Forum)
admin.site.register(models.Topic)
admin.site.register(models.Post)

12
forums/forms.py Normal file
View File

@@ -0,0 +1,12 @@
from django import forms
import models
class ReplyForm(forms.ModelForm):
class Meta:
model = models.Post
exclude = ('user', 'parent')
class TopicForm(forms.ModelForm):
class Meta:
model = models.Topic
exclude = ('forum', 'rootPost')

93
forums/models.py Normal file
View File

@@ -0,0 +1,93 @@
from django.db import models
from django.contrib.auth.models import User
from mptt.models import MPTTModel, TreeForeignKey
def unique_slug(item,slug_source,slug_field):
"""Ensures a unique slug field by appending an integer counter to duplicate slugs.
The item's slug field is first prepopulated by slugify-ing the source field. If that value already exists, a counter is appended to the slug, and the counter incremented upward until the value is unique.
For instance, if you save an object titled Daily Roundup, and the slug daily-roundup is already taken, this function will try daily-roundup-2, daily-roundup-3, daily-roundup-4, etc, until a unique value is found.
Call from within a model's custom save() method like so:
unique_slug(item, slug_source='field1', slug_field='field2')
where the value of field slug_source will be used to prepopulate the value of slug_field.
"""
if not getattr(item, slug_field): # if it's already got a slug, do nothing.
from django.template.defaultfilters import slugify
slug = slugify(getattr(item,slug_source))
itemModel = item.__class__
# the following gets all existing slug values
allSlugs = [sl.values()[0] for sl in itemModel.objects.values(slug_field)]
if slug in allSlugs:
import re
counterFinder = re.compile(r'-\d+$')
counter = 2
slug = "%s-%i" % (slug, counter)
while slug in allSlugs:
slug = re.sub(counterFinder,"-%i" % counter, slug)
counter += 1
setattr(item,slug_field,slug)
class Forum(MPTTModel):
parent = TreeForeignKey('self', blank=True, null=True, related_name='children')
name = models.CharField(max_length=30)
slug = models.SlugField(blank=True)
def save(self, *args, **kwargs):
unique_slug(self, slug_source='name', slug_field='slug')
super(Forum, self).save(*args, **kwargs)
def topicCount(self):
return self.topic_set.count()
def latestTopic(self):
return self.topic_set.extra(order_by = ['created'])
@models.permalink
def get_absolute_url(self):
return ('forums.views.forum', [self.slug])
def __unicode__(self):
return self.name
class Topic(models.Model):
forum = models.ForeignKey(Forum)
title = models.CharField(max_length=100)
rootPost = models.OneToOneField('Post')
created = models.DateTimeField(editable=False, auto_now_add=True)
updated = models.DateTimeField(editable=False, auto_now=True)
slug = models.SlugField(editable=False, blank=True)
def save(self, *args, **kwargs):
unique_slug(self, slug_source='title', slug_field='slug')
super(Topic, self).save(*args, **kwargs)
def lastPost(self):
return self.rootPost.get_descendants(True).extra(order_by = ['updated'])
@models.permalink
def get_absolute_url(self):
return ('forums.views.topic', [], {"forumSlug":self.forum.slug, "topicSlug":self.slug, "topicID":self.id})
def __unicode__(self):
return self.title
class Post(MPTTModel):
parent = TreeForeignKey('self', blank=True, null=True, related_name='children')
user = models.ForeignKey(User)
body = models.TextField()
created = models.DateTimeField(editable=False, auto_now_add=True)
updated = models.DateTimeField(editable=False, auto_now=True)
def forum(self):
if parent is None:
return Forum.objects.get(rootTopic=self.id)
return parent.forum()
@models.permalink
def get_absolute_url(self):
return ('forums.views.post', [self.id])
def __unicode__(self):
return self.body

16
forums/tests.py Normal file
View File

@@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

12
forums/urls.py Normal file
View File

@@ -0,0 +1,12 @@
from django.conf.urls.defaults import patterns, include, url
urlpatterns = patterns('forums',
url(r'^$', 'views.index'),
url(r'^forum/(?P<forum>.*)/$', 'views.forum'),
url(r'^forum/(?P<forumID>.*)/new$', 'views.newTopic'),
url(r'^forum/(?P<forumSlug>.*)/(?P<topicID>.*)/(?P<topicSlug>.*)$', 'views.topic'),
url(r'^topic/(?P<topicID>.*)$', 'views.topic'),
url(r'^reply/(?P<topicID>.*)', 'views.reply'),
url(r'^reply$', 'views.reply'),
url(r'^post/(?P<id>.*)$', 'views.post'),
)

60
forums/views.py Normal file
View File

@@ -0,0 +1,60 @@
import models
import forms
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
from django.core.urlresolvers import reverse
def index(request):
forums = models.Forum.objects.filter(parent=None)
return render_to_response('forums/index.html', {"forums":forums}, context_instance = RequestContext(request))
def forum(request, forum):
forum = models.Forum.objects.get(slug=forum)
topics = forum.topic_set.all()
return render_to_response('forums/forum.html', {"forum":forum, "topics":topics}, context_instance = RequestContext(request))
def topic(request, topicID, forumSlug=None, topicSlug=None):
topic = models.Topic.objects.get(id=topicID)
return render_to_response('forums/topic.html', {"topic":topic}, context_instance = RequestContext(request))
def post(request, id):
post = models.Post.objects.get(id=id)
rootPost = post.get_root()
return HttpResponseRedirect(reverse('forums.views.topic', kwargs={"topicID":rootPost.topic.id})+"#reply-"+id)
def reply(request, topicID=None):
parentPost = models.Post.objects.get(id__exact=topicID)
if request.method == 'POST':
form = forms.ReplyForm(request.POST)
else:
form = forms.ReplyForm()
if form.is_valid():
reply = models.Post()
reply.parent = parentPost
reply.body = form.cleaned_data['body']
reply.user = request.user
reply.save()
return HttpResponseRedirect(reverse('forums.views.post', kwargs={"id":reply.id}))
return render_to_response('forums/reply.html', {"post":parentPost, "form":form}, context_instance = RequestContext(request))
def newTopic(request, forumID=None):
parentForum = models.Forum.objects.get(id__exact=forumID)
if request.method == 'POST':
replyForm = forms.ReplyForm(request.POST, prefix='reply')
topicForm = forms.TopicForm(request.POST, prefix='topic')
else:
replyForm = forms.ReplyForm(prefix='reply')
topicForm = forms.TopicForm(prefix='topic')
if replyForm.is_valid() and topicForm.is_valid():
topic = models.Topic()
topic.title = topicForm.cleaned_data['title']
topic.forum = parentForum
reply = models.Post()
reply.body = replyForm.cleaned_data['body']
reply.user = request.user
reply.save()
topic.rootPost = reply
topic.save()
return HttpResponseRedirect(reverse('forums.views.post', kwargs={'id': reply.id}))
return render_to_response('forums/newTopic.html', {"forum":parentForum, "replyForm":replyForm, "topicForm": topicForm}, context_instance = RequestContext(request))