BBS

准备

  新建工程

    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">&laquo;</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
原文地址:https://www.cnblogs.com/hongpeng0209/p/6593143.html