BBS总结

2019/02/15 09:24
阅读数 38

表设计

from django.db import models
from django.contrib.auth.models import AbstractUser


# Create your models here.
class UserInfo(AbstractUser):
    nid=models.AutoField(primary_key=True)
    # blank=True admin中改字段可以不填,null=True是数据库层面可以为空
    telephone = models.BigIntegerField(null=True,blank=True)
    # auto_now_add 创建时自动添加当前时间
    create_date = models.DateField(auto_now_add=True)
    # upload_to需要传一个路径
    # default 默认头像的路径
    avatar = models.FileField(upload_to='avatar/', default='avatar/default.png')
    # 跟Blog表一对一
    blog = models.OneToOneField(to='Blog', to_field='nid',null=True)
    class Meta:
        verbose_name='用户表'
        verbose_name_plural = verbose_name
# 个人站点表
class Blog(models.Model):
    nid = models.AutoField(primary_key=True)
    site_name = models.CharField(max_length=32)
    site_title = models.CharField(max_length=64)
    # 存css文件的路径
    theme = models.CharField(max_length=32)
    def __str__(self):
        return self.site_name


class Category(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to='Blog', to_field='nid')


class Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    blog = models.ForeignKey(to='Blog', to_field='nid')
    def __str__(self):
        return self.name


class Article(models.Model):
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64,verbose_name='文章标题')
    desc = models.CharField(max_length=255)
    # 存大文本
    content = models.TextField()
    create_time = models.DateTimeField(auto_now_add=True)
    # 跟站点表一对多
    blog = models.ForeignKey(to='Blog', to_field='nid',null=True)
    # 跟分类一对多
    category = models.ForeignKey(to='Category', to_field='nid',null=True)
    # 跟标签多对多,手动创建第三张表
    tag = models.ManyToManyField(to='Tag', through='Article2Tag', through_fields=('article', 'tag'))

    #     后来想到的
    # 评论数,点赞,点踩数
    comment_num=models.IntegerField(default=0)
    up_num=models.IntegerField(default=0)
    down_num=models.IntegerField(default=0)
    def __str__(self):
        return self.title


class Article2Tag(models.Model):
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to='Article', to_field='nid')
    tag = models.ForeignKey(to='Tag', to_field='nid')

#谁对哪篇文章,点赞还是点踩
class UpAndDown(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article', to_field='nid')
    is_up = models.BooleanField()

#评论对评论自关联,
#谁,什么时候,对哪篇文章,评论了什么内容,父评论是谁
class Comment(models.Model):
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to='UserInfo')
    article = models.ForeignKey(to='Article', to_field='nid')
    content = models.CharField(max_length=255)
    # 评论时间
    create_time=models.DateTimeField(auto_now_add=True)
    # parent =models.ForeignKey(to='Comment',to_field='nid')
    # 自关联,存父评论id
    parent = models.ForeignKey(to='self', to_field='nid',null=True)
    # parent =models.IntegerField()
View Code

1 登陆功能:

-图形验证码

-ajax提交
-认证用的是auth
-认证通过,login

1 forms组件
        1 定义
            from django import forms
            from django.forms import widgets
            from django.core.exceptions import ValidationError
            # 2 写一个类
            class RegForm(forms.Form):
                # 3 写属性
                name=forms.CharField(max_length=8,min_length=3,label='用户名',error_messages={'max_length':'超长了'},
                                     widget=widgets.TextInput(attrs={'class':'form-control'})
                                     )
                
                # 4 局部钩子函数
                def clean_name(self):
                    name=self.cleaned_data.get('name')
                    if name.startswith('sb'):
                        raise ValidationError('不能以sb开头')
                    else:
                        # 切记,如果正确,一定要返回name
                        return name
                    
                #     5 全局钩子函数
                def clean(self):
                    #一系列逻辑判断
                    #如果校验通过:返回cleaned_data
                    #如果校验不通过:raise ValidationError('两次密码不一致'),错误放到__all__
                    pass
        2 views中使用:
            def test(request):
                if request.method=='GET':
                    regform=RegForm()
                else:
                    regform=RegForm(request.POST)
                    if regform.is_valid():
                        #一般情况需要存数据库了
                        pass
                    else:
                        error_all=regform.errors.get('__all__')
                        # error_all=regform.errors['__all__']
                return render(request,'register.html',locals())
        3 模板中使用
            <form action="">

            {% for foo in regform %}
                {{ foo.label }}:{{ foo }} <span>{{ foo.errors.0 }}</span>
            {% endfor %}
            <input type="submit"> <span>{{ error_all }}</span>

            </form>
            
    2 cookie和session
        1 cookie:由服务器产生,存放在客户端浏览器上的键值对
        2 django中使用cookie:
            -设置值:
                obj=HttpResponse('ok')
                obj.set_cookie('key','value',max_age=10)
            -取值:
                request.COOKIES.get('key')
                request.COOKIES['key']
            -删除值:
                obj=HttpResponse('ok')
                obj.delete_cookie('key')
                
        3 session:保存在服务器上的键值对
            -设置值:
                request.session['key']='value'
                request.session['key1']='value1'
                
                     1 生成一个随机字符串:dasfasdf
                     2 在django_session表中存入dasfasdf   {'key':'value','key1':value1}  超时时间
                     3 把sessionid:dasfasdf写入到cookie
                
            -取值:
                request.session.get('key')
            -删除值:
                request.session.flush():全删除
                request.session.delete():只删除数据库
            -其它配置参数:
                了解
                
    3 Auth模块
        1 Django自带的用户认证模块,可以快速的实现登录,注销,修改密码...
        2 扩展auth表,需要继承AbstractUser
        3 一定不要忘记在setting中配置:AUTH_USER_MODEL = "app名.UserInfo"
        4 它提供的功能
            -from django.contrib.auth import authenticate,login,logout
            -用户认证:user=authenticate(username=lqz,password=123)
            -用户一旦认证通过,调用login(request,user),以后从request.user中就能取出当前登录人对象
            -退出:logout(request),request.user就是匿名用户
            -校验是否通过认证(是否登录):request.user.is_authenticated()
            -创建普通用户
                -models.UserInfo.objects.create_user(username=lqz)
            -创建超级用户
                -models.UserInfo.objects.create_superuser(username=lqz)
            -修改密码
                -用user对象.set_password(新密码)
                -一定要记住save
            -校验密码
                -check_password(password)
            -登录认证装饰器(没有登陆的时候跳转)
                -login_required(login_url='/login/')
                -全局配置(在setting中配置):
                    LOGIN_URL = '/login/' 
            is_staff: 用户是否拥有网站的管理权限:create_superuser:is_staff是1
            is_active: 是否允许用户登录, 设置为 False,可以在不删除用户的前提下禁止用户登录。
            
            
今日内容:
    1 中间件
        -是什么?中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出。因为改变的是全局,所以需要谨慎实用,用不好会影响到性能
        -怎么用:
        -自定义中间件:
            1 写一个类,继承MiddlewareMixin,
            2 在类中写方法:
                process_request
            3 在settings中配置
        
        -5个方法(process_request,process_response)
            ******
            process_request(self,request)
                -执行顺序,settings中中间件自上而下执行
                -请求来的时候会执行它
                -request对象,就是本次请求的request对象,对它处理后,视图函数拿到的就是处理后的request对象
                
            process_view(self, request, callback, callback_args, callback_kwargs)
                -callback是视图函数,callback_args, callback_kwargs是视图函数的参数
                -可以调用callback方法
            
            process_template_response(self,request,response)(忘掉)
                -只有视图函数返回的对象中有render方法的时候,才会执行
            process_exception(self, request, exception)
                -视图函数出错,会执行它
            *****
            process_response(self, request, response)
                -执行顺序,settings中中间件自下而上执行
                -响应走的时候,会执行它
                -request对象,就是本次请求的request对象,response是响应对象(HttpResponse的对象)
            
            
        -如果process_request方法返回HttpResponse的对象,请求直接返回,按中间件方法执行顺序往回走
        
    2 csrf
        xss攻击/csrf或xsrf跨站请求伪造
        使用:中间件不注释,form表单中写{% csrf_token %}
    
    3 bbs表设计
        -一个项目从无到有
            1 需求分析
                -登录ajax,图形验证码
                -注册forms和ajax,上传头像,头像预览
                -博客首页
                -个人站点
                -点赞,点踩
                -评论
                    -根评论
                    -子评论
                -后台展示
                -添加文章
                    -防xss攻击
            2 设计程序以及程序的架构
                -Django
                -数据库设计
                
                
            3 分任务开发程序
            
            4 测试
            5 上线运行
            
        -数据库设计
            -用户表:UserInfo---auth模块
                ...
                -telephone:手机号
                -create_date:注册时间
                -avatar:用户头像
                -blog:跟Blog表一对一
            -个人站点表:Blog
                -site_name:站点名称
                -site_title:站点标题
                -theme:站点主题样式
            -分类表:Category
                -nid
                -name:分类名称
                -blog:属于哪个站点            一对多
            -标签表:Tag
                -nid
                -name:标签的名称
                -blog:属于哪个站点            一对多
            -文章表:Article
                -nid
                -title:文章标题
                -desc:文章摘要
                -content:文章内容
                -create_time:文章发布时间
                -blog:文章属于哪个站点,      一对多
                -category:文章分类           一对多
                -tag:文章标签                多对多
                
            -Article2Tag:文章跟标签的中间表
                -nid
                -article_id:文章id
                -tag_id:标签id
            -点赞点踩表:UpAndDown
                -nid
                -user:谁                一对多            
                -article:给那篇文章     一对多
                -is_up:点赞或点踩
            -评论表:Comment
                -nid
                -user:谁                 一对多
                -article:给那篇文章      一对多
                -content:评论了什么内容
                -parent_id:父评论的id    自关联
                
        nid     用户id  文章id   内容        parent_id(父评论的id)
        1        1     1       你好          null
        2        2     1       写的真好      null
        3       3     1       你傻么         1
            
作业:
    1 csrf中间件不注释,用ajax提交post请求
    2 用中间件实现只有登录了以后,才能访问order,userinfo这些路径,如果没有登录,重定向到登录页面
    
            
            
View Code

2 注册功能

-forms组件
-ajax提交
-错误信息渲染

1 注册:
        -forms组件渲染页面
        -头像预览
        -点击img触发头像上传
        -$("#myform").serializeArray()的使用
        -jq的循环 $.each(循环的对象,匿名函数(两个参数)):
            循环对象如果是个列表,匿名函数的两个参数,一个是索引,一个是索引对应的值
            循环对象如果是个字典,匿名函数的两个参数,一个是key,一个是value
        -上传文件:
            processData: false,
            contentType: false,
        -错误信息渲染
        -定时器
        
        -reg_form.is_valid()走了它之后,cleaned_data中才有值
    -2 登陆
        -生成验证码
            -Pillow
        -生成图片
        -在图片上写字
        -指定写字的字体
        -内存管理器BytesIO
        -验证码保存在session中
        
    -验证码刷新:
    $("#id_imgcode")[0].src += '?'
    
    127.0.0.1:8000/getcode/?random=随机数
View Code

3 首页

    admin中添加数据要将用户与一个用户表进行关联
     media的配置,设置用户上传头像路径和默认头像
     开口:

      url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),

-全部文章取出来(分页)for循环渲染

-首页设计:
            -用户是否登陆
                {% if request.user.is_authenticated %}
                    <li><a href="#">{{ request.user.username }}</a></li>
                    <li class="dropdown">
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button"
                           aria-haspopup="true" aria-expanded="false">个人中心 <span class="caret"></span></a>
                        <ul class="dropdown-menu">
                            <li><a href="#">修改密码</a></li>
                            <li><a href="#">修改头像</a></li>
                            <li role="separator" class="divider"></li>
                            <li><a href="/logout/">退出</a></li>
                        </ul>
                    </li>
                {% else %}
                    <li><a href="/login/">登陆</a></li>
                    <li><a href="/register/">注册</a></li>
                {% endif %}
            -for循环显示所有文章
            -文章作者显示:
                -{{ article.blog.userinfo.username }}:通过文章,正向查询站点(blog),通过站点反向查询用户,通过用户取出姓名
            -文章评论和点赞数显示:
                -<span>评论({{ article.comment_set.count }})</span>
                -<span>点赞({{ article.upanddown_set.count}})</span>
                -通过文章反向查询(表名小写_set)所有评论,计算评论条数
                比较复杂,所以用了在文章表中加入三个字段
                    comment_num=models.IntegerField(default=0)
                    up_num=models.IntegerField(default=0)
                    down_num=models.IntegerField(default=0)
        -admin的使用(快速录入数据):
            -注册表,才能在admin后台看到
                admin.site.register(models.UserInfo)
                admin.site.register(models.Blog)
                admin.site.register(models.Tag)
                admin.site.register(models.Category)
                admin.site.register(models.Article)
                admin.site.register(models.UpAndDown)
                admin.site.register(models.Comment)
            -表名的中文显示:
                class Meta:
                    verbose_name='用户表'
                    verbose_name_plural = verbose_name
            -字段的中文显示(verbose_name):
                title = models.CharField(max_length=64,verbose_name='文章标题')
            -admin表单提交,是否必填(blank),注意区分null=True
                -telephone = models.BigIntegerField(null=True,blank=True)
            
        -mediaroot配置
            -media文件:用户上传的静态文件
            -static文件:网站所用的静态文件
            -在settings中配置:MEDIA_ROOT = os.path.join(BASE_DIR, 'media'),用户上传的文件放到里面
            -在路由中写:  url(r'^media/(?P<path>.*)', serve,{'document_root':settings.MEDIA_ROOT}),
View Code

4 个人站点

-文章展示(当前站点的所有文章)
-左侧标签显示
-三个分组查询,TruncMonth


-抽取成inclusion_tag
-左侧过滤

-个人站点:
            -路由:url(r'^(?P<username>\w+)',views.site),
            -视图函数:
                def site(request,username):
                    #通过名字取数据库查询此人是否存在,如果存在,返回此人个人站点,如果不存在,返回404
                    user=models.UserInfo.objects.filter(username=username).first()
                    if not user:
                        return render(request,'error.html')
                    # 取到此人个人站点
                    blog=user.blog
                    # 取出该站点下所有文章(从blog取article,反向查询,一对多,按表名小写_set.all())
                    article_list=blog.article_set.all()
                    return render(request,'site.html',locals())
            -页面样式显示:(动态显示)
                <link rel="stylesheet" href="/static/css/{{ blog.theme }}">
            
            
        -补充:
            如果想django不自动加斜杠,需要配置APPEND_SLASH=False
            -防盗链:
                -就是根据refer信息

    -自定义session(在中间件中处理)
    -自己的图片防盗链(头三次,可以访问)
    -访问频率控制(一分钟只能,同一个ip地址只能访问3次)
        -设置一个全局变量,记录60sip 访问次数
    
    
View Code
-个人站点左侧显示
        -截断表分析
            id   时间                           文章内容        month
            1   2019-01-25 03:04:07.844138          111           2019-01
            2   2018-12-01 03:04:54.000000          222           2018-12
            3   2018-12-01 03:05:17.000000          333           2018-12
            4   2018-11-01 03:05:38.000000          444           2018-11
        -官方提供
            from django.db.models.functions import TruncMonth
            Sales.objects
            .annotate(month=TruncMonth('timestamp'))  # Truncate to month and add to select list
            .values('month')  # Group By month
            .annotate(c=Count('id'))  # Select the count of the grouping
            .values('month', 'c')  # (might be redundant, haven't tested) select month and count

        -重点总结:
            -查询当前站点分类下的文章数
                annotate 方法不局限于用于本文提到的统计分类下的文章数,你也可以举一反三,只要是两个 model 类通过 ForeignKey 或者 ManyToMany 关联起来,那么就可以使用 annotate 方法来统计数量。
                category_ret = models.Category.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name', 'c')
            -查询当前站点标签下的文章数
                tag_ret=models.Tag.objects.filter(blog=blog).annotate(c=Count('article')).values_list('name','c')
            -查询当前站点下每个月份下的文章数
                -month_ret=models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values('month').annotate(c=Count('pk')).values_list('month','c')
            -前端显示:
                {% for category in category_ret %}
                    <p><a href="">{{ category.0 }}({{ category.1 }})</a></p>
                {% endfor %}
    -个人站点过滤
        -抽取成inclusion_tag
        -路由设计:
            <p><a href="/{{ username }}/category/{{ category.2 }}">{{ category.0 }}({{ category.1 }})</a></p>
左边栏

5 文章详情

-继承了母版
-marksafe展示html

6 点赞点踩

-事务

-点赞点踩
        -前端:
            -样式:从博客园扣过来的
            -ajax提交数据
                -谁对哪篇文章点赞或点赞
                -只写一个ajax,取出当前点击div内的span,后续直接在span上加一
        -后端:
            -先判断是否登录
            -再判断是否已经点过
            -去article表中修改点赞或点踩数
            -去点赞点踩表中插入一条数据
View Code

7 评论

-根评论
-根评论提交
-根评论render显示
-根评论ajax显示
-子评论
-子评论提交
-子评论render显示
-子评论ajax显示

-根评论
        -提交
            -跟点赞点踩类似
        -render显示
            -后端取出所有评论
            -模板中渲染: <ul class="list-group">
            -日期过滤:comment.create_time|date:'Y-m-d H:i:s'
            
        -ajax显示:
            -es6的字符串替换语法:`asdfasfd ${变量名}`,变量需要先定义
            -$(".list-group").append(ss) 拼完追加到后面
        
        -es6字符串替换
            'you is %s'%'big'
            var user_name='lqz'
             ss=`
                 <li class="list-group-item">
                 <p>
                 <span>${ user_name }</span>
                    <span>${ time }</span>
                </p>
                ${content}
                </li>
                `
View Code
1 子评论提交
        -点击回复按钮,1 获取parent_id 2在文本框中输入:@人名 回车
        -提交的时候:如果是子评论,需要切掉头部
            var index=content.indexOf('\n')+1
            content=content.slice(index)
        -提交的时候:需要把parent_id提交到后台
    2 子评论render显示
        -判断是否有父评论,如果有,显示父评论的人名
            -@{{ comment.parent.user.username }}
    3 子评论的ajax显示
        -根据parent_id是否有值,判断要拼接的html样式
        -评论提交成功,parent_id的值要置为空
        -后台:如果是子评论格式,需要返回父评论评论人的名字
子评论

8 后台文章展示

-table的展示

9 新增文章

-富文本编辑器使用
-富文本编辑器上传图片
-处理xss攻击(bs4)

5 文章添加
        -富文本编辑器
            -从官网下载,放到static文件夹下
            -window.editor = K.create('#id_content')
            -其它参数,具体看文档
            -上传图片,携带csrf
                uploadJson:'/add_img/',
                extraFileUploadParams : {
                        csrfmiddlewaretoken : '{{ csrf_token }}',
                }
        -xss攻击(bs4)
            -通过bs4模块,取出script标签,删除decompose()
            -取出html中150个字符:soup.text[0:150]
        beautifulsoup4  模块
            -解析html页面的
            -爬虫中用的多
        
    -局部禁用csrf,局部使用
        from django.views.decorators.csrf import csrf_protect,csrf_exempt
        @csrf_exempt 局部禁用,前提是csrf中间件使用着
        @csrf_protect 局部使用,前提是csrf中间件没有使用
    -CBV加装饰器
        1 可以加在方法上
            @method_decorator(auth_login)
        2 可以加在类上
            -@method_decorator(auth_login,name='dispatch'):全部使用
            -@method_decorator(auth_login,name='get'):只在get上使用
        3 csrf的装饰器,只能加在类上
            @method_decorator(csrf_exempt,name='dispatch')
View Code

 

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部