博客系统

1、建表阶段:

关系图:

解释:

这个表总共有十张表:

    用户表(userinfo):                

         建表原因:保存用户的信息。继承了django自带的AbstractUser用户信息表                              
 
        字段:
             nickname(昵称)                                                                     
             telephone(手机号)
             avatar(头像)
             create_time(创建时间)
    
    博主个人站点表(Blog):   
             建表原因:为了显示个人的所有博客信息
             字段:
                 title(个人博客标题)
                 site(个人博客后缀)
                 theme(个人博客主题)
                 user(对应用户)---------和用户表一对一
    文章分类表(Category):   
             建表原因:为了把文章细分成类
             字段:
                 title(文章标题)
                 blog(所属博客)---------和博客表多对一     

    文章表(Article):
             建表原因:存储个人文章的信息
             字段:
                 title(文章标题)
                 desc(文章描述)
                 read(文章阅读量)
                 comment_count(文章评论量)
                 up_count(文章置顶)
                 down_count(文章置末尾)
                 create_time(文章创建时间)
                 category(文章类型)----和类型表多对一
                 user(所属用户)--------和用户表多对一
                 tags(标签)------------和标签表多对多

    文章详情表(ArticleDetail):  
             建表原因:存储个人文章的详细信息    
             content(文章类容)
             article(所属文章)----------和文章表示一对一             

  
                 
    评论表(Comment):
             建表原因:对文章的评论    
             content(评论类容)
             create_time(创建时间)
             up_count(评论量)
             user(评论者)--------------和用户多对一
             article(评论文章)---------和文章表多对一
             parent_comment(父级评论)--和自己自关联(为的是能自己评论自己,可以评论别人的评论)
             
    评论点赞表(CommentUp):
             建表原因:对评论的点赞
             user(点赞的用户)------------和用户表多对一
             comment(被点赞评论)----------和评论表多对一

    
     文章点赞表(ArticleUp):
             建表原因:对文章的点赞     
             user(点赞的用户)------------和用户表多对一
             comment(被点赞文章)----------和文章表多对一     

    标签表(Tag):
             title(标签名称)
             bolg(所属博客)--------------和博客表多对一

    文章和标签第二张表(Article2Tag):
             建表原因:为了形成文章和标签的多对多关系,让后期添加数据更方便    
             article(文章)---------------和文章表多对一
             tag(标签)--------------------和标签表多对一    

 

数据库代码实现:

from django.db import models
from django.contrib.auth.models import AbstractUser       #admin 表中的有些字段就不会显示

# Create your models here.
class UserInfo(AbstractUser):  # settings:   AUTH_USER_MODEL = "blog.UserInfo"
    """
    用户信息
    """
    nid = models.BigAutoField(primary_key=True)
    nickname = models.CharField(verbose_name='昵称', max_length=32)
    telephone = models.CharField(max_length=11, blank=True, null=True, unique=True, verbose_name='手机号码')
    avatar = models.FileField(verbose_name='头像', upload_to='avatar', default="/avatar/default.png")
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    class Meta:
        verbose_name_plural = "用户信息表"
    def __str__(self):
        return self.username            #这里可以看做是验证成功返回的值


class Blog(models.Model):
    """
    站点信息
    """
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(verbose_name='个人博客标题', max_length=64)
    site = models.CharField(verbose_name='个人博客后缀', max_length=32, unique=True)
    theme = models.CharField(verbose_name='博客主题', max_length=32)
    user = models.OneToOneField(to='UserInfo', to_field='nid')
    class Meta:
        verbose_name_plural = "站点信息表"
    def __str__(self):
        return self.title


class Category(models.Model):
    """
    博主个人文章分类表
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='分类标题', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')         #一个站点多个博客
    def __str__(self):
        return self.title
    class Meta:
        verbose_name_plural="文章分类表"


class Article(models.Model):
    '''
    文章表,最主要的一张表
    '''
    nid = models.BigAutoField(primary_key=True)
    title = models.CharField(max_length=50, verbose_name='文章标题')
    desc = models.CharField(max_length=255, verbose_name='文章描述')     #外键用更加详细的描述
    read_count = models.IntegerField(default=0)
    comment_count = models.IntegerField(default=0)
    up_count = models.IntegerField(default=0)
    down_count = models.IntegerField(default=0)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)       # auto_now_add    当前的时间
    category = models.ForeignKey(verbose_name='文章类型', to='Category', to_field='nid', null=True)      #多个文章对应一个类型
    user = models.ForeignKey(verbose_name='所属用户', to='UserInfo', to_field='nid')              #多个文章对应一个用户
    tags = models.ManyToManyField(
        to="Tag",
        through='Article2Tag',
        through_fields=('article', 'tag'),
    )
    type_choices = [
        (1, "编程语言"),
        (2, "软件设计"),
        (3, "前端"),
        (4, "操作系统"),
        (5, "数据库"),
    ]
    article_type_id = models.IntegerField(choices=type_choices, default=None)
    class Meta:
        verbose_name_plural = "文章表"
    def __str__(self):
        return self.title


class ArticleDetail(models.Model):
    """
    文章详细表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField(verbose_name='文章内容', )
    article = models.OneToOneField(verbose_name='所属文章', to='Article', to_field='nid')   #每个文章的简介只是对应一片文章
    class Meta:
        verbose_name_plural = "文章详细表"
    def __str__(self):
        return self.content


class Comment(models.Model):
    """
    评论表
    """
    nid = models.BigAutoField(primary_key=True)
    content = models.CharField(verbose_name='评论内容', max_length=255)
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)
    up_count = models.IntegerField(default=0)
    user = models.ForeignKey(verbose_name='评论者', to='UserInfo', to_field='nid')     #评论表是由多个人组件起来的
    article = models.ForeignKey(verbose_name='评论文章', to='Article', to_field='nid')         #评论的是文章
    parent_comment = models.ForeignKey('self', blank=True, null=True, verbose_name='父级评论')     #自关联,父级评论
    class Meta:
        verbose_name_plural = "评论表"
    def __str__(self):
        return self.content


class CommentUp(models.Model):
    """
    评论点赞表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True)         #用户点赞
    comment = models.ForeignKey("Comment", null=True)       #对评论点赞
    class Meta:
        verbose_name_plural = "评论点赞表"



class ArticleUp(models.Model):
    """
    文章点赞表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey('UserInfo', null=True)    #用户点赞
    article = models.ForeignKey("Article", null=True)   #文章点赞
    class Meta:
        verbose_name_plural = "文章点赞表"


class Tag(models.Model):
    '''
    标签表
    '''
    nid = models.AutoField(primary_key=True)
    title = models.CharField(verbose_name='标签名称', max_length=32)
    blog = models.ForeignKey(verbose_name='所属博客', to='Blog', to_field='nid')     #多个标签属于一个博客,,
    class Meta:
        verbose_name_plural = "标签表"
    def __str__(self):
        return self.title


class Article2Tag(models.Model):
    '''
    文章标签的第二张表,自己新建的
    '''
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(verbose_name='文章', to="Article", to_field='nid')     #对应文章
    tag = models.ForeignKey(verbose_name='标签', to="Tag", to_field='nid')             #对应标签
    class Meta:
        unique_together = [
            ('article', 'tag'),
        ]

2、登录界面的实现:

界面显示:

 

前段代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客登录页面</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css/">
</head>
<body>
<div class="container col-md-offset-3 col-sm-6 " >
    <div class="panel panel-primary " style="margin-top:80px">
        <div class="panel-heading">请登录</div>
        <div class="panel-body">
            <form class="form-horizontal" method="post">
                {% csrf_token %}
                <div class="form-group ">
                    <label for="username" class="col-sm-3 control-label">账号:</label>
                    <div class="col-sm-8">
                        <input type="text" class="form-control" id="username" placeholder="账号" >
                    </div>
                </div>
                <div class="form-group">
                    <label for="password" class="col-sm-3 control-label">密码:</label>
                    <div class="col-sm-8">
                        <input type="password" class="form-control" id="password" placeholder="密码" >
                    </div>

                </div>
                <div class="form-group">

                    <label for="verify" class="col-sm-3 control-label">验证码:</label>
                    <div class="col-sm-3">
                        <input type="text" class="form-control" id="verify" placeholder="验证码">
                    </div>
                    <div class="col-md-4">
                        <img class="validCode_img" src="/get_validCode_img/" alt="" width="200px" height="35px">
                    </div>
                </div>
                <div id="qq" class="col-md-offset-4"></div>
                <dic class="error"></dic>
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="button" class="btn btn-primary login_btn" style="margin-left: 70px">登录</button>
                        <a href="/reg/" type="button" class="btn btn-info" id="refBtn">注册</a>
                    </div>

                </div>
            </form>
        </div>
    </div></div>

<script src="/static/jquery-3.2.1.js"></script>
<script src="/static/jquery.cookie.js"></script>
<script>
    $(".login_btn").click(function () {
        $.ajax({
            url:"/login/",
            type:"POST",
            headers: {"X-CSRFToken": $.cookie('csrftoken')},
            data:{
                "username":$("#username").val(),
                "password":$("#password").val(),
                "validCode":$("#verify").val()
            },
           success:function (data) {
               console.log(data);

               var response=JSON.parse(data);
               if (response["is_login"]){
                   location.href="/index/"
               }
               else {
                    $(".error").html(response["error_msg"]).css("color","red")
               }
           }
       });

        {#点击图片刷新验证码#}
        $(".validCode_img").click(function () {
        $(this)[0].src+="?";
    })})
</script>
</body>
</html>

后台登录代码示例:

def login(request):
    if request.method == "POST":
        print(request.POST)
        username=request.POST.get("username")
        password=request.POST.get("password")
        volidCode=request.POST.get("validCode")
        login_response={"is_login":False,"error_msg":None}

        if volidCode.upper()==request.session.get("keepValidCode").upper(): #对比验证码
            user = auth.authenticate(username=username,password=password)     #对比账户和密码
            if user: #如果user正确
                login_response["is_login"]=True   #就把login_response的值改为true
                auth.login(request,user)    #应为继承了uesrinfo的表所以就用
            else:
                login_response["error_msg"] = "用户名或密码错误"
        else:
            login_response["error_msg"] = '验证码错误'
        import json
        return HttpResponse(json.dumps(login_response))
    else:
        return render(request, "login.html")

后台验证码的代码示例:

def get_validCode_img(request):
    from io import BytesIO
    import random
    #导入的画笔包ImageDraw,字体包ImageFont
    from PIL import Image,ImageDraw,ImageFont

    img=Image.new(mode="RGB",size=(120,40) ,color=(random.randint(0,255),random.randint(0,255),random.randint(0,255)))
    draw=ImageDraw.Draw(img,"RGB")
    font=ImageFont.truetype("app01/static/font/kumo.ttf",25)

    valid_list=[]
    for i in range(5):
        random_num = str(random.randint(0, 9))
        random_lower_zimu = chr(random.randint(65, 90))
        random_upper_zimu = chr(random.randint(97, 122))

        random_char = random.choice([random_num, random_lower_zimu, random_upper_zimu])
        draw.text([5 + i * 24, 10], random_char, (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)),
                  font=font)
        valid_list.append(random_char)

        f = BytesIO()
        img.save(f, "png")
        data = f.getvalue()

        valid_str = "".join(valid_list)
        print(valid_str)

        request.session["keepValidCode"] = valid_str
    return HttpResponse(data)

  

 

流程客户端请求服务端的流程图:

 流程:

流程:

        
    1、客户端向————(第一次git请求)————>服务端返回登录页
    2、延验码-----src(url再次请求服务端)---->服务端执行验证码函数,生成验证码,保存到session中
    3、用户输入内容---点击提交发送agax请求---(post)------>服务端从ruquest中去agax请求的信息,
         从sessing中取出验证码进行和agax中的信息进行匹配
             成功-----进行下一步登录验证                              失败-----记录错误信息
                        |                                                      |
                         |                                                      |
                  验证用户信息的状态                             用字典的形式保存到login_response
                         |
      正确用:"is_login":true保存状态写入session
      错误:返回一个字段的字符串
                     
                     最后 return login_response

使用到的知识点;

    1,用ajax提交数据
      $(".log_btn").click(function(){
        $.ajax({
        url:url路径
        type:访问状态
        headers: {"X-CSRFToken": $.cookie('csrftoken')},#跨站请求访问
        返回的数据
        data:{
              字典名:$(".字段标识名").val()
        }
        
        接收数据
        sussecc:funsess(data){
        函数
        }            
        })
      }
    2、图片刷新(写在sussecc函数中)
       原理:给图片绑定一个点击事件:每次给src+?就会再次发送一次get请求,就起到了刷星的作用
       
       dom对象
       $(".validCode_img").click(function () {
       $(this)[0].src+="?";
View Code

 

 

3、注册功能实现

   

 

   代码示例

前端代码示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>

    <script src="/static/jquery-3.2.1.js"></script>
    <script src="/static/bootstrap-3.3.7/js/bootstrap.js"></script>

    <link rel="stylesheet" href="/static/bootstrap-3.3.7/css/bootstrap.min.css">
    <link rel="stylesheet" href="/static/css/reg.css">
  <script src="/static/jquery.cookie.js"></script>
<body>
<div class="container">
    <div class="row">
        <div class="col-md-5 col-md-offset-3">
            <form>
                      {% csrf_token %}
                      <div class="form-group">
                        <label for="username">用户名</label>
                        {{ form_obj.username }}
                      </div>
                      <div class="form-group">
                        <label for="password">密码</label>
                        {{ form_obj.password }}
                      </div>

                       <div class="form-group">
                        <label for="password">确认密码</label>
                           {{ form_obj.repeat_pwd }}
                      </div>

                      <div class="form-group">
                        <label for="email">邮箱</label>
                          {{ form_obj.email }}
                      </div>

                      <div class="form-group avatar">
                        <label for="avatar">头像选择</label>
                        <img src="/static/img/lll.jpg" alt="" id="avatar_img">
                        <input type="file" class="form-control" id="avatar_file" >
                      </div>


                    <input type="button" value="提交" class="btn btn-primary" id="subBtn"><span class="error"></span>

                </form>
        </div>
    </div>
</div>

<script>
    // 头像预览

    $("#avatar_file").change(function () {

        var ele_file = this.files[0];  //this.files

        var reader=new FileReader();
        reader.readAsDataURL(ele_file);//获取当前选择的图片路径
        reader.onload=function () {
            $("#avatar_img")[0].src=this.result
        }
    });



{#    提交数据#}
    $("#subBtn").click(function () {

        var formdata=new FormData();
        formdata.append("username",$("#id_username").val());
        formdata.append("password",$("#id_password").val());
        formdata.append("repeat_pwd",$("#id_repeat_pwd").val());
        formdata.append("email",$("#id_email").val());
        formdata.append("avatar_img",$("#avatar_file")[0].files[0]);

        $.ajax({
            url:"/reg/",
            type:'POST',
            data:formdata,
            contentType:false,
            processData:false,
            headers:{"X-CSRFToken":$.cookie('csrftoken')},
            success:function (data) {
                console.log(data);
                var data=JSON.parse(data);

                if (data.user){
                    location.href="/login/"
                }
                else {

                    console.log(data.errorsList);
                    $(".pull-right").html("").parent().removeClass("has-error");
                    //去除前面一次的错误信息
                    //parent().removeClass("has-error");去除input框的颜色
                    $.each(data.errorsList,function (i,j) {

                        console.log(i,j);
                        //   i           j
                        // username ["用户名不能为空"]
                        // password ["密码不能为空"]
                        //  repeat_pwd ["验证密码不能为空"]
                        //  email ["邮箱不能为空"]
                        $span=$("<span>");

                        $span.addClass("pull-right").css("color","red");
                        {# 添加一个span标签#}
                        $span.html(j[0]);
                        {#把log的值j写入span中#}
                        $("#id_"+i).after($span).parent().addClass("has-error")
                        //#id_"+i找到当前的错误的input框
                        //parent().addClass("has-error")给错误的input框加一个红色的颜色
                        if (i=="__all__"){
                            $("#id_repeat_pwd").after($span)
                            //全局钩子的错误信息

                        }
                    })


                }


            }
        })
    })
</script>
</body>
</html>
View Code

后台代码示例:

def reg(request):
    if request.method == "POST":
        #若果是post请求
        #进入RegForm中验证格式是否错误
        form_obj=RegForm(request.POST)
        regResponse={"user":None,"errorsList":None}
        #
        if form_obj.is_valid():
            # cleaned_data 正确的信息
            username=form_obj.cleaned_data["username"]
            password=form_obj.cleaned_data["password"]
            email=form_obj.cleaned_data.get("email")
            avatar_img=request.FILES.get("avatar_img")
            user_obj=models.UserInfo.objects.create_user(username=username,password=password,email=email,avatar=avatar_img,nickname=username)
            regResponse["user"]=user_obj.password
        else:
            regResponse["errorsList"]=form_obj.errors
            print("&&&&&&&&&&&&&&&&&&&&",form_obj.errors)
            print("===================",form_obj.cleaned_data)
            # #<ul class="errorlist"><li>email<ul class="errorlist"><li>邮箱不能为空</li></ul></li></ul>
        import json
        return HttpResponse(json.dumps(regResponse))
    else:
        form_obj=RegForm()
        return render(request,"reg.html",{"form_obj":form_obj})
        #如果是get请求将返回一个页面,并且请from生成的标签返回
View Code

后台生成form表单代码

from django import forms

from django.forms import widgets,ValidationError


from app01 import models

class RegForm(forms.Form):
    #账户  required=TRUE必须要填的  error_messages错误提示信息
    username=forms.CharField(max_length=12,min_length=2,required=True,
    error_messages={"required":"用户名不能为空","min_length":"用户名必须大于五位",},
    widget=widgets.TextInput(attrs={"class":"form-control","placeholder":"username"})
                             )
    #密码
    password=forms.CharField(min_length=6,required=True,
     error_messages={"required": "密码不能为空", "max_length": "用户名必须大于五位", },
     widget=widgets.PasswordInput(
     attrs={"class": "form-control", "placeholder": "password"}
    ))
    #确认密码
    repeat_pwd=forms.CharField(min_length=6,
     error_messages={"required": "验证密码不能为空", "max_length": "密码名必须大于六位", },
      widget=widgets.PasswordInput(attrs={"class": "form-control", "placeholder": "repeat_pwd"}
    ))
    # 邮件
    email=forms.EmailField(required=True,
        error_messages={"required": "邮箱不能为空" },
        widget=widgets.EmailInput(
        attrs={"class": "form-control", "placeholder": "email"}
    ))

    #用户名的钩子

    def clean_username(self):
        ret=models.UserInfo.objects.filter(username=self.cleaned_data.get("username"))
        print("****************************>>>>>",self.cleaned_data.get)
        print ("*****************************")
        if not ret:
            return self.cleaned_data.get("username")
        else:
            raise ValidationError("用户名已注册")
#密码的钩子
    def clean_password(self):
        data=self.cleaned_data.get("password")

        if not data.isdigit():
            return self.cleaned_data.get("password")
        else:
            raise ValidationError("密码不能全是数字")

#全局钩子
    def clean(self):
        if self.cleaned_data.get("password") == self.cleaned_data.get("repeat_pwd"):
            return self.cleaned_data
        else:
            raise  ValidationError("两次密码不一致")
View Code

 

代码流程

注册
	1. form组件生成html标签  
	    在RegFrom类中继承From这个类,
		利用这个类的方法,生成input标签,在这个类中可设置,标签的属性
		- username
		- password
		- email
	2. 利用后端传来的form在前端生成html标签
	    第一次get请求时后返回RegFrom类实例化的对象,
	        前端生成- username,- password,- emai,- avatar对应的四个标签
			
			显示图片:
			- 图片和上传文件折叠
				- 作用:上传文件覆盖图片,透明度为零 用户点击图片其实点击上传文件
				-把
				- 把上传文件和图片的父亲标签设置为相对路径 设置长宽,自己
			- 图片预览
				- 用户点击上传文件 上传文件的标签发生变化,会触发change事件
				- 取到用户上传的文件  var ele_file = this.files[0];  
				- 创建FileReader()对象4
				- 把上传文件的对象的路径写入FileReader()对象,结果为FileReader().result
				- FileReader()加载onload事件,把FileReader().result结果写入到img标签

	3. 用户提交数据
	     
		  用户通过ajav提交数据:
		  如果要传二进制的数据必须要用FormData打包
		         在传二进制的时候必须要到的参数
				         -------contentType:false,
                         ------processData:false,
		                 -----headers:{"X-CSRFToken":$.cookie('csrftoken')},防止跨站请求      
		       --用FormData打包数据
			      var formdata=new FormData();
                      formdata.append("username",$("#id_username").val());
			    --通过data把数据传到后台函数

				
	4.验证数据
	     post请求走Regfrom函数
		     -有错误利用RegForm 的钩子返回错误,----》
			        有错误将错误信息用键值对保存到errorst中
					没有错误将前端拿到的数据放入cleaned_data中
			    注册函数中判断form_obj.is_valid对象是否正确
			        ---正确则获取数据写入数据库---返回一个状态 regResponse["user"]=user_obj.username-----给前端
			        ---错误则获取数据写入数据库---返回一个状太regResponse["errorsList"]=form_obj.errors-----给前端
				 
			 
	     后台函数利用form_obj.cleaned_data拿到数据
	5、前端用ajax接收处理错误信息
	                   $.each(data.errorsList,function (i,j) {
{#                       controls.log(data.errorsList)#}
{#                       console.log(i,j);#}
                             //   i           j
                        ------- username ["用户名不能为空"]
                        ------- password ["密码不能为空"]
                        -------  repeat_pwd ["验证密码不能为空"]
                        -------  email ["邮箱不能为空"]
                       $span=$("<span>");
                       $span.addClass("pull-right").css("color","red");
                        --------{# 添加一个span标签#}
                       $span.html(j[0]);
                        --------{#把log的值j写入span中#}
                       $("#id_"+i).after($span).parent().addClass("has-error")
                       -------#id_"+i找到当前的错误的input框
                       -------parent().addClass("has-error")给错误的input框加一个红色的颜色
                       if (i=="__all__"){
                            $("#id_repeat_pwd").after($span)
                        -------全局钩子的错误信息
                       }
                   })

4、博客首页的实现

 

后台代码示例:

def index(request,*args,**kwargs):
    if kwargs:
        article_list=models.Article.objects.filter(site_article_category__name=kwargs.get("site_article_category"))
    else:
        article_list=models.Article.objects.all()
    cate_list=models.SiteCategory.objects.all()
    return render(request,"index.html",{"article_list":article_list,"cate_list":cate_list})
View Code

 前端代码的实现:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>博客首页</title>
    <link rel="stylesheet" href="/static/bootstrap-3.3.7-dist/css/bootstrap.min.css">

    <script src="/static/jquery-3.2.1.js"></script>
    <script src="/static/bootstrap-3.3.7-dist/js/bootstrap.js"></script>
    <style>

        .hides {
            display: none;
        }

    </style>
<body>
<nav class="navbar navbar-inverse">
    <div class="container">
        <!-- Brand and toggle get grouped for better mobile display -->
        <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
                    data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="#">博客园</a>
        </div>

        <!-- Collect the nav links, forms, and other content for toggling -->
        <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
            <ul class="nav navbar-nav">
                <li class="active"><a href="#">园子 <span class="sr-only">(current)</span></a></li>
                <li><a href="#">博文</a></li>
                <li><a href="#">新闻</a></li>
                <li><a href="#">收藏</a></li>
                <li><a href="#">小组</a></li>
                <li><a href="#">招聘</a></li>

            </ul>

            <ul class="nav navbar-nav navbar-right">
                {% if request.user.is_authenticated %}
                    <li><a href=""><span class="glyphicon glyphicon-user"></span>{{ request.user.username }}</a></li>
                    <li><a href="/log_out/">注销</a></li>
                {% else %}
                    <li><a href="/login/">登录</a></li>
                    <li><a href="/reg/">注册</a></li>
                {% endif %}

                <li class="dropdown">
                    <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
                       aria-expanded="false">Dropdown <span class="caret"></span></a>
                    <ul class="dropdown-menu">
                        <li><a href="#">Action</a></li>
                        <li><a href="#">Another action</a></li>
                        <li><a href="#">Something else here</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">Separated link</a></li>
                        <li role="separator" class="divider"></li>
                        <li><a href="#">One more separated link</a></li>
                    </ul>
                </li>

            </ul>
        </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
</nav>

<div class="container">

    {#左侧菜单部分#}
    <div class="col-md-2">
        <div class="panel panel-default">
            <div class="panel-heading">网站分类</div>
            {% for cate in cate_list %}
                <div class="panel panel-default ">
                    <div class="panel-heading  cate_title ">
                        <div class="panel-body">{{ cate.name }}
                        </div>
                    </div>
                    <div class="panel-body hides">
                        {% for sitearticlecategory in cate.sitearticlecategory_set.all %}
                             <p><a href="/cate/{{ sitearticlecategory.name }}">{{ sitearticlecategory.name }}</a></p>
                        {% endfor %}
                    </div>
                </div>
            {% endfor %}
            
        </div>
    </div>

    {#文章部分#}
    <div class="col-md-7">
        {% for article in article_list %}
            <div class="article_itme">
                <div class="title"><a href="/blog/{{ article.user.username }}">{{ article.title }}</a></div>
                <div class="row">
                    <div class="avatar col-md-2">
                        <a href="/blog/{{ article.user.username }}"><img src="/media/{{ article.user.avatar }}" alt="" width="70" height="70"></a>
                    </div>
                    <div class="desc col-md-10"><p>{{ article.desc }}</p></div>
                </div>
                <div class="row">
                    &nbsp;&nbsp;&nbsp;<a href="/blog/{{ article.user.username }}" class="name">{{ article.user.username }}</a>&nbsp;
                    <span class="time ">发布于:{{ article.create_time|date:"Y-m-d" }}</span>&nbsp;&nbsp;&nbsp;
                    <a href="" class="glyphicon glyphicon-comment"> 评论:{{ article.comment_count }}</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                    <a href="" class="glyphicon glyphicon-thumbs-up"> 点赞:{{ article.comment_count }}</a>
                </div>
                <hr>

            </div>
        {% endfor %}

    </div>


    {#右侧部分#}
    <div class="col-md-3">
        <div class="panel panel-default">
            <div class="panel-heading">Panel heading without title</div>
            <div class="panel-body">
                Panel content
            </div>
        </div>
        <div class="panel panel-default">
            <div class="panel-heading">Panel heading without title</div>
            <div class="panel-body">
                Panel content
            </div>
        </div>
        <div class="panel panel-default">
            <div class="panel-heading">Panel heading without title</div>
            <div class="panel-body">
                Panel content
            </div>
        </div>
    </div>
    </div>

</div>
<script>
    $(".cate_title").mouseover(function () {
        $(this).next().slideDown(300)
    }).parent().mouseleave(function () {
         $(this).children(".panel-body").slideUp(300)
     })
</script>
</body>



</html>
View Code

逻辑流程:

问题一:
       在导入图片的时候需要在url中配置,在查找图片的后面需要加上.url
       模块的导入
             from django.conf import settings
             from django.views.static import serve
       url(r'^media/(?P<path>.*)$', serve, {'document_root': settings.MEDIA_ROOT}),#配置图片

问题二:
      给博客站点的分类下的字分类进行隐藏
        .hides {
            display: none;
        }
问题三:给分类下的给一个事件,鼠标移动就展开mouseover(鼠标移动事件)
       
           $(".cate_title").mouseover(function () {
        $(this).next().slideDown(300)
        
        
          <script>
             $(".cate_title").mouseover(function () {
                $(this).next().slideDown(300)
             }).parent().mouseleave(function () {
                $(this).children(".panel-body").slideUp(300)
             })
</script>



                url(r'^index', views.index)
                
            2:创建视图函数,从数据库取出数据,返回到前端。
            
                -------找到文章表,取出文章对象,返回。
                
                -------找到网站分类表,取出分类对象,返回。
                
            3:返回到主页html进行渲染:
            
                1:-----拿到文章对象用for标签循环,{{.文章内容}}进行渲染。
                
                2:-----拿到分类对象用for标签循环,{{.网站分类,父}}进行渲染。
                
                3:-----拿到网站分类对象,{{.(反向查询,表名_set).网站分类,子}}进行渲染。
                
            4:JQ做动态折叠效果,鼠标悬浮展开,鼠标移开折叠。
            
                1:-----选择器找到网站分类,给他绑定一个(悬浮)事件,当触发这个事件(鼠标悬浮),执行function,$(this)找到选择器对象,执行展开事件。
                
                2:-----选择器找到网站分类的父级,给他绑定一个(悬浮)执行function,$(this)找到选择器对象,执行折叠事件事件。        
                         $(".cate_title").mouseover(function () {
                            $(this).next().slideDown(300)
                        }).parent().mouseleave(function () {
                             $(this).children(".panel-footer").slideUp(300)
                         });
                         
            5:点击网站分类,子分类,查找到这个分类下的所有文章。
            
                -------分类渲染在<a>标签中,给他一个url,鼠标点击,------走urls进行路由匹配--------找url的视图函数--------进行查找
                
                        a标签:---------/site/{{网站分类,name}}
                        
                        url:------------以site开头,有名分组(接收点击的分类),按关键字传参.*匹配所有,返回一个键值对,返回到视图函数。 
                                        url(r'^site/(?P<site_article_category>.*)/$', views.index),
                                        
                        视图函数--------**kwargs接收键值对,判断kwargs,判断正确,进行网站查询,否则查询所有文章。
                        
                        查询------------找到文章表,过滤(反向查询按字段)出文章的网站分类,子分类 =kwargs传过来的分类,返回到前端进行渲染。
                                    article_list = models.Article.objects.filter(site_article_category__name=kwargs.get("site_article_category"))
View Code

 

posted on 2017-11-18 21:20  ❦阿猿  阅读(249)  评论(0编辑  收藏  举报