准备
新建工程
django-admin startproject BBS
新建app
python3 manage.py startapp bbs
设计表结构
from django.db import models # Create your models here. from django.db import models from django.contrib.auth.models import User # Create your models here. class UserProifle(models.Model): user = models.OneToOneField(User) name = models.CharField(max_length=32) def __str__(self): return self.name class Article(models.Model): """文章表""" title = models.CharField(max_length=128,unique=True) author = models.ForeignKey("UserProifle") category = models.ForeignKey("Category") pub_date = models.DateTimeField(auto_now_add=True,auto_created=True) tags = models.ManyToManyField("Tag", null=True) body = models.TextField(max_length=100000) head_img = models.ImageField(upload_to="uploads") status_choices = ((0,'草稿'),(1,'发布'),(2,'隐藏')) priority = models.SmallIntegerField(default=1000,verbose_name="优先级") def __str__(self): return self.title class Category(models.Model): """板块""" name = models.CharField(max_length=64,unique=True) set_as_top_menu = models.BooleanField(default=True) def __str__(self): return self.name class Tag(models.Model): """标签表""" name = models.CharField(max_length=64, unique=True) def __str__(self): return self.name class Comment(models.Model): """评论""" article = models.ForeignKey("Article") p_node = models.ForeignKey("Comment",null=True,blank=True,related_name='my_child_comment') user = models.ForeignKey("UserProifle") date = models.DateTimeField(auto_now_add=True) comment = models.TextField(max_length=1024) def __str__(self): return self.comment class Like(models.Model): """点赞""" article = models.ForeignKey("Article") user = models.ForeignKey("UserProifle") date = models.DateTimeField(auto_now_add=True) class PrivateMail(models.Model): """私信""" pass
评论自关联,需要加上related_name,null=True默认可以为空,但是在django中需要加上blank=True
admin
django amdin是django提供的一个后台管理页面,该管理页面提供完善的html和css,使得你在通过Model创建完数据库表之后,就可以对数据进行增删改查,而使用django admin 则需要以下步骤:
- 创建后台管理员
- 配置url
- 注册和配置django admin后台管理页面
1、创建后台管理员
1
|
python manage.py createsuperuser |
2、配置后台管理url
1
|
url(r '^admin/' , include(admin.site.urls)) |
3、注册和配置django admin 后台管理页面
a、在admin中执行如下配置
1
2
3
4
5
6
7
8
|
from django.contrib import admin from bbs import models admin.site.register(models.UserProfile) admin.site.register(models.Article) admin.site.register(models.Comments) admin.site.register(models.Category) |
b、设置数据表名称
1
2
3
4
5
6
|
class UserType(models.Model): name = models.CharField(max_length = 50 ) class Meta: verbose_name = '用户类型' verbose_name_plural = '用户类型' |
c、打开表之后,设定默认显示,需要在model中作如下配置
1
2
3
4
5
|
class UserType(models.Model): name = models.CharField(max_length = 50 ) def __unicode__( self ): return self .name |
1
2
3
4
5
6
7
8
9
10
11
12
|
from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ( 'username' , 'password' , 'email' ) admin.site.register(models.UserType) admin.site.register(models.UserInfo,UserInfoAdmin) admin.site.register(models.UserGroup) admin.site.register(models.Asset) |
d、为数据表添加搜索功能
1
2
3
4
5
6
7
8
9
10
11
12
|
from django.contrib import admin from app01 import models class UserInfoAdmin(admin.ModelAdmin): list_display = ( 'username' , 'password' , 'email' ) search_fields = ( 'username' , 'email' ) admin.site.register(models.UserType) admin.site.register(models.UserInfo,UserInfoAdmin) admin.site.register(models.UserGroup) admin.site.register(models.Asset) |
bootstrap
bootstrap的三个文件夹css fonts js放入statics目录下
好看的页面下载全部到本地,会有个文件夹,包含了这个页面所用到的css,js,放入statics下的css和js目录下,下载下来的html页面中的css,js的引用路径需要换一下
页面有不同的板块,点击板块高亮并且显示对应板块下的文章
不同板块的都是同一模板html,只是显示的url不同,根据urls.py里面的别名做
html :{% url 'category' i.id%} --category是urls.py的name
urls.py: url(r'^category/(d+)/$',views.category,name='category') --前一个category是点击板块时显示的url
点击板块显示文章并且高亮
<li><a href="{% url 'category' i.id %}">{{ i.name }}</a></li>
views.py拿到这个id后去文章表中取出板块对应的文章
前端页面request.path得到当前页面的地址,通过js加上active,这段js需要写在base.html中
图标base
<link href="http://libs.baidu.com/fontawesome/4.0.3/css/font-awesome.css" rel="stylesheet">
head_img
simple_tag
上传的图片找不到需要进行以下步骤
app下新建templatetags目录
目录下新建xxoo.py
from django import template register = template.Library() @register.simple_tag def truncate_upload_img(img_src): return img_src.name.lstrip('/uploads/')
前端页面引用的时候
<img src="/static/{% truncate_upload_img i.head_img %}"
分页
views.py
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
paginator = Paginator(articles, 5) # Show 5 contacts per page
page = request.GET.get('page')url中传来的?page
try:
objs = paginator.page(page)
except PageNotAnInteger:
objs = paginator.page(1)
except EmptyPage:
objs = paginator.page(paginator.num_pages)
return render(request,'index.html',{'category':categories,'article':objs})
simple_tag
def paginator_btn(article,page): current_page = article.number if abs(current_page-page)<3: ele = """<li><a href="?page={page}">{page}</a></li>""".format(page=page) return mark_safe(ele) return ''
html
<nav aria-label="..."> <ul class="pagination"> <li class="disabled"><a href="#" aria-label="Previous"><span aria-hidden="true">«</span></a></li> {% for page in article.paginator.page_range %} {% if article.number == page %} <li class="active"><a href="?page={{ page }}">{{ page }}<span class="sr-only">(current)</span></a></li> {% else %} {% paginator_btn article page %} {% endif %} {% endfor %} </ul> </nav>
发布文章ckeditor
用modelform
forms.py
from django import forms from app01 import models class NewArticleForm(forms. ModelForm):
#修改modelform的new方法,给字段加样式 def __new__(cls, *args, **kwargs): for field_name in cls.base_fields: field = cls.base_fields[field_name] attr_dic = {'class':'form-control'} field.widget.attrs.update(attr_dic) return forms.ModelForm.__new__(cls)#还需要在调用下modelform原来的new方法 class Meta: model = models.Article fields = "__all__" exclude = ('priority','author')
html
因为有图片,所以form里面加上enctype='multipart/form-data'
{% extends 'index.html' %} {% block extra_source %} <script src="/static/plugins//ckeditor/ckeditor.js"></script> {% endblock %} {% block container %} <form method="post" enctype="multipart/form-data"> {% for field in form %} <div class="form-group"> <label class="col-sm-2 control-label">{{ field.name }}</label> <div class="col-sm-10"> {{ field }} <span style="color: red">{{ field.errors }}</span> <span style="color: red">{{ field.errors }}</span> </div> </div> {% endfor %} <input type="submit" class="col-lg-offset-5 btn btn-sm btn-success" value="提交"> </form> <script> // Replace the <textarea id="editor1"> with a CKEditor // instance, using default configuration. CKEDITOR.replace( 'id_body' ); </script> {% endblock %}
views.py
def new_article(request): if request.method == 'POST': form = forms.NewArticleForm(data=request.POST,files=request.FILES) if form.is_valid(): print(request.user.id) form.cleaned_data['author_id'] = request.user.id tags = form.cleaned_data.pop('tags') obj = models.Article(**form.cleaned_data) obj.save() obj.tags.add(*tags) obj.save() return HttpResponse('''<h3><a href="/article/%s/">%s</a></h3>''' % (obj.id,obj.title) ) elif request.method == 'GET': form = forms.NewArticleForm() return render(request,'new_article.html',{'form':form})
*文章和tag是多对多的关系,只能先create对象,再add tags
*因为添加modelform中去了author,所以只能在form.cleaned_data字典里面加上author_id,request.user.id就是当前用户的id
*{{article.body | safe}}
用户登录认证
from django.contrib.auth import authenticate,login,logout from django.contrib.auth.decorators import login_required#django自带的装饰器
def account(request):
error={}
if request.method == 'GET':
return render(request,'login.html')
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
user = authenticate(username=username,password=password)
#如果认证成功,user是个对象
if user:
login(request,user)#如果认证成功,需要再login一下才能显示当前用户
return redirect(request.GET.get('next') or '/category/all')
else:
error = {'error':'wrong username or password'}
return render(request,'login.html',error)
login.html
{% extends "base.html" %} {% block body %} <div class="container"> <div class="col-lg-3 col-lg-offset-4"> <form class="form-signin" method="post"> {% csrf_token %} <h2 class="form-signin-heading">1024黄鳝社区</h2> <label for="inputEmail" class="sr-only">Username</label> <input type="text" name="username" class="form-control" placeholder="username" required autofocus> <label for="inputPassword" class="sr-only">Password</label> <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" required> <span style="color: red">{{ error }}</span> <div class="checkbox"> <label> <input type="checkbox" value="remember-me"> Remember me </label> </div> <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button> </form> </div> </div> {% endblock %}
html
<ul class="nav navbar-nav navbar-right"> <li class=""><a href="{% url 'new_article' %}">发帖</a></li>#只有登录了,才能发帖,views.py里会验证 {% if request.user.is_authenticated %} #只有认证了,才显示当前用户 <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ request.user }} <span class="caret"></span></a> <ul class="dropdown-menu"> <li><a href="{% url 'logout' %}">Logout</a></li> </ul> </li> {% else %} <li class=""><a href="{% url 'login' %}">登录</a></li>#这里默认会是accounts/login,需要在settings.py中修改,写上LOGIN_URL ="/account/login/"
{% endif %} </ul>
退出
def acc_logout(request): logout(request) return redirect('/login')
多级评论
#1.把数据库里所有的这篇文章的评论查出来,转成字典
#2.递归循环字典,生成html
在文章html页加
<div style="border: 1px dashed red"> {% load_comments article%} </div>
通过simple_tag业务逻辑全写在bbs_tags.py中
from django.template import Library from django.utils.safestring import mark_safe register = Library() @register.simple_tag def load_comments(article): comment_dic = {}#评论存在字典中 comment_objs = article.comment_set.all().order_by('date')#通过文章反查comment for obj in comment_objs: if not obj.p_node:#就是顶级评论 comment_dic[obj] = {} else:#不是顶级评论 build_comment_tree(comment_dic,obj) comment_list = sorted(comment_dic.items(),key=lambda x:x[0].date)#按时间顺序排列 print(comment_dic) return(comment_dic) def build_comment_tree(comment_dic,obj): for k,v in comment_dic.items(): if obj.p_node == k:找到父级评论 comment_dic[k][obj]={} else:#开始深度查找 build_comment_tree(comment_dic[k],obj)
字典转换成html格式
@register.simple_tag def load_comments(article): comment_dic = {} comment_objs = article.comment_set.all().order_by('date') for obj in comment_objs: if not obj.p_node: comment_dic[obj] = {} else: build_comment_tree(comment_dic,obj) comment_list = sorted(comment_dic.items(),key=lambda x:x[0].date) print(comment_list) comment_html = """""" for comment_branch in comment_list: margin=0 html_ele = """<div style="border:1px dashed red">{user}--{date}--{comment}</div>""" .format(user=comment_branch[0].user, date=comment_branch[0].date.strftime('%Y-%M-%D %H:%M:%S'), comment = comment_branch[0].comment) comment_html += html_ele comment_html += build_comment_html(comment_branch[1],margin+20) return mark_safe(comment_html) def build_comment_tree(comment_dic,obj): for k in comment_dic: if obj.p_node == k: comment_dic[k][obj]={} else: build_comment_tree(comment_dic[k],obj) def build_comment_html(comment_branch,margin): comment_ele = """""" for k,v in comment_branch.items(): comment_ele += """<div style="border:1px dashed red;margin-left:{margin}px">{user}--{date}--{comment}</div>""" .format(margin=margin, user=k.user, date=k.date.strftime('%Y-%M-%D %H:%M:%S'), comment=k.comment, ) if v: comment_ele += build_comment_html(comment_branch[k],margin+20) return comment_ele