module-07-2 简单BBS

需求


 

项目:开发一个简单的BBS论坛

需求:

1、整体参考“抽屉新热榜” + “虎嗅网”

2、实现不同论坛版块

3、帖子列表展示

4、帖子评论数、点赞数展示

5、在线用户展示

6、允许登录用户发贴、评论、点赞

7、允许上传文件

8、帖子可被置顶

9、可进行多级评论

 

code


 

 1 #! /usr/bin/env python3
 2 #  -*- coding:utf-8 -*-
 3 
 4 from django.template import Library
 5 from django.utils.safestring import mark_safe
 6 
 7 register = Library()
 8 
 9 
10 @register.simple_tag
11 def searchCondition(arg_dict, k):
12     if k == 'article_type':
13         if arg_dict['article_type'] == 0:
14             res = '<a class="active_condition" href="/backend/article-0-%s.html">全部</a>' % arg_dict['category_id']
15         else:
16             res = '<a href="/backend/article-0-%s.html">全部</a>' % arg_dict.category_id
17         return mark_safe(res)
18     else:
19         if arg_dict['category_id'] == 0:
20             res = '<a class="active_condition" href="/backend/article-%s-0.html">全部</a>' % arg_dict['article_type']
21         else:
22             res = '<a href="/backend/article-%s-0.html">全部</a>' % arg_dict['article_type']
23         return mark_safe(res)
24 
25 
26 @register.simple_tag
27 def searchCondition2(arg_dict,category_list):
28     res_list = []
29     for c in category_list:
30         if arg_dict['category_id'] == c.nid:
31             res_list.append('<a class="active_condition" href="/backend/article-%s-%s.html">%s</a>' % (
32             arg_dict['article_type'], c.nid, c.title))
33         else:
34             res_list.append('<a href="/backend/article-%s-%s.html">%s</a>' % (arg_dict['article_type'], c.nid, c.title))
35 
36     return mark_safe(' '.join(res_list))
37 
38 
39 @register.simple_tag
40 def searchCondition3(arg_dict,article_type_list):
41     res_list = []
42     for t in article_type_list:
43         if arg_dict['article_type'] == t[0]:
44             temp = '<a class ="active_condition" href="/backend/article-%s-%s.html">%s</a>'%(t[0],arg_dict['category_id'],t[1])
45         else:
46             temp = '<a href="/backend/article-%s-%s.html">%s</a>'%(t[0],arg_dict['category_id'],t[1])
47         res_list.append(temp)
48 
49     return mark_safe(' '.join(res_list))
backend/templatetags/search.py
  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 from django.shortcuts import render,redirect,HttpResponse
  4 from django.db import transaction
  5 from repository import models
  6 from backend import forms
  7 from utils.custom_json_encoder import CustomJsonEncoder
  8 import json,os,time
  9 
 10 
 11 def auth(func):
 12     '''
 13     用户验证装饰器 | 或写成2个函数,由对应的view启用对应的装饰器或许更好?
 14     :param func:
 15     :return:
 16     '''
 17     def wrapper(request,*args,**kwargs):
 18         # print(request.path_info)
 19         if request.path_info == '/backend/base-info.html':
 20             if request.session.get('username',None):
 21                 return func(request,*args,**kwargs)
 22             else:
 23                 return redirect('/login.html')
 24         else:
 25             if request.session.get('username',None) and request.session.get('blog_nid',None):
 26                 return func(request,*args,**kwargs)
 27             elif request.session.get('username',None) and not request.session.get('blog_nid',None):
 28                 return render(request,'backend_return_to_base_info.html')
 29             else:
 30                 return redirect('/login.html')
 31 
 32     return wrapper
 33 
 34 
 35 @auth
 36 def base_info(request):
 37     """
 38     博主个人信息
 39     :param request:
 40     :return:
 41     """
 42     if request.method == 'GET':
 43         user = models.UserInfo.objects.filter(username=request.session['username']).select_related('blog').first()
 44         # cur_user = request.session['cur_user']
 45         return render(request, 'backend_base_info.html',{'user':user})
 46 
 47     elif request.method == 'POST':
 48         # print(request.POST)
 49         data = {}
 50         for k,v in request.POST.items():
 51             data[k] = v.strip()
 52 
 53         data['username'] = request.session['username']
 54 
 55         bf = forms.BlogForm(data)
 56         res = {'status':True,'error':None,'data':None}
 57         if bf.is_valid():
 58             userS = models.UserInfo.objects.filter(username=request.session['username'])
 59             if bf.cleaned_data['nickname']:
 60                 userS.update(nickname=bf.cleaned_data['nickname'])
 61 
 62             blog_obj,is_created = models.Blog.objects.update_or_create(
 63                 user=userS.first(),
 64                 defaults={
 65                     'site':request.POST['site'],
 66                     'theme':request.POST['theme'],
 67                     'title':request.POST['title'],
 68                 }
 69             )
 70             # 对于新注册的博客,将blog的id保存入session
 71             if is_created:
 72                 request.session['blog_nid'] = blog_obj.nid
 73         else:
 74             res['status'] = False
 75             res['error'] = bf.errors.as_data()
 76         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
 77 
 78     else:
 79         return redirect('/')
 80 
 81 
 82 @auth
 83 def uploadAvatar(request):
 84     '''
 85     上传头像
 86     :param request:
 87     :return:
 88     '''
 89     if request.method == 'POST':
 90         file = request.FILES.get('avatar',None)
 91         res = {'status':True,'data':None}
 92         file_path = os.path.join('static/imgs/avatar',file.name)
 93         with open( file_path,'wb' ) as f:
 94             for line in file:
 95                 f.write(line)
 96 
 97         res['data'] = '/' + file_path
 98         models.UserInfo.objects.filter(username = request.session['username']).update(avatar=res['data'])
 99 
100         return HttpResponse(json.dumps(res))
101     else:
102         return redirect('/')
103 
104 
105 @auth
106 def tag(request):
107     """
108     博主个人标签管理
109     :param request:
110     :return:
111     """
112     # cur_blog = models.Blog.objects.filter(user__username=request.session['username']).first()
113     cur_blog = models.Blog.objects.filter(nid=request.session['blog_nid']).first()
114     if request.method == 'GET':
115         tags = models.Tag.objects.filter(blog=cur_blog)
116         return render(request,'backend_tag.html',{'tags':tags})
117     elif request.method == 'POST':
118         res = {'status':True,'error':None,'data':None}
119         data = {}
120         for k,v in request.POST.items():
121             data[k]=v
122         data['blog_nid'] = cur_blog.nid #;print(data)
123         tf = forms.TagForm(data)
124         if tf.is_valid():
125             models.Tag.objects.create(title=tf.cleaned_data['title'],
126                                       blog=cur_blog)
127         else:
128             res['status'] = False
129             res['error'] = tf.errors.as_data()
130         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
131     else:
132         return redirect('/')
133 
134 
135 @auth
136 def del_tag(request):
137     '''
138     删除标签
139     :param request:
140     :return:
141     '''
142     if request.method == 'POST':
143         res = {'status':True,'error':None,'data':None}
144         try:
145             nid = request.POST.get('nid',None)
146             models.Tag.objects.filter(nid=nid).delete()
147         except Exception as e:
148             res['status'] = False
149             res['error'] = str(e)
150         return HttpResponse(json.dumps(res))
151     else:
152         return redirect('/')
153 
154 @auth
155 def edit_tag(request):
156     '''
157     编辑标签名称
158     :param request:
159     :return:
160     '''
161     if request.method == 'POST':
162         res = {'status':True,'error':None,'data':None}
163         tf = forms.TagForm2(request.POST)
164         if tf.is_valid():
165             models.Tag.objects.filter(nid=tf.cleaned_data['nid']).update(title=tf.cleaned_data['title'])
166         else:
167             res['status'] = False
168             res['error'] = tf.errors.as_data()
169         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
170     else:
171         return redirect('/')
172 
173 
174 @auth
175 def category(request):
176     """
177     博主个人分类管理
178     :param request:
179     :return:
180     """
181     # cur_blog = models.Blog.objects.filter(user__username=request.session['username']).first()
182     cur_blog = models.Blog.objects.filter(nid=request.session['blog_nid']).first()
183     if request.method == 'GET':
184 
185         categories = models.Category.objects.filter(blog=cur_blog)
186         # request.session['blog_title'] = cur_blog.title
187         # cur_categories = request.session['cur_categories']
188         return render(request, 'backend_category.html',{'cs':categories})
189     elif request.method == 'POST':
190         res = {'status':True,'error':None,'data':None}
191         # 添加验证项
192         data = {}
193         for k,v in request.POST.items():
194             data[k] = v
195         data['blog_nid'] = cur_blog.nid # ;print(blog.nid)
196         # 验证
197         cf = forms.CategoryForm(data)
198         if cf.is_valid():
199             models.Category.objects.create(title=cf.cleaned_data['title'],blog=cur_blog)
200         else:
201             res['status'] = False
202             res['error'] = cf.errors.as_data()
203         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
204     else:
205         return redirect('/')
206 
207 
208 @auth
209 def del_ctgy(request):
210     '''
211     删除文章分类
212     :param request:
213     :return:
214     '''
215     if request.method == 'POST':
216         res = {'status':True,'error':None,'data':None}
217         try:
218             nid = request.POST.get('nid',None)
219             models.Category.objects.filter(nid=nid).delete()
220         except Exception as e:
221             res['status'] = False
222             res['error'] = str(e)
223         return HttpResponse(json.dumps(res))
224     else:
225         return redirect('/')
226 
227 
228 @auth
229 def edit_ctgy(request):
230     '''
231     编辑文章分类
232     :param request:
233     :return:
234     '''
235     if request.method == 'POST':
236         res = {'status':True,'error':None,'data':None}
237         cf = forms.CategoryForm2(request.POST)
238         if cf.is_valid():
239             nid = request.POST.get('nid',None) # ;print(nid)
240             title = request.POST.get('title',None)
241             models.Category.objects.filter(nid=nid).update(title=title)
242         else:
243             res['status'] = False
244             res['error'] = cf.errors.as_data()
245         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
246     else:
247         return redirect('/')
248 
249 
250 @auth
251 def article(request,*args,**kwargs):
252     """
253     博主个人文章管理
254     :param request:
255     :return:
256     """
257     cur_blog = models.Blog.objects.filter(nid=request.session['blog_nid']).first()  # ;print(cur_blog.nid)
258     if request.method == 'GET':
259         condition = {}
260         for k,v in kwargs.items():
261             kwargs[k] = int(v)
262             if v != '0':
263                 condition[k] = v
264 
265         articles =  models.Article.objects.filter(blog_id=cur_blog.nid,**condition)  # ;print(articles);print(condition)
266         category_list = models.Category.objects.all()
267         article_type_list = models.Article.type_choices
268         return render(request, 'backend_article.html',{'articles':articles,'category_list':category_list,'arg_dict':kwargs,'article_type_list':article_type_list})
269     elif request.method == 'POST':
270         pass
271     else:
272         return redirect('/')
273 
274 
275 @auth
276 def add_article(request):
277     """
278     添加文章
279     :param request:
280     :return:
281     """
282     cur_blog = models.Blog.objects.filter(nid=request.session['blog_nid']).first()
283     if request.method == 'GET':
284         categories = models.Category.objects.filter(blog=cur_blog)
285         tags = models.Tag.objects.filter(blog=cur_blog)
286         return render(request,'backend_add_article.html',{'categories':categories,'tags':tags,'blog_nid':cur_blog.nid})
287     elif request.method == 'POST':
288         res={'status':True,'error':None,'data':None}
289         # data = {}
290         # for k in request.POST.keys():
291         #     if k == 'tag':
292         #         data[k] = request.POST.getlist(k,None)
293         #     else:
294         #         data[k] = request.POST.get(k,None)
295         # # data['blog_nid'] = request.session['blog_nid']
296 
297         # 验证
298         # af = forms.ArticleForm(data);print(af.__dict__)
299         af = forms.ArticleForm(request.POST) # ;print(af.__dict__)
300         if af.is_valid():
301             with transaction.atomic():
302                 # 创建Article表数据
303                 obj = models.Article.objects.create(
304                     blog_id = af.cleaned_data['blog_nid'],
305                     title = af.cleaned_data['title'],
306                     summary = af.cleaned_data['summary'],
307                     article_type = af.cleaned_data['article_type'],
308                     category_id = af.cleaned_data['category'],
309                 )
310                 # 创建Article表与Tag表的多对多关系
311                 tag_list = []
312                 for tag_id in af.cleaned_data['tags']:
313                     tag_list.append(models.Article2Tag(article_id = obj.nid,tag_id = tag_id))
314 
315                 models.Article2Tag.objects.bulk_create(tag_list)
316 
317 
318                 # 创建ArticleDetail表数据
319                 models.ArticleDetail.objects.create(
320                     content = af.cleaned_data['content'],
321                     article = obj
322                 )
323         else:
324             res['status'] = False
325             res['error'] = af.errors.as_data()
326         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
327     else:
328         return redirect('/')
329 
330 
331 @auth
332 def edit_article(request,nid):
333     """
334     编辑文章
335     :param request:
336     :return:
337     """
338     cur_blog = models.Blog.objects.filter(nid=request.session['blog_nid']).first()
339     cur_article = models.Article.objects.filter(nid=nid).select_related('category','articledetail').first() # ;print(cur_article.tags.all())
340     if request.method == 'GET':
341         categories = models.Category.objects.filter(blog=cur_blog)
342         tags = models.Tag.objects.filter(blog=cur_blog)
343         return render(request, 'backend_edit_article.html',{'article':cur_article,'categories':categories,'tags':tags,'blog_nid':cur_blog.nid})
344     elif request.method == 'POST':
345         res = {'status':True,'error':None,'data':None}
346         af = forms.ArticleForm(request.POST) # ;print(request.POST)
347         if af.is_valid():
348             cur_article.title = af.cleaned_data['title']
349             cur_article.summary = af.cleaned_data['summary']
350             cur_article.articledetail.content = af.cleaned_data['content']
351             cur_article.article_type = af.cleaned_data['article_type']
352             cur_article.category_id = af.cleaned_data['category']
353             cur_article.save()
354 
355             with transaction.atomic():
356                 # 重设Article表 与 tag 表的多对多关系
357                 models.Article2Tag.objects.filter(article=cur_article).delete()
358                 for tid in af.cleaned_data['tags']:
359                     models.Article2Tag.objects.create(
360                         article=cur_article,
361                         tag_id=tid
362                     )
363 
364                 # 修改文章内容
365                 models.ArticleDetail.objects.filter(article_id = nid).update(content = af.cleaned_data['content'],)
366 
367         else:
368             res['status'] = False
369             res['error'] = af.errors.as_data()
370         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
371     else:
372         return redirect('/')
373 
374 
375 
376 @auth
377 def del_article(request):
378     '''
379     删除文章
380     :param request:
381     :return:
382     '''
383     if request.method == 'POST':
384         res = {'status':True,'error':None,'data':None}
385         try:
386             a_id = request.POST.get('article_id',None)
387             models.Article.objects.filter(nid=a_id).delete()
388         except Exception as e:
389             res['status'] = True
390             res['error'] = str(e)
391         return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
392     else:
393         return redirect('/')
394 
395 @auth
396 def keUploadFile(request):
397     '''
398     KindEditor 上传文件API
399     :param request:
400     :return:
401     '''
402     if request.method == 'POST':
403         res = {'error':0,'url':None,'message':None}
404         file_type = request.GET.get('dir', None)
405         if file_type == 'image' or file_type == 'flash' or file_type == 'file':  # 暂时不考虑 flash 和 file 的情况
406             uf = request.FILES.get('imgFile',None)
407             save_path = os.path.join('static/imgs',uf.name)
408             with open(save_path,'wb') as f:
409                 for line in uf:
410                     f.write(line)
411             res['url'] = save_path
412         else:
413             res['error'] = 1
414             res['message'] = '上传类型不存在'
415         return HttpResponse(json.dumps(res))
416     else:
417         return redirect('/')
418 
419 
420 
421 @auth
422 def keFileManager(request):
423     '''
424     KindEditor 文件管理API
425     :param request:
426     :return:
427     '''
428 
429     dic = {}
430     root_path = r'E:\学习\Python_Practice\51cto\第七模块\EdmureBlog\static/'
431     static_root_path = '/static/'
432     request_path = request.GET.get('path')
433     if request_path:
434         abs_current_dir_path = os.path.join(root_path, request_path)
435         move_up_dir_path = os.path.dirname(request_path.rstrip('/'))
436         dic['moveup_dir_path'] = move_up_dir_path + '/' if move_up_dir_path else move_up_dir_path
437 
438     else:
439         abs_current_dir_path = root_path
440         dic['moveup_dir_path'] = ''
441 
442     dic['current_dir_path'] = request_path
443     dic['current_url'] = os.path.join(static_root_path, request_path)
444 
445     file_list = []
446     for item in os.listdir(abs_current_dir_path):
447         abs_item_path = os.path.join(abs_current_dir_path, item)
448         a, exts = os.path.splitext(item)
449         is_dir = os.path.isdir(abs_item_path)
450         if is_dir:
451             temp = {
452                 'is_dir': True,
453                 'has_file': True,
454                 'filesize': 0,
455                 'dir_path': '',
456                 'is_photo': False,
457                 'filetype': '',
458                 'filename': item,
459                 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
460             }
461         else:
462             temp = {
463                 'is_dir': False,
464                 'has_file': False,
465                 'filesize': os.stat(abs_item_path).st_size,
466                 'dir_path': '',
467                 'is_photo': True if exts.lower() in ['.jpg', '.png', '.jpeg'] else False,
468                 'filetype': exts.lower().strip('.'),
469                 'filename': item,
470                 'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
471             }
472 
473         file_list.append(temp)
474     dic['file_list'] = file_list
475     return HttpResponse(json.dumps(dic))
backend/views/user.py
  1 #! /usr/bin/env python3
  2 # -*- coding:utf-8 -*-
  3 # Author:Jailly
  4 
  5 from django.forms import Form
  6 from django.forms import fields
  7 from django.forms import widgets
  8 from repository import models
  9 from django.core.validators import ValidationError
 10 from django.db.models import Q
 11 from django.forms.models import ModelChoiceField
 12 
 13 class BlogForm(Form):
 14     username = fields.CharField()
 15     nickname = fields.CharField(max_length=64,required=False)
 16     site = fields.CharField(max_length=32,
 17                             error_messages={
 18                                 'max_length':'博客地址不能超过32个字符',
 19                                 'required':'请填写博客地址'
 20                             })
 21     theme = fields.ChoiceField(
 22         choices=((1,''),(2,''),(3,''),(4,''),(5,''))
 23     )
 24     title = fields.CharField(max_length=64,required=False,error_messages={'max_length':'内容过长'})
 25 
 26     def clean_site(self):
 27         # print(self.cleaned_data)
 28         if models.Blog.objects.exclude(user__username=self.cleaned_data['username']).filter(site=self.cleaned_data['site']).count():
 29             raise ValidationError('该地址已存在')
 30         else:
 31             return self.cleaned_data['site']
 32 
 33 
 34 class CategoryForm(Form):
 35     blog_nid = fields.CharField()
 36     title = fields.CharField(max_length=32,
 37                              error_messages={
 38                                  'max_length':'分类名称太长',
 39                                  'required':'请填写分类名称',
 40                              })
 41 
 42     def clean_title(self):
 43         if models.Category.objects.filter(blog__nid=self.cleaned_data['blog_nid'],title=self.cleaned_data['title']).count():
 44             raise ValidationError('您已经在使用该分类')
 45         else:
 46             return self.cleaned_data['title']
 47 
 48 
 49 class CategoryForm2(Form):
 50     nid = fields.CharField()
 51     title = fields.CharField(max_length=32,
 52                              error_messages={
 53                                  'max_length':'分类名称太长',
 54                                  'required':'请填写分类名称',
 55                              })
 56 
 57 
 58 
 59 class TagForm(Form):
 60     blog_nid = fields.CharField()
 61     title = fields.CharField(max_length=32,
 62                              error_messages={
 63                                  'max_length': '标签名称太长',
 64                                  'required': '请填写标签名称',
 65                              })
 66 
 67     def clean_title(self):
 68         if models.Tag.objects.filter(blog__nid=self.cleaned_data['blog_nid'],title=self.cleaned_data['title']).count():
 69             raise ValidationError('您已经拥有该标签')
 70         else:
 71             return self.cleaned_data['title']
 72 
 73 
 74 class TagForm2(Form):
 75     nid = fields.CharField()
 76     title = fields.CharField(max_length=32,
 77                              error_messages={
 78                                  'max_length': '标签名称太长',
 79                                  'required': '请填写标签名称',
 80                              })
 81 
 82 class ArticleForm(Form):
 83     blog_nid = fields.CharField()
 84     title = fields.CharField(max_length=128,
 85                              error_messages={
 86                                  'max_length':'文章标题不能超过128个字符',
 87                                  'required':'请填写文章标题'
 88                              })
 89     summary = fields.CharField(max_length=255,
 90                                error_messages={
 91                                    'max_length':'文章简介不能超过255个字符',
 92                                    'required':'请填写文件简介',
 93                                })
 94     content = fields.CharField(error_messages={'required':'请填写内容'})
 95 
 96     article_type = fields.ChoiceField(
 97         choices=((1,'Python'),(2,'Linux'),(3,'OpenStack'),(4,'GoLang'),),
 98         required=False
 99     )
100 
101     category = fields.ChoiceField()
102     
103     tags = fields.MultipleChoiceField()
104     
105     def __init__(self,*args,**kwargs):
106         super(ArticleForm, self).__init__(*args,**kwargs)
107         self.fields['category'].choices = models.Category.objects.filter(blog__nid=self.data['blog_nid']).values_list('nid','title')
108         self.fields['tags'].choices = models.Tag.objects.filter(blog__nid=self.data['blog_nid']).values_list('nid','title')
109         # print(self.fields['tag'].choices)
backend/forms.py
 1 from django.conf.urls import url
 2 from django.conf.urls import include
 3 from .views import user
 4 
 5 urlpatterns = [
 6     url(r'^base-info.html$', user.base_info),
 7 
 8     url(r'^tag.html$', user.tag),
 9     url(r'^del_tag.html$',user.del_tag),
10     url(r'^edit_tag.html$',user.edit_tag),
11 
12     url(r'^category.html$', user.category),
13     url(r'^del_ctgy.html$', user.del_ctgy),
14     url(r'^edit_ctgy.html$', user.edit_ctgy),
15 
16     url(r'^article-(?P<article_type>\d+)-(?P<category_id>\d+).html$', user.article),
17     url(r'^add-article.html$', user.add_article),
18     url(r'^del-article.html$', user.del_article),
19     url(r'^edit-article-(\d+).html$', user.edit_article),
20 
21     url(r'^uploadAvatar.html$',user.uploadAvatar),
22 
23     url(r'^ke-upload-file.html$',user.keUploadFile),
24     url(r'^ke-file-manager.html$',user.keFileManager),
25 ]
backend/urls.py
 1 """EdmureBlog URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from django.conf.urls import include
18 from django.contrib import admin
19 
20 urlpatterns = [
21     url(r'^admin/docs/', include('django.contrib.admindocs.urls')),
22     url(r'^admin/', admin.site.urls),
23     url(r'^backend/', include('backend.urls')),
24     url(r'^', include('web.urls')),
25 ]
EdmureBlog/urls.py
  1 from django.db import models
  2 
  3 
  4 class UserInfo(models.Model):
  5     """
  6     用户表
  7     """
  8     nid = models.BigAutoField(primary_key=True)
  9     username = models.CharField(verbose_name='用户名', max_length=32, unique=True)
 10     password = models.CharField(verbose_name='密码', max_length=64)
 11     nickname = models.CharField(verbose_name='昵称', max_length=32,null=True)
 12     email = models.EmailField(verbose_name='邮箱', unique=True)
 13     avatar = models.ImageField(verbose_name='头像',null=True)
 14 
 15     create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
 16 
 17     fans = models.ManyToManyField(verbose_name='粉丝们', to='UserInfo', through='UserFans',
 18                                   through_fields=('user', 'follower'))
 19 
 20 
 21 class Blog(models.Model):
 22     """
 23     博客信息
 24     """
 25     nid = models.BigAutoField(primary_key=True)
 26     title = models.CharField(verbose_name='个人博客标题', max_length=64,null=True)
 27     site = models.CharField(verbose_name='个人博客前缀', max_length=32, unique=True)
 28     theme = models.CharField(verbose_name='博客主题', max_length=32)
 29     user = models.OneToOneField(to='UserInfo', to_field='nid',on_delete=models.CASCADE)
 30 
 31 
 32 class UserFans(models.Model):
 33     """
 34     互粉关系表
 35     """
 36     user = models.ForeignKey(verbose_name='博主', to='UserInfo', to_field='nid', related_name='users',on_delete=models.CASCADE)
 37     follower = models.ForeignKey(verbose_name='粉丝', to='UserInfo', to_field='nid', related_name='followers',on_delete=models.CASCADE)
 38 
 39     class Meta:
 40         unique_together = [
 41             ('user', 'follower'),
 42         ]
 43 
 44 
 45 class Category(models.Model):
 46     """
 47     博主个人文章分类表
 48     """
 49     nid = models.AutoField(primary_key=True)
 50     title = models.CharField(verbose_name='分类标题', max_length=32)
 51 
 52     blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
 53 
 54 
 55 class ArticleDetail(models.Model):
 56     """
 57     文章详细表
 58     """
 59     content = models.TextField(verbose_name='文章内容', )
 60 
 61     article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid',on_delete=models.CASCADE)
 62 
 63 
 64 class UpDown(models.Model):
 65     """
 66     文章顶或踩
 67     """
 68     article = models.ForeignKey(verbose_name='文章', to='Article', to_field='nid',on_delete=models.CASCADE)
 69     user = models.ForeignKey(verbose_name='赞或踩用户', to='UserInfo', to_field='nid',on_delete=models.CASCADE)
 70     up = models.BooleanField(verbose_name='是否赞')
 71 
 72     class Meta:
 73         unique_together = [
 74             ('article', 'user'),
 75         ]
 76 
 77 
 78 class Comment(models.Model):
 79     """
 80     评论表
 81     """
 82     nid = models.BigAutoField(primary_key=True)
 83     content = models.CharField(verbose_name='评论内容', max_length=255)
 84     create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
 85 
 86     # reply = models.ForeignKey(verbose_name='回复评论', to='self', related_name='back', null=True,on_delete=models.CASCADE)
 87     reply = models.ManyToManyField(verbose_name='回复评论', to='self', related_name='back', null=True,symmetrical=False)
 88     article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid',on_delete=models.CASCADE)
 89     user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid',on_delete=models.CASCADE)
 90 
 91 
 92 class Tag(models.Model):
 93     nid = models.AutoField(primary_key=True)
 94     title = models.CharField(verbose_name='标签名称', max_length=32)
 95     blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
 96 
 97 
 98 class Article(models.Model):
 99     nid = models.BigAutoField(primary_key=True)
100     title = models.CharField(verbose_name='文章标题', max_length=128)
101     summary = models.CharField(verbose_name='文章简介', max_length=255)
102     read_count = models.IntegerField(default=0)
103     comment_count = models.IntegerField(default=0)
104     up_count = models.IntegerField(default=0)
105     down_count = models.IntegerField(default=0)
106     create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
107 
108     blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid',on_delete=models.CASCADE)
109     category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True,on_delete=models.CASCADE)
110 
111     type_choices = [
112         (1, "Python"),
113         (2, "Linux"),
114         (3, "OpenStack"),
115         (4, "GoLang"),
116     ]
117 
118     article_type = models.IntegerField(choices=type_choices, default=None)
119 
120     tags = models.ManyToManyField(
121         to="Tag",
122         through='Article2Tag',
123         through_fields=('article', 'tag'),
124     )
125 
126 
127 class Article2Tag(models.Model):
128     article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid',on_delete=models.CASCADE)
129     tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid',on_delete=models.CASCADE)
130 
131     class Meta:
132         unique_together = [
133             ('article', 'tag'),
134         ]
repository/models.py

static - 略

templates - 略

  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 from io import BytesIO
  4 from django.shortcuts import HttpResponse, redirect, render
  5 from utils.check_code import create_validate_code
  6 from web import forms
  7 import json
  8 from utils.custom_json_encoder import CustomJsonEncoder
  9 from repository import models
 10 
 11 
 12 def check_code(request):
 13     """
 14     验证码
 15     :param request:
 16     :return:
 17     """
 18     stream = BytesIO()
 19     img, code = create_validate_code()
 20     img.save(stream, 'PNG')
 21     request.session['checkCode'] = code
 22     print(code)
 23     return HttpResponse(stream.getvalue())
 24 
 25 
 26 def save_session(request):
 27     '''
 28     注册/登录成功时,保存session
 29     :param request:
 30     :return:
 31     '''
 32     # username = request.POST['username']
 33     # cur_userS = models.UserInfo.objects.filter(username=username)
 34     # cur_user = cur_userS.values('username', 'email', 'avatar','nickname').first()
 35     # request.session['cur_user'] = cur_user
 36     #
 37     # cur_blogS = models.Blog.objects.filter(user__username=username)
 38     # cur_blog = cur_blogS.values('title').first()
 39     # request.session['cur_blog'] = cur_blog if cur_blog else ''  # django的session只能接受可以被json序列化的对象,None不能被序列化
 40     #
 41     # cur_categoriesS = models.Category.objects.filter(blog__title=cur_blogS.first().title)
 42     # cur_categories = cur_categoriesS.values('title')
 43     # request.session['cur_categories'] = cur_categories if cur_categories else ''
 44     #
 45     # cur_tagsS = models.Tag.objects.filter(blog=cur_blogS.first())
 46     # cur_tags = cur_tagsS.values('title')
 47     # request.session['cur_tags'] = cur_tags if cur_tags else ''
 48     #
 49     # cur_articlesS = models.Article.objects.filter(blog=cur_blogS.first())
 50     # cur_articles = cur_articlesS.values('title','summary','read_count','comment_count','up_count','down_count','blog__site','category__title','tags__title')
 51     # request.session['cur_articles'] = cur_articles if cur_articles else ''
 52 
 53     request.session['username'] = request.POST['username']
 54     cur_blog = models.Blog.objects.filter(user__username=request.POST['username'])
 55     request.session['blog_nid'] = cur_blog.first().nid if cur_blog else ''
 56 
 57 
 58 def login(request):
 59     """
 60     登陆
 61     :param request:
 62     :return:
 63     """
 64     if request.method == 'GET':
 65         return render(request, 'login.html')
 66     elif request.method == 'POST':
 67 
 68         lf = forms.LoginForm(request.POST)
 69         res = {'status': True, 'error': None, 'data': None}
 70         if lf.is_valid():
 71             if request.POST['checkCode'].upper() == request.session['checkCode'].upper():
 72                 # 判断是否勾选自动登陆
 73                 if request.POST.get('autoLogin', None):
 74                     request.session.set_expiry(60 * 60 * 24 * 30)
 75                 # 保存session
 76                 save_session(request)
 77             else:
 78                 res['status'] = False # 方法2:重构传入 Form 中的参数,将session中的checkcode传入,由Form来验证。哪种方法更好?
 79                 res['error'] = {'checkCode': ['验证码错误', ]}
 80         else:
 81             res['status'] = False
 82             res['error'] = lf.errors.as_data()
 83             if request.POST['checkCode'].upper() != request.session['checkCode'].upper():
 84                 res['error']['checkCode'] = ['验证码错误', ]
 85 
 86         return HttpResponse(json.dumps(res, cls=CustomJsonEncoder))
 87     else:
 88         return redirect('/')
 89 
 90 
 91 def register(request):
 92     """
 93     注册
 94     :param request:
 95     :return:
 96     """
 97 
 98     if request.method == 'GET':
 99         return render(request, 'register.html')
100     elif request.method == 'POST':
101         res = {'status': True, 'error': None, 'data': None}
102         rf = forms.RegisterForm(request.POST)
103         # print(request.POST)
104         if rf.is_valid():
105             # 判断验证码是否输入正确
106             if request.session['checkCode'].upper() != request.POST.get('checkCode', None).upper():
107                 res['status'] = False
108                 res['error'] = {'checkCode': ['验证码错误', ]}
109             else:
110                 request.session['username'] = request.POST.get('username', None)
111                 # 将注册信息存储入数据库
112                 username = request.POST.get('username', None)
113                 password = request.POST.get('password', None)
114                 email = request.POST.get('email', None)
115                 models.UserInfo.objects.create(username=username, password=password, email=email)
116                 save_session(request)
117 
118         else:
119             res['status'] = False
120             res['error'] = rf.errors.as_data()
121             # 向ErrorDict中添加验证码项
122             res['error']['checkCode'] = ['验证码错误', ] if request.session['checkCode'].upper() != request.POST.get(
123                 'checkCode', None).upper() else ''
124 
125         return HttpResponse(json.dumps(res, cls=CustomJsonEncoder))
126     else:
127         return redirect('/index.html')
128 
129 
130 def logout(request):
131     '''
132     注销
133     :param request:
134     :return:
135     '''
136     request.session.clear()
137     return redirect('/')
138 
139 # def t(request):
140 #     if request.method == 'GET':
141 #         f = forms.LoginForm()
142 #         return render(request,'t.html',{'f':f})
143 #
144 #     elif request.method == 'POST':
145 #         print(request.POST)
146 #         f = forms.LoginForm(request.POST)
147 #         if f.is_valid():
148 #             print(f.cleaned_data)
149 #             return HttpResponse(f.cleaned_data)
150 #         else:
151 #             print(f.errors)
152 #             return HttpResponse(f.errors)
web/views/account.py
  1 #!/usr/bin/env python
  2 # -*- coding:utf-8 -*-
  3 from django.shortcuts import render, redirect, HttpResponse
  4 from repository import models
  5 from web import forms
  6 import json,datetime
  7 from utils.custom_json_encoder import CustomJsonEncoder
  8 from dateutil.relativedelta import relativedelta
  9 from django.db.models import F
 10 from django.db import transaction
 11 from django.utils.safestring import mark_safe
 12 
 13 def index(request):
 14     """
 15     博客首页,展示全部博文
 16     :param request:
 17     :return:
 18     """
 19     article_list = models.Article.objects.all()  # ;print('index')
 20 
 21     if request.session.get('blog_nid',None):
 22         visitor_blog = models.Blog.objects.filter(nid = request.session['blog_nid']).values('site').first()
 23     else:
 24         visitor_blog = None
 25 
 26     return render(request, 'index.html', {'article_list': article_list,'visitor_blog':visitor_blog})
 27 
 28 
 29 def home(request, site):
 30     """
 31     博主个人首页
 32     :param request:
 33     :param site: 博主的网站后缀如:http://xxx.com/wupeiqi.html
 34     :return:
 35     """
 36     if request.method == 'GET':
 37         if request.session.get('username', None):
 38             visitor = models.UserInfo.objects.filter(username=request.session['username']).select_related('blog').first()
 39             visitor_blog = visitor.blog
 40         else:
 41             visitor = ''
 42             visitor_blog = ''
 43 
 44         cur_blog = models.Blog.objects.filter(site=site).select_related('user').first()
 45         cur_user = cur_blog.user
 46         articles = models.Article.objects.filter(blog=cur_blog)
 47 
 48         # d_c 是 由日期(年+月)和文章数组成的元组们,组成的列表
 49         dates = models.Article.objects.filter(blog=cur_blog).dates('create_time','month',order='DESC')
 50         d_c = []
 51         for d in dates:
 52             c = models.Article.objects.filter(create_time__date__gte = d,create_time__date__lte=d+relativedelta(months=1)).count()
 53             d_c.append((d,c))
 54 
 55         return render(request, 'home.html', {'blog': cur_blog,'user':cur_user,'visitor_blog': visitor_blog,'visitor':visitor,'d_c':d_c,'articles':articles})
 56     else:
 57         return redirect('/')
 58 
 59 
 60 def filter(request, site, condition, val):
 61     """
 62     分类显示
 63     :param request:
 64     :param site:
 65     :param condition:
 66     :param val:
 67     :return:
 68     """
 69     cur_blog = models.Blog.objects.filter(site=site).select_related('user').first()
 70     cur_user = cur_blog.user
 71     template_name = "home_summary_list.html"
 72 
 73     if request.session.get('username', None):
 74         visitor = models.UserInfo.objects.filter(username=request.session['username']).select_related('blog').first()
 75         visitor_blog = visitor.blog
 76     else:
 77         visitor = ''
 78         visitor_blog = ''
 79 
 80     # d_c 是 由日期(年+月)和文章数组成的元组们,组成的列表
 81     dates = models.Article.objects.filter(blog=cur_blog).dates('create_time','month',order='DESC')
 82     d_c = []
 83     for d in dates:
 84         c = models.Article.objects.filter(create_time__date__gte = d,create_time__date__lte=d+relativedelta(months=1)).count()
 85         d_c.append((d,c))
 86 
 87     if condition == 'tag':
 88         article_list = models.Article.objects.filter(tags__title=val, blog=cur_blog).all()
 89     elif condition == 'category':
 90         article_list = models.Article.objects.filter(category__title=val, blog=cur_blog).all()
 91     elif condition == 'date':
 92         article_list = models.Article.objects.filter(blog=cur_blog).extra(
 93             where=['date_format(create_time,"%%Y-%%m")=%s'], params=[val, ]).all()
 94     else:
 95         article_list = []
 96 
 97 
 98     return render(request, template_name,{'article_list':article_list,'blog': cur_blog,'user':cur_user,'visitor_blog': visitor_blog,'visitor':visitor,'d_c':d_c})
 99 
100 
101 def detail(request, site, nid):
102     """
103     博文详细页
104     :param request:
105     :param site:
106     :param nid:
107     :return:
108     """
109     cur_blog = models.Blog.objects.filter(site=site).select_related('user').first()
110     cur_article = models.Article.objects.filter(nid=nid).first()
111     cur_user = cur_blog.user
112     cur_comments = models.Comment.objects.filter(article=cur_article) #;print( mark_safe(cur_comments[5].content))
113     if request.method == 'GET':
114         if request.session.get('username', None):
115             visitor = models.UserInfo.objects.filter(username=request.session['username']).select_related('blog').first()
116             visitor_blog = visitor.blog
117         else:
118             visitor = ''
119             visitor_blog = ''
120 
121         # 阅读数 +1
122         cur_article.read_count += 1
123         cur_article.save()
124 
125         # d_c 是 由日期(年+月)和文章数组成的元组们,组成的列表
126         dates = models.Article.objects.filter(blog=cur_blog).dates('create_time', 'month', order='DESC')
127         d_c = []
128         for d in dates:
129             c = models.Article.objects.filter(create_time__date__gte=d,
130                                               create_time__date__lte=d + relativedelta(months=1)).count()
131             d_c.append((d, c))
132 
133         return render(request, 'home_detail.html',
134                       {'article': cur_article, 'user': cur_user, 'blog': cur_blog, 'comments': cur_comments,
135                        'visitor_blog': visitor_blog,'visitor':visitor,'d_c':d_c})
136     elif request.method == 'POST':  # 可以做对 提交评论 的处理,或者 让 提交评论 由单独的视图函数处理
137         pass
138     else:
139         return redirect('/')
140 
141 def auth(func):
142     '''
143     验证 ajax请求的操作者是否是登录用户
144     :param request:
145     :return:
146     '''
147 
148     def wrapper(request,*args,**kwargs):
149         if request.session.get('username',None):
150             return func(request,*args,**kwargs)
151         else:
152             res = {'status': False, 'error': '请登录', 'data': None}
153             return HttpResponse(json.dumps(res))
154     return wrapper
155 
156 @auth
157 def comment(request):
158     '''
159     对 用户对某文章提交评论 的处理
160     :param request:
161     :return:
162     '''
163     if request.method == 'POST':
164         res = {'status': True, 'error': None, 'data': None}
165         cf = forms.CommentForm(request.POST)
166         if cf.is_valid():
167             with transaction.atomic():
168                 cur_user = models.UserInfo.objects.filter(username=cf.cleaned_data['username']).first()
169                 c_obj = models.Comment.objects.create(
170                     content=cf.cleaned_data['content'],
171                     user=cur_user,
172                     article_id=cf.cleaned_data['article_id']
173                 )
174                 print(cf.cleaned_data['content'])
175                 # print('reply:',cf.cleaned_data['reply']);print('reply_class:',type(cf.cleaned_data['reply']))
176                 reply_id_list = cf.cleaned_data['reply'].split(',') if cf.cleaned_data['reply'] else []
177                 c_obj.reply.add(*reply_id_list)
178 
179                 # 评论数+1
180                 models.Article.objects.filter(nid=cf.cleaned_data['article_id']).update(comment_count = F('read_count')+1 )
181 
182         else:
183             res['status'] = False
184             res['error'] = cf.errors.as_data()
185         return HttpResponse(json.dumps(res, cls=CustomJsonEncoder))
186     else:
187         return redirect('/')
188 
189 @auth
190 def updown(request):
191     '''
192     点赞/踩
193     :param request:
194     :return:
195     '''
196     res = {'status':True,'error':None,'data':None}
197     data = {};print(request.POST)
198     for k,v in request.POST.items():
199         data[k] = v
200     data['username'] = request.session['username'];print('data:',data)
201     udf = forms.UpDownForm(data)
202 
203     if udf.is_valid():
204         with transaction.atomic():
205             article = models.Article.objects.filter(nid=udf.cleaned_data['article_id']).first()
206             if udf.cleaned_data['up'] == '0':
207                 up = False
208                 res['data'] = article.down_count = article.down_count + 1
209             else:
210                 up = True
211                 res['data'] = article.up_count = article.up_count + 1
212 
213             article.save()
214             models.UpDown.objects.create(
215                 article=article,
216                 user = models.UserInfo.objects.filter(username=udf.cleaned_data['username']).first(),
217                 up=up,
218             )
219 
220     else:
221         res['status'] = False
222         res['error'] = udf.errors.as_data()
223 
224     return HttpResponse(json.dumps(res,cls=CustomJsonEncoder))
225 
226 
227 def t(request):
228     # print(request.GET)
229 
230     func = request.GET.get('callback',None)
231 
232     # return HttpResponse('callback(123456)')
233     return HttpResponse('%s("From EdmureBlog")'%func)
web/views/home.py
 1 #! /usr/bin/env python3
 2 #  -*- coding:utf-8 -*-
 3 
 4 from django.forms import Form
 5 from django.forms import fields
 6 from django.forms import widgets
 7 from repository import models
 8 from django.core.validators import ValidationError,RegexValidator
 9 
10 
11 class RegisterForm(Form):
12     '''验证注册信息'''
13     username = fields.CharField(max_length=32,error_messages={'required':'请填写用户名'})
14     password = fields.CharField(max_length=64,error_messages={'required':'请填写密码'})
15     confirm_password = fields.CharField(max_length=64,error_messages={'required':'请再次填写密码'})
16     email = fields.EmailField(error_messages={'required':'请填写邮箱'})
17     checkCode = fields.CharField(max_length=10,error_messages={'required':'请填写验证码'})
18 
19     def clean_username(self):
20         if models.UserInfo.objects.filter(username=self.cleaned_data['username']).count():
21             raise ValidationError('用户名已存在')
22         else:
23             return self.cleaned_data['username']
24 
25     def clean_confirm_password(self):
26         if self.cleaned_data['password'] == self.cleaned_data['confirm_password']:
27             return self.cleaned_data['confirm_password']
28         else:
29             raise ValidationError(message='两次密码输入不一致')
30 
31 
32 class LoginForm(Form):
33     '''验证登陆信息'''
34     username = fields.CharField(max_length=32,error_messages={'required':'请填写用户名'})
35     password = fields.CharField(max_length=64,error_messages={'required':'请填写密码'})
36     checkCode = fields.CharField(max_length=10,
37                                  error_messages={'required':'请填写验证码'},
38                                  # validators={r'':''}
39                                  )
40     autoLogin = fields.CharField(max_length=1,required=False)
41 
42     def clean_username(self):
43         if models.UserInfo.objects.filter(username = self.cleaned_data['username']).count():
44             return self.cleaned_data['username']
45         else:
46             raise ValidationError('用户名不存在')
47 
48     def clean(self):
49         if self.cleaned_data.get('username',None):
50             if models.UserInfo.objects.filter(username=self.cleaned_data['username'],password=self.cleaned_data['password']).count():
51                 return self.cleaned_data
52             else:
53                 raise ValidationError('用户名或密码错误')
54 
55 
56 class CommentForm(Form):
57     '''验证文章评论'''
58     content = fields.CharField(error_messages={'required':'请填写评论内容',})
59     article_id = fields.CharField()
60     username = fields.CharField()
61     reply = fields.CharField(required=False,
62                              # validators={RegexValidator(r'(\d+,)*\d+'):'回复评论的id错误'}
63                              )
64 
65 
66 class UpDownForm(Form):
67     '''验证文章点赞/踩'''
68     article_id = fields.CharField(max_length=65535,error_messages={'required':'文章id 不存在'})
69     up = fields.CharField()
70     username = fields.CharField()
71 
72     def clean_atrticle_id(self):
73         if self.cleaned_data['article_id'].isdigit():
74             return self.cleaned_data['article_id']
75         else:
76             raise ValidationError('文章id必须是正整数')
77 
78     def clean_up(self):
79         if self.cleaned_data['up'] in ['0','1']:
80             return self.cleaned_data['up']
81         else:
82             raise ValidationError('up字段必须是0或1')
83 
84     def clean(self):
85         print(models.UpDown.objects.filter( user__username=self.cleaned_data['username'],article_id = self.cleaned_data['article_id'] ).count())
86         if models.UpDown.objects.filter( user__username=self.cleaned_data['username'],article_id = self.cleaned_data['article_id'] ).count():
87             raise ValidationError('请不要重复评价')
88         else:
89             return self.cleaned_data
web/forms.py
 1 """EdmureBlog URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r'^$', views.home, name='home')
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r'^$', Home.as_view(), name='home')
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r'^blog/', include('blog.urls'))
15 """
16 from django.conf.urls import url
17 from .views import home
18 from .views import account
19 
20 urlpatterns = [
21     # url(r'^t$', account.t),
22     url(r'^t$',home.t),
23     url(r'^login.html$', account.login),
24     url(r'^register.html$', account.register),
25     url(r'^check_code.html$', account.check_code),
26     url(r'^logout.html$', account.logout),
27     url(r'^comment.html$', home.comment),
28 
29     url(r'^updown.html$',home.updown),
30 
31     url(r'^(?P<site>\w+)/(?P<condition>((tag)|(date)|(category)))/(?P<val>(\w+-)*\w+).html', home.filter),
32     url(r'^(?P<site>\w+)/(?P<nid>\d+).html', home.detail),
33     url(r'^(?P<site>\w+).html', home.home),
34     url(r'^', home.index),
35 ]
web/urls.py

 

posted @ 2018-05-08 03:46  jailly  阅读(217)  评论(0编辑  收藏  举报