Commit 5988f9bea49168f0c156859bf58d3f3bfacfe7bc
1 parent
bd3fab00d1
Exists in
master
and in
1 other branch
remove breakpoint
Showing
3 changed files
with
4 additions
and
13 deletions
Show diff stats
README.md
1 | ### Django forum | 1 | ### Django forum |
2 | 2 | ||
3 | *** | ||
4 | |||
5 | demo: <http://djangoforum.coding.io/> | ||
6 | |||
7 | Django forum是使用Django实现的轻型现代论坛程序,是fork自[F2E.im](https://github.com/PaulGuo/F2E.im)的Django版本. | ||
8 | 相对于原版的主要区别在于使用Django admin实现了一个简单的后台管理. | ||
9 | |||
10 | Django forum有3个分支,master分支用于主机上部署,SAE分支是适配Sina App Engine的版本,api分支是一个试验性质的分支,详情见更新 | ||
11 | |||
12 | #### 安装部署 | 3 | #### 安装部署 |
13 | 4 | ||
14 | 主机版: | 5 | 主机版: |
15 | 6 | ||
16 | 依赖MySQL数据库,以及memcached | 7 | 依赖MySQL数据库,以及memcached |
17 | 8 | ||
18 | 1. 获取代码 | 9 | 1. 获取代码 |
19 | 2. 安装依赖 | 10 | 2. 安装依赖 |
20 | 3. 导入数据库文件 | 11 | 3. 导入数据库文件 |
21 | 4. 修改配置文件 | 12 | 4. 修改配置文件 |
22 | 5. 运行服务 | 13 | 5. 运行服务 |
23 | 14 | ||
24 | ```shell | 15 | ```shell |
25 | shell> git clone git@github.com:zhu327/forum.git | 16 | shell> git clone git@github.com:zhu327/forum.git |
26 | 17 | ||
27 | shell> cd forum | 18 | shell> cd forum |
28 | shell> pip install -r requirements.txt | 19 | shell> pip install -r requirements.txt |
29 | 20 | ||
30 | shell> mysql -u YOURUSERNAME -p | 21 | shell> mysql -u YOURUSERNAME -p |
31 | 22 | ||
32 | mysql> create database forum; | 23 | mysql> create database forum; |
33 | mysql> exit | 24 | mysql> exit |
34 | 25 | ||
35 | shell> mysql -u YOURUSERNAME -p --database=forum < forum.sql | 26 | shell> mysql -u YOURUSERNAME -p --database=forum < forum.sql |
36 | ``` | 27 | ``` |
37 | 28 | ||
38 | 修改`xp/settings.py` | 29 | 修改`xp/settings.py` |
39 | 30 | ||
40 | ```python | 31 | ```python |
41 | # 修改数据库配置 | 32 | # 修改数据库配置 |
42 | DATABASES = { | 33 | DATABASES = { |
43 | 'default': { | 34 | 'default': { |
44 | 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. | 35 | 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. |
45 | 'NAME': 'forum', # Or path to database file if using sqlite3. | 36 | 'NAME': 'forum', # Or path to database file if using sqlite3. |
46 | # The following settings are not used with sqlite3: | 37 | # The following settings are not used with sqlite3: |
47 | 'USER': 'root', | 38 | 'USER': 'root', |
48 | 'PASSWORD': '', | 39 | 'PASSWORD': '', |
49 | 'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. | 40 | 'HOST': '127.0.0.1', # Empty for localhost through domain sockets or '127.0.0.1' for localhost through TCP. |
50 | 'PORT': '3306', # Set to empty string for default. | 41 | 'PORT': '3306', # Set to empty string for default. |
51 | } | 42 | } |
52 | } | 43 | } |
53 | 44 | ||
54 | # 修改memcached配置,如果没有memcahed请删除这些与cache相关的内容 | 45 | # 修改memcached配置,如果没有memcahed请删除这些与cache相关的内容 |
55 | CACHES = { # memcached缓存设置 | 46 | CACHES = { # memcached缓存设置 |
56 | 'default': { | 47 | 'default': { |
57 | 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', | 48 | 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', |
58 | 'LOCATION': '127.0.0.1:11211', | 49 | 'LOCATION': '127.0.0.1:11211', |
59 | } | 50 | } |
60 | } | 51 | } |
61 | 52 | ||
62 | SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 使用memcached存储session | 53 | SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 使用memcached存储session |
63 | 54 | ||
64 | # 配置邮件发送 | 55 | # 配置邮件发送 |
65 | EMAIL_HOST = 'smtp.qq.com' | 56 | EMAIL_HOST = 'smtp.qq.com' |
66 | EMAIL_PORT = 25 | 57 | EMAIL_PORT = 25 |
67 | EMAIL_HOST_USER= '*********' | 58 | EMAIL_HOST_USER= '*********' |
68 | EMAIL_HOST_PASSWORD= '******' | 59 | EMAIL_HOST_PASSWORD= '******' |
69 | DEFAULT_FROM_EMAIL = '*********@qq.com' | 60 | DEFAULT_FROM_EMAIL = '*********@qq.com' |
70 | ``` | 61 | ``` |
71 | 62 | ||
72 | 运行服务 | 63 | 运行服务 |
73 | |||
74 | ```shell | 64 | ```shell |
75 | python manage.py runserver | 65 | python manage.py runserver |
76 | ``` | 66 | ``` |
77 | 67 | ||
78 | 默认超级用户`admin@admin.com`,密码`123456`,后台`/manage/admin/` | 68 | 默认超级用户`admin@admin.com`,密码`123456`,后台`/manage/admin/` |
79 | 69 | ||
80 | 生产环境下推荐使用gunicorn. |
forum/forms/user.py
1 | # coding: utf-8 | 1 | # coding: utf-8 |
2 | 2 | ||
3 | from django import forms | 3 | from django import forms |
4 | from django.contrib.auth import authenticate | 4 | from django.contrib.auth import authenticate |
5 | from django.conf import settings | 5 | from django.conf import settings |
6 | from forum.models import ForumUser | 6 | from forum.models import ForumUser |
7 | 7 | ||
8 | 8 | ||
9 | error_messages = { | 9 | error_messages = { |
10 | 'username': { | 10 | 'username': { |
11 | 'required': u'必须填写用户名', | 11 | 'required': u'必须填写用户名', |
12 | 'min_length': u'用户名长度过短(3-12个字符)', | 12 | 'min_length': u'用户名长度过短(3-12个字符)', |
13 | 'max_length': u'用户名长度过长(3-12个字符)', | 13 | 'max_length': u'用户名长度过长(3-12个字符)', |
14 | 'invalid': u'用户名格式错误(英文字母开头,数字,下划线构成)' | 14 | 'invalid': u'用户名格式错误(英文字母开头,数字,下划线构成)' |
15 | }, | 15 | }, |
16 | 'email': { | 16 | 'email': { |
17 | 'required': u'必须填写E-mail', | 17 | 'required': u'必须填写E-mail', |
18 | 'min_length': u'Email长度有误', | 18 | 'min_length': u'Email长度有误', |
19 | 'max_length': u'Email长度有误', | 19 | 'max_length': u'Email长度有误', |
20 | 'invalid': u'Email地址无效' | 20 | 'invalid': u'Email地址无效' |
21 | }, | 21 | }, |
22 | 'password': { | 22 | 'password': { |
23 | 'required': u'必须填写密码', | 23 | 'required': u'必须填写密码', |
24 | 'min_length': u'密码长度过短(6-64个字符)', | 24 | 'min_length': u'密码长度过短(6-64个字符)', |
25 | 'max_length': u'密码长度过长(6-64个字符)' | 25 | 'max_length': u'密码长度过长(6-64个字符)' |
26 | }, | 26 | }, |
27 | } | 27 | } |
28 | 28 | ||
29 | 29 | ||
30 | class SettingPasswordForm(forms.Form): | 30 | class SettingPasswordForm(forms.Form): |
31 | password_old = forms.CharField(min_length=6, max_length=64, | 31 | password_old = forms.CharField(min_length=6, max_length=64, |
32 | error_messages=error_messages.get('password')) | 32 | error_messages=error_messages.get('password')) |
33 | password = forms.CharField(min_length=6, max_length=64, | 33 | password = forms.CharField(min_length=6, max_length=64, |
34 | error_messages=error_messages.get('password')) | 34 | error_messages=error_messages.get('password')) |
35 | password_confirm = forms.CharField(required=False) | 35 | password_confirm = forms.CharField(required=False) |
36 | 36 | ||
37 | def __init__(self, request): | 37 | def __init__(self, request): |
38 | self.user = request.user | 38 | self.user = request.user |
39 | super(SettingPasswordForm, self).__init__(request.POST) | 39 | super(SettingPasswordForm, self).__init__(request.POST) |
40 | 40 | ||
41 | def clean(self): | 41 | def clean(self): |
42 | password_old = self.cleaned_data.get('password_old') | 42 | password_old = self.cleaned_data.get('password_old') |
43 | password = self.cleaned_data.get('password') | 43 | password = self.cleaned_data.get('password') |
44 | password_confirm = self.cleaned_data.get('password_confirm') | 44 | password_confirm = self.cleaned_data.get('password_confirm') |
45 | 45 | ||
46 | if not (password_old and self.user.check_password(password_old)): | 46 | if not (password_old and self.user.check_password(password_old)): |
47 | raise forms.ValidationError(u'当前输入旧密码有误') | 47 | raise forms.ValidationError(u'当前输入旧密码有误') |
48 | 48 | ||
49 | if password and password_confirm and password != password_confirm: | 49 | if password and password_confirm and password != password_confirm: |
50 | raise forms.ValidationError(u'两次输入新密码不一致') | 50 | raise forms.ValidationError(u'两次输入新密码不一致') |
51 | return self.cleaned_data | 51 | return self.cleaned_data |
52 | 52 | ||
53 | 53 | ||
54 | class ForgotPasswordForm(forms.Form): | 54 | class ForgotPasswordForm(forms.Form): |
55 | username = forms.RegexField(min_length=3, max_length=12, | 55 | username = forms.RegexField(min_length=3, max_length=12, |
56 | regex=r'^[a-zA-Z][a-zA-Z0-9_]*$', | 56 | regex=r'^[a-zA-Z][a-zA-Z0-9_]*$', |
57 | error_messages=error_messages.get('username')) | 57 | error_messages=error_messages.get('username')) |
58 | email = forms.EmailField(min_length=4, max_length=64, | 58 | email = forms.EmailField(min_length=4, max_length=64, |
59 | error_messages=error_messages.get('email')) | 59 | error_messages=error_messages.get('email')) |
60 | 60 | ||
61 | def __init__(self, *args, **kwargs): | 61 | def __init__(self, *args, **kwargs): |
62 | self.user_cache = None | 62 | self.user_cache = None |
63 | super(ForgotPasswordForm, self).__init__(*args, **kwargs) | 63 | super(ForgotPasswordForm, self).__init__(*args, **kwargs) |
64 | 64 | ||
65 | def clean(self): | 65 | def clean(self): |
66 | username = self.cleaned_data.get('username') | 66 | username = self.cleaned_data.get('username') |
67 | email = self.cleaned_data.get('email') | 67 | email = self.cleaned_data.get('email') |
68 | 68 | ||
69 | if username and email: | 69 | if username and email: |
70 | try: | 70 | try: |
71 | self.user_cache = ForumUser.objects.get(username=username, email=email) | 71 | self.user_cache = ForumUser.objects.get(username=username, email=email) |
72 | except ForumUser.DoesNotExist: | 72 | except ForumUser.DoesNotExist: |
73 | raise forms.ValidationError(u'所填用户名和邮箱有误') | 73 | raise forms.ValidationError(u'所填用户名和邮箱有误') |
74 | return self.cleaned_data | 74 | return self.cleaned_data |
75 | 75 | ||
76 | def get_user(self): | 76 | def get_user(self): |
77 | return self.user_cache | 77 | return self.user_cache |
78 | 78 | ||
79 | 79 | ||
80 | class LoginForm(forms.Form): | 80 | class LoginForm(forms.Form): |
81 | username = forms.CharField(min_length=3, max_length=12, | 81 | username = forms.CharField(min_length=3, max_length=12, |
82 | error_messages=error_messages.get('username')) | 82 | error_messages=error_messages.get('username')) |
83 | password = forms.CharField(min_length=6, max_length=64, | 83 | password = forms.CharField(min_length=6, max_length=64, |
84 | error_messages=error_messages.get('password')) | 84 | error_messages=error_messages.get('password')) |
85 | 85 | ||
86 | def __init__(self, *args, **kwargs): | 86 | def __init__(self, *args, **kwargs): |
87 | self.user_cache = None | 87 | self.user_cache = None |
88 | super(LoginForm, self).__init__(*args, **kwargs) | 88 | super(LoginForm, self).__init__(*args, **kwargs) |
89 | 89 | ||
90 | def clean(self): | 90 | def clean(self): |
91 | import pdb; pdb.set_trace() | ||
92 | username = self.cleaned_data.get('username') | 91 | username = self.cleaned_data.get('username') |
93 | password = self.cleaned_data.get('password') | 92 | password = self.cleaned_data.get('password') |
94 | 93 | ||
95 | if username and password: | 94 | if username and password: |
96 | self.user_cache = authenticate(username=username, password=password) | 95 | self.user_cache = authenticate(username=username, password=password) |
97 | if self.user_cache is None: | 96 | if self.user_cache is None: |
98 | raise forms.ValidationError(u'用户名或者密码不正确') | 97 | raise forms.ValidationError(u'用户名或者密码不正确') |
99 | elif not self.user_cache.is_active: | 98 | elif not self.user_cache.is_active: |
100 | raise forms.ValidationError(u'用户已被锁定,请联系管理员解锁') | 99 | raise forms.ValidationError(u'用户已被锁定,请联系管理员解锁') |
101 | return self.cleaned_data | 100 | return self.cleaned_data |
102 | 101 | ||
103 | def get_user(self): | 102 | def get_user(self): |
104 | return self.user_cache | 103 | return self.user_cache |
105 | 104 | ||
106 | 105 | ||
107 | class RegisterForm(forms.ModelForm): | 106 | class RegisterForm(forms.ModelForm): |
108 | username = forms.RegexField(min_length=3, max_length=12, | 107 | username = forms.RegexField(min_length=3, max_length=12, |
109 | regex=r'^[a-zA-Z][a-zA-Z0-9_]*$', | 108 | regex=r'^[a-zA-Z][a-zA-Z0-9_]*$', |
110 | error_messages=error_messages.get('username')) | 109 | error_messages=error_messages.get('username')) |
111 | email = forms.EmailField(min_length=4, max_length=64, | 110 | email = forms.EmailField(min_length=4, max_length=64, |
112 | error_messages=error_messages.get('email')) | 111 | error_messages=error_messages.get('email')) |
113 | password = forms.CharField(min_length=6, max_length=64, | 112 | password = forms.CharField(min_length=6, max_length=64, |
114 | error_messages=error_messages.get('password')) | 113 | error_messages=error_messages.get('password')) |
115 | password_confirm = forms.CharField(required=False) | 114 | password_confirm = forms.CharField(required=False) |
116 | 115 | ||
117 | class Meta: | 116 | class Meta: |
118 | model = ForumUser | 117 | model = ForumUser |
119 | fields = ('username', 'email') | 118 | fields = ('username', 'email') |
120 | 119 | ||
121 | def clean_username(self): | 120 | def clean_username(self): |
122 | username = self.cleaned_data['username'] | 121 | username = self.cleaned_data['username'] |
123 | try: | 122 | try: |
124 | ForumUser.objects.get(username=username) | 123 | ForumUser.objects.get(username=username) |
125 | raise forms.ValidationError(u'所填用户名已经被注册过') | 124 | raise forms.ValidationError(u'所填用户名已经被注册过') |
126 | except ForumUser.DoesNotExist: | 125 | except ForumUser.DoesNotExist: |
127 | if username in settings.RESERVED: | 126 | if username in settings.RESERVED: |
128 | raise forms.ValidationError(u'用户名被保留不可用') | 127 | raise forms.ValidationError(u'用户名被保留不可用') |
129 | return username | 128 | return username |
130 | 129 | ||
131 | def clean_email(self): | 130 | def clean_email(self): |
132 | email = self.cleaned_data['email'] | 131 | email = self.cleaned_data['email'] |
133 | try: | 132 | try: |
134 | ForumUser.objects.get(email=email) | 133 | ForumUser.objects.get(email=email) |
135 | raise forms.ValidationError(u'所填邮箱已经被注册过') | 134 | raise forms.ValidationError(u'所填邮箱已经被注册过') |
136 | except ForumUser.DoesNotExist: | 135 | except ForumUser.DoesNotExist: |
137 | return email | 136 | return email |
138 | 137 | ||
139 | def clean_password_confirm(self): | 138 | def clean_password_confirm(self): |
140 | password1 = self.cleaned_data.get('password') | 139 | password1 = self.cleaned_data.get('password') |
141 | password2 = self.cleaned_data.get('password_confirm') | 140 | password2 = self.cleaned_data.get('password_confirm') |
142 | if password1 and password2 and password1 != password2: | 141 | if password1 and password2 and password1 != password2: |
143 | raise forms.ValidationError(u'两次输入密码不一致') | 142 | raise forms.ValidationError(u'两次输入密码不一致') |
144 | return password2 | 143 | return password2 |
145 | 144 | ||
146 | def save(self, commit=True): | 145 | def save(self, commit=True): |
147 | user = super(RegisterForm, self).save(commit=False) | 146 | user = super(RegisterForm, self).save(commit=False) |
148 | user.set_password(self.cleaned_data['password']) | 147 | user.set_password(self.cleaned_data['password']) |
149 | if commit: | 148 | if commit: |
150 | user.save() | 149 | user.save() |
151 | return user | 150 | return user |
152 | 151 | ||
153 | 152 | ||
154 | class SettingForm(forms.Form): | 153 | class SettingForm(forms.Form): |
155 | username = forms.CharField() # readonly | 154 | username = forms.CharField() # readonly |
156 | email = forms.EmailField() # readonly | 155 | email = forms.EmailField() # readonly |
157 | nickname = forms.CharField(min_length=3, max_length=12, required=False, | 156 | nickname = forms.CharField(min_length=3, max_length=12, required=False, |
158 | error_messages={ | 157 | error_messages={ |
159 | 'min_length': u'昵称长度过短(3-12个字符)', | 158 | 'min_length': u'昵称长度过短(3-12个字符)', |
160 | 'max_length': u'昵称长度过长(3-12个字符)', | 159 | 'max_length': u'昵称长度过长(3-12个字符)', |
161 | }) | 160 | }) |
162 | signature = forms.CharField(required=False) | 161 | signature = forms.CharField(required=False) |
163 | location = forms.CharField(required=False) | 162 | location = forms.CharField(required=False) |
164 | website = forms.URLField(required=False, | 163 | website = forms.URLField(required=False, |
165 | error_messages={ | 164 | error_messages={ |
166 | 'invalid': u'请填写合法的URL地址(如:http://f2e.im)', | 165 | 'invalid': u'请填写合法的URL地址(如:http://f2e.im)', |
167 | }) | 166 | }) |
168 | company = forms.CharField(required=False) | 167 | company = forms.CharField(required=False) |
169 | github = forms.CharField(required=False) | 168 | github = forms.CharField(required=False) |
170 | twitter = forms.CharField(required=False) | 169 | twitter = forms.CharField(required=False) |
171 | douban = forms.CharField(required=False) | 170 | douban = forms.CharField(required=False) |
172 | self_intro = forms.CharField(required=False) | 171 | self_intro = forms.CharField(required=False) |
173 | 172 |
requirements.txt
1 | Django==1.5.2 | 1 | Django==1.8.2 |
2 | Markdown==2.5.2 | 2 | Markdown==2.5.2 |
3 | MySQL-python==1.2.3 | 3 | MySQL-python==1.2.3 |
4 | Pillow==2.7.0 | ||
5 | Pygments==2.0.1 | ||
4 | 6 |