zoukankan      html  css  js  c++  java
  • Django小项目简单BBS论坛

    开发一个简单的BBS论坛

    项目需求:

    复制代码
    1 整体参考“抽屉新热榜” + “虎嗅网”
    2 实现不同论坛版块
    3 帖子列表展示
    4 帖子评论数、点赞数展示
    5 在线用户展示
    6 允许登录用户发贴、评论、点赞
    7 允许上传文件
    8 帖子可被置顶
    9 可进行多级评论
    复制代码

    知识必备:(注:没有必备下面知识的同学,请返回去看会之后再看下面的内容防止蒙了~~!

    1 Django
    2 HTMLCSSJS
    3 BootStrap
    4 Jquery

    设计表结构

    1、表结构重要性

    在开发任何项目的时候,设计到数据库,第一个事情要做的是设计表结构。表结构设计不好就不要写代码,表结构是体现了你业务逻辑关系的。你的数据都要往数据库里存,其实表结构你要理清了你的架构也就出来了!

    2、设计表

     

    复制代码
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-from __future__ import unicode_literals
    from django.db import models
    from django.contrib.auth.models import User
    
    # Create your models here.class Article(models.Model):
        '''
        帖子表
        '''#标题最大长度255,不能重名
        title = models.CharField(u'文章标题',max_length=255,unique=True)
        #发布办款-使用外键关联Category
        category = models.ForeignKey("Category",verbose_name='板块名称')
        '''
        这里在admin中,title默认是显示英文的,我们可以在他的最前面加要给字段,在admin中就可以显示中文,他和verbose_name一样,什么时候必须使用
        verbose_name呢?比如上面的{category = models.ForeignKey("Category",verbose_name='板块名称')} 这个字段第一个字段是关联的类,这里
        就必须使用verbose_name
        '''#上传文件
        head_img = models.ImageField(upload_to="uploads")
        #文章内容(文章内容可能有很多,所以我们就不用"CharField"来写了,我们用TextField,不用规定他多长了,为可扩展长度)
        content = models.TextField(u"内容")
        #文章作者
        author = models.ForeignKey("UserProfile",verbose_name="作者")
        #发布日期
        publish_date = models.DateTimeField(auto_now=True,verbose_name="发布日期")
        #是否隐藏
        hidden = models.BooleanField(default=False,verbose_name="是否隐藏")
        #帖子的优先级
        priority = models.IntegerField(default=1000,verbose_name="优先级")
    
        def __unicode__(self):
            return "<%s,author:%s>" % (self.title,self.author)
    
    class Comment(models.Model):
        '''
        评论表
        '''#评论是基于文章的,并且一条评论只属于一个文章!对多的关系#一个文章可以有多个评论,一个评论只属于一个文章#评论文章
        article = models.ForeignKey("Article")
        #评论用户
        user = models.ForeignKey("UserProfile")
        #评论内容
        comment = models.TextField(max_length=1000)
        #评论时间
        date = models.DateTimeField(auto_now=True)
    
        #多级评论,是不是评论评论的当前的表(自己表),所以就得和自己做一个关联!#这里在关联自己的时候必须设置一个related_name否则会报错冲突#这里parent_comment,必须设置为可以为空,因为如果他是第一评论他是没有父ID的
        parent_comment = models.ForeignKey("self",related_name='p_comment',blank=True,null=True)
        '''
        prent self
        Null    1
        1       2
        1       3
        2       4
        通过上面的这种方法来记录,评论的级别关系!
        '''def __unicode__(self):
            return "<user:%s>" %(self.user)
    class ThumbUp(models.Model):
        '''
        点赞
        '''#给那个文章点的
        article = models.ForeignKey('Article')
        #用户名
        user = models.ForeignKey('UserProfile')
        #时间
        date = models.DateTimeField(auto_now=True)
    
    
    class Category(models.Model):
        '''
        板块表
        '''#板块名称
        name = models.CharField(max_length=64,unique=True,verbose_name="板块名称")
        #板块管理员
        admin = models.ManyToManyField("UserProfile",verbose_name="模块管理员")
        def __unicode__(self):
            return self.name
    
    class UserProfile(models.Model):
        '''
        用户表
        '''#使用Django提供的用户表,直接继承就可以了.在原生的User表里扩展!(原生的User表里就有用户名和密码)#一定要使用OneToOne,如果是正常的ForeignKey的话就表示User中的记录可以对应UserProfile中的多条记录!#并且OneToOne的实现不是在SQL级别实现的而是在代码基本实现的!
        user = models.OneToOneField(User)
        #名字
        name = models.CharField(max_length=32)
        #属组
        groups = models.ManyToManyField("UserGroup")
    
        def __unicode__(self):
            return self.name
    
    class UserGroup(models.Model):
        '''
        用户组表
        '''
        name = models.CharField(max_length=64,unique=True)
        def __unicode__(self):
            return self.name
    复制代码

     

    配置Django Admin

    配置admin注册model,不要忘记创建Django 管理员用户

    复制代码
    from django.contrib import admin
    import models
    # Register your models here.
    admin.site.register(models.Article)
    admin.site.register(models.Category)
    admin.site.register(models.Comment)
    admin.site.register(models.ThumbUp)
    admin.site.register(models.UserProfile)
    admin.site.register(models.UserGroup)
    复制代码

    我创建了几个板块,我在板块中查看的时候。只能看到下面简单的信息:

    这里我想看到板块中的ID或其他信息怎么办?

    复制代码
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-from django.contrib import admin
    import models
    # Register your models here.#给某个表专门的定制的类class CategoryAdmin(admin.ModelAdmin):
        list_display = ('id','name')
    
    class ArticleAdmin(admin.ModelAdmin):
        list_display = ('id','title','author','hidden','publish_date')
    
    admin.site.register(models.Article,ArticleAdmin) #把自定义的类绑定到注册的类中
    admin.site.register(models.Category,CategoryAdmin)  #把自定义的类绑定到注册的类中admin.site.register(models.Comment)
    admin.site.register(models.ThumbUp)
    admin.site.register(models.UserProfile)
    admin.site.register(models.UserGroup)
    复制代码

    效果如下:

    前端页面&URLorViews配置

    1、url别名使用

    url里配置别名

    url(r'^category/(d+)/$',views.category,name='category'),

    html里配置的时候就只认那个别名了

              <li role="presentation"><a href="{% url 'category' 1 %}">欧美专区</a></li><li role="presentation"><a href="{% url 'category' 2 %}">日韩专区</a></li><li role="presentation"><a href="{% url 'category' 3 %}">印度专区</a></li>

    别名的好处:如果说那天想修改url里的这个url名称了,是不是所有前端都得修改!并且在有好几层的时候怎么改使用别名就会非常方便了!

    2、前端页面写完之后发现图片无法正常显示

    出现这个问题的原因:他能找到uploads这个目录吗?他能直接访问这个目录吗?他不能直接访问不了!

    • 一个是在Linux环境下做一个软连接连接过去

    如果在settings里加入uploads这个目录,但是这个方法还是有问题!他会去找/static/uploads/uploads目录,看下面的图!

    但是通过下面的方式就可以访问(原因就是因为:他去/static/uploads/uploads目录找了)

    2.2、我们自己写上传的方法

     定义form表单认证

    复制代码
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    # Tim Luo  LuoTianShuaifrom django import forms
    
    class ArticleForm(forms.Form):
        title = forms.CharField(max_length=255,min_length=5)
        summary = forms.CharField(max_length=255,min_length=5)
        head_img = forms.ImageField()
        content = forms.CharField(min_length=10)
        category_id = forms.IntegerField()
    复制代码

    定义上传方法

    复制代码
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    # Tim Luo  LuoTianShuaiimport os
    
    def handle_upload_file(f,request):  #f这里获取到文件句柄
        base_img_upload_path = 'static/Uploads'
        user_path = "%s/%s" % (base_img_upload_path,request.user.userprofile.id)
        if not os.path.exists(user_path):
            os.mkdir(user_path)
        with open('%s/%s'% (user_path,f.name),'wb+') as destinations:
            for chunk in f.chunks():
                destinations.write(chunk)
            #为了防止用户传送图片进行冲突,我们为每个用户进行创建用户return "/static/Uploads/%s/%s" % (request.user.userprofile.id,f.name)
    复制代码

    定义views

    复制代码
    def new_article(request):
        category_list = models.Category.objects.all()
        if request.method == 'POST':
            form = ArticleForm(request.POST,request.FILES)
            if form.is_valid():
                form_data = form.cleaned_data
                form_data['author_id'] = request.user.userprofile.id
    
                #自定义图片上传
                new_img_path = handle_upload_file(request.FILES['head_img'],request)
                #但是在views也保存了一份,我们给他改掉改成我们自己的就行了
                form_data['head_img'] = new_img_path
    
    
    
                #create只能返回成功失败,我想在创建完成之后返回文章的ID,直接下面那么写就可以print form_data
                new_article_obj = models.Article(**form_data)
                new_article_obj.save()#这个对象就直接返回了return render(request,'new_article.html',{'new_article_obj':new_article_obj}) #如果没有这个变量说明是创建新文章呢else:
                print form.errors
    
        return render(request,'new_article.html',{'category_list':category_list})
    复制代码

     

     

     

    多级评论实现

    用户可以直接对贴子进行评论,其它用户也可以对别的用户的评论再进行评论,也就是所谓的垒楼,如下图:

    所有的评论都存在一张表中, 评论与评论之前又有从属关系,如何在前端 页面上把这种层级关系体现出来?

    首先咱们在存储数据的时候是怎么来实现记录层级关系的呢?(下面的图是经过简化的把其他列隐藏了)

    我们在上面创建数据库表结构的时候,就定义了一个外键为他们自己(parent_comment_id),如果他没有父级别的ID说明他们是第一层,如果有说明他包含在一个评论之内!(仔细看上面的表结构)

    先把评论简化成一个这样的模型:

    复制代码
    data = [
        (None,'A'),
        ('A','A1'),
        ('A','A1-1'),
        ('A1','A2'),
        ('A1-1','A2-3'),
        ('A2-3','A3-4'),
        ('A1','A2-2'),
        ('A2','A3'),
        ('A2-2','A3-3'),
        ('A3','A4'),
        (None,'B'),
        ('B','B1'),
        ('B1','B2'),
        ('B1','B2-2'),
        ('B2','B3'),
        (None,'C'),
        ('C','C1'),
    
    ]
    复制代码

    转换为字典之后:

    复制代码
    data_dic = {
        'A': {
            'A1': {
                'A2':{
                    'A3':{
                        'A4':{}
                    }
                },
                'A2-2':{
                    'A3-3':{}
                }
            }
        },
        'B':{
            'B1':{
                'B2':{
                    'B3':{}
                },
                'B2-2':{}
            }
        },
        'C':{
            'C1':{}
        }
    
    }
    复制代码

    看上面的字典,我们能通过for循来获取他有多少层吗?当然不行,我们不知道他有多少层就没有办法进行找,或者通过while循环,最好是用递归进行一层一层的查找

    我们在前端展示的时候需要知道,那条数据是那一层的,不可能是垒下去的!因为他们是有层级关系的!

    我们用后端来实现:咱们给前端返回一个字典这样是不行的,咱们在后端把层级关系建立起来~返回的时候直接返回一个完整的HTML

    转换为字典之后就有层级关系了我们可以通过递归来实现了!上面再没有转换为字典的时候层级关系就不是很明确了!

     

    在循环的过程中不断的创建字典,先建立最顶级的,然后在一层一层的建立

    先通过一个简单的例子看下:

    复制代码
    #!/usr/bin/env python
    #-*- coding:utf-8 -*-
    # Tim Luo  LuoTianShuai
    
    
    data = [
        (None,'A'),
        ('A','A1'),
        ('A','A1-1'),
        ('A1','A2'),
        ('A1-1','A2-3'),
        ('A2-3','A3-4'),
        ('A1','A2-2'),
        ('A2','A3'),
        ('A2-2','A3-3'),
        ('A3','A4'),
        (None,'B'),
        ('B','B1'),
        ('B1','B2'),
        ('B1','B2-2'),
        ('B2','B3'),
        (None,'C'),
        ('C','C1'),
    
    ]
    
    def tree_search(d_dic,parent,son):
        #一层一层找,先拨第一层,一层一层往下找for k,v in d_dic.items():
            #举例来说我先遇到A,我就把A来个深度查询,A没有了在找Bif k == parent:#如果等于就找到了parent,就吧son加入到他下面
                d_dic[k][son] = {} #son下面可能还有儿子#这里找到就直接return了,你找到就直接退出就行了returnelse:
                #如果没有找到,有可能还有更深的地方,的需要剥掉一层            tree_search(d_dic[k],parent,son)
    
    
    
    data_dic = {}
    
    for item in data:
        # 每一个item代表两个值一个父亲一个儿子
        parent,son = item
        #先判断parent是否为空,如果为空他就是顶级的,直接吧他加到data_dicif parent is None:
            data_dic[son] = {}  #这里如果为空,那么key就是他自己,他儿子就是一个空字典else:
            '''
            如果不为空他是谁的儿子呢?举例来说A3他是A2的儿子,但是你能直接判断A3的父亲是A2你能直接判断他是否在A里面吗?你只能到第一层.key
            所以咱们就得一层一层的找,我们知道A3他爹肯定在字典里了,所以就得一层一层的找,但是不能循环找,因为你不知道他有多少层,所以通过递归去找
            直到找到位置
            '''
            tree_search(data_dic,parent,son) #因为你要一层一层找,你的把data_dic传进去,还的把parent和son传进去for k,v in data_dic.items():
        print(k,v)
    复制代码

    执行结果:(完美)

    ('A', {'A1': {'A2': {'A3': {'A4': {}}}, 'A2-2': {'A3-3': {}}}, 'A1-1': {'A2-3': {'A3-4': {}}}})
    ('C', {'C1': {}})
    ('B', {'B1': {'B2-2': {}, 'B2': {'B3': {}}}})

     2、前端返回

    当咱们把这个字典往前端返回的时候,前端模板里是没有一个语法递归的功能的,虽然咱们的层级关系已经出来了!所以咱们需要自定义一个模板语言然后拼成一个html然后返回给前端展示!

    2.1、配置前端吧数据传给simple_tag

    {% load custom_tags %}
    
    {% build_comment_tree article_obj.comment_set.select_related %}

    2.2、simple_tag获取数据然后把用户穿过来的数据进行转换为字典

    复制代码
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    def tree_search(d_dic,comment_obj):#这里不用传附近和儿子了因为他是一个对象,可以直接找到父亲和儿子for k,v_dic in d_dic.items():
            if k == comment_obj.parent_comment:#如果找到了
                d_dic[k][comment_obj] = {} #如果找到父亲了,你的把自己存放在父亲下面,并把自己当做key,value为一个空字典returnelse:#如果找不到递归查找            tree_search(d_dic[k],comment_obj)
    
    
    
    
    @register.simple_tag
    def build_comment_tree(comment_list):
        '''
        把评论传过来只是一个列表格式(如下),要把列别转换为字典,在把字典拼接为html
        [<Comment: <A,user:罗天帅>>, <Comment: <A2-1,user:罗天帅>>, <Comment: <A3-1,user:罗天帅>>, <Comment: <A2-2,user:罗天帅>>,
        <Comment: <A4-1,user:罗天帅>>, <Comment: <A4-2,user:罗天帅>>, <Comment: <A5-1,user:罗天帅>>, <Comment: <A3-2,user:罗天帅>>,
         <Comment: <B2,user:罗天帅>>, <Comment: <B2-1,user:罗天帅>>]
        :param comment_list:
        :return:
        '''
        comment_dic = {}
        #print(comment_list)for comment_obj in comment_list: #每一个元素都是一个对象if comment_obj.parent_comment is None: #如果没有父亲
                comment_dic[comment_obj] = {}
            else:
                #通过递归找            tree_search(comment_dic,comment_obj)
    
        # #测试:# for k,v in comment_dic.items():#     print(k,v)# 上面完成之后开始递归拼接字符串
    复制代码

    2、3生成html标签

    复制代码
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    def tree_search(d_dic,comment_obj):#这里不用传附近和儿子了因为他是一个对象,可以直接找到父亲和儿子for k,v_dic in d_dic.items():
            if k == comment_obj.parent_comment:#如果找到了
                d_dic[k][comment_obj] = {} #如果找到父亲了,你的把自己存放在父亲下面,并把自己当做key,value为一个空字典returnelse:#如果找不到递归查找            tree_search(d_dic[k],comment_obj)
    
    
    
    def generate_comment_html(sub_comment_dic):
        #先创建一个html默认为空
        html = ""for k,v_dic in sub_comment_dic.items():#循环穿过来的字典
            html += "<div  class='comment-node'>"  + k.comment + "</div>"#上面的只是把第一层加了他可能还有儿子,所以通过递归继续加if v_dic:
                html += generate_comment_html(v_dic)
        return html
    
    
    
    @register.simple_tag
    def build_comment_tree(comment_list):
        '''
        把评论传过来只是一个列表格式(如下),要把列别转换为字典,在把字典拼接为html
        [<Comment: <A,user:罗天帅>>, <Comment: <A2-1,user:罗天帅>>, <Comment: <A3-1,user:罗天帅>>, <Comment: <A2-2,user:罗天帅>>,
        <Comment: <A4-1,user:罗天帅>>, <Comment: <A4-2,user:罗天帅>>, <Comment: <A5-1,user:罗天帅>>, <Comment: <A3-2,user:罗天帅>>,
         <Comment: <B2,user:罗天帅>>, <Comment: <B2-1,user:罗天帅>>]
        :param comment_list:
        :return:
        '''
        comment_dic = {}
        #print(comment_list)for comment_obj in comment_list: #每一个元素都是一个对象if comment_obj.parent_comment is None: #如果没有父亲
                comment_dic[comment_obj] = {}
            else:
                #通过递归找            tree_search(comment_dic,comment_obj)
    
        # #测试:# for k,v in comment_dic.items():#     print(k,v)# 上面完成之后开始递归拼接字符串#div框架
        html = "<div class='comment-box'>"
        margin_left = 0
        for k,v in comment_dic.items():
            #第一层的html
            html += "<div class='comment-node'>" + k.comment + "</div>"#通过递归把他儿子加上
            html += generate_comment_html(v)
        html += "</div>"return mark_safe(html)
    复制代码

    效果如下:

    2.4、上面的看起来不是很好看怎么办?给他增加一个margin-left让他来显示层级效果,每次进行递归的时候给他加一个值!

    复制代码
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-from django import template
    from django.utils.safestring import mark_safe
    
    register = template.Library()
    
    def tree_search(d_dic,comment_obj):#这里不用传附近和儿子了因为他是一个对象,可以直接找到父亲和儿子for k,v_dic in d_dic.items():
            if k == comment_obj.parent_comment:#如果找到了
                d_dic[k][comment_obj] = {} #如果找到父亲了,你的把自己存放在父亲下面,并把自己当做key,value为一个空字典returnelse:#如果找不到递归查找            tree_search(d_dic[k],comment_obj)
    
    
    
    def generate_comment_html(sub_comment_dic,margin_left_val):
        #先创建一个html默认为空
        html = ""for k,v_dic in sub_comment_dic.items():#循环穿过来的字典
            html += "<div style='margin-left:%spx'  class='comment-node'>" % margin_left_val + k.comment + "</div>"#上面的只是把第一层加了他可能还有儿子,所以通过递归继续加if v_dic:
                html += generate_comment_html(v_dic,margin_left_val+15)
        return html
    
    
    
    @register.simple_tag
    def build_comment_tree(comment_list):
        '''
        把评论传过来只是一个列表格式(如下),要把列别转换为字典,在把字典拼接为html
        [<Comment: <A,user:罗天帅>>, <Comment: <A2-1,user:罗天帅>>, <Comment: <A3-1,user:罗天帅>>, <Comment: <A2-2,user:罗天帅>>,
        <Comment: <A4-1,user:罗天帅>>, <Comment: <A4-2,user:罗天帅>>, <Comment: <A5-1,user:罗天帅>>, <Comment: <A3-2,user:罗天帅>>,
         <Comment: <B2,user:罗天帅>>, <Comment: <B2-1,user:罗天帅>>]
        :param comment_list:
        :return:
        '''
        comment_dic = {}
        #print(comment_list)for comment_obj in comment_list: #每一个元素都是一个对象if comment_obj.parent_comment is None: #如果没有父亲
                comment_dic[comment_obj] = {}
            else:
                #通过递归找            tree_search(comment_dic,comment_obj)
    
        # #测试:# for k,v in comment_dic.items():#     print(k,v)# 上面完成之后开始递归拼接字符串#div框架
        html = "<div class='comment-box'>"
        margin_left = 0
        for k,v in comment_dic.items():
            #第一层的html
            html += "<div class='comment-node'>" + k.comment + "</div>"#通过递归把他儿子加上
            html += generate_comment_html(v,margin_left+15)
        html += "</div>"return mark_safe(html)
    复制代码

    效果如下:






  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 贪吃的大嘴
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 士兵排队问题
    Java实现 蓝桥杯VIP 算法提高 数字黑洞
    Minifilter微过滤框架:框架介绍以及驱动层和应用层的通讯
  • 原文地址:https://www.cnblogs.com/bruceg/p/5572687.html
Copyright © 2011-2022 走看看