基于Django编写blog网站

  1. 数据库的创建
    from django.db import models
    from blog import settings
    # Create your models here.
    
    
    
    from django.contrib.auth.models import AbstractUser
    
    class UserInfo(AbstractUser):
        '''
        用户信息表
        继承AbstractUser 可以直接使用相当于扩充django默认的user可以使用django内置的功能
        需要导入   from django.contrib.auth.models import AbstractUser
    
        并且需要在setting中添加设置  AUTH_USER_MODEL='app01.UserInfo'  告诉django你自定义的用户表app名字.表名
    
        '''
        nid=models.AutoField(primary_key=True) #主键
        phone=models.CharField(verbose_name='手机号',max_length=11,null=True,unique=True) #手机号unique唯一
        avatar=models.FileField(verbose_name='头像',upload_to=settings.UPLOAD_TO,default=settings.DEFAULT_AVATAR)#头像
        create_time=models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    
        blog=models.OneToOneField(to='Blog',to_field='nid',null=True,on_delete=models.SET_NULL)#关联博客表,一对一
    
    
    
    class Blog(models.Model):
        '''
        博客信息表
        '''
        nid=models.AutoField(primary_key=True)
        title=models.CharField(verbose_name='个人博客标题',max_length=64)
        site_name=models.CharField(verbose_name='站点名称',max_length=64)
        theme=models.CharField(verbose_name='博客主题',max_length=32,default='default.css')
        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',on_delete=models.CASCADE)
    
    
    
    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',on_delete=models.CASCADE)
    
    
    class Article(models.Model):
        '''
        文章表
        '''
    
        nid=models.AutoField(primary_key=True)
        title=models.CharField(max_length=50,verbose_name='文章标题')
        desc=models.CharField(max_length=255,verbose_name='文章描述')
        create_time=models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
    
    
        content = models.TextField(verbose_name='文章内容', )
        comment_count=models.IntegerField(verbose_name='评论数',default=0)
        up_count=models.IntegerField(verbose_name='点赞数',default=0)
        down_count=models.IntegerField(verbose_name='被踩数',default=0)
    
    
        user=models.ForeignKey(verbose_name='作者',to='UserInfo',to_field='nid',on_delete=models.CASCADE)
        category =models.ForeignKey(to='Category',to_field='nid',null=True,verbose_name='文章分类',on_delete=models.SET_NULL)
    
        tags=models.ManyToManyField(
            verbose_name='文章关联标签',
            to='Tag',
            through='Article2Tag',
            through_fields=('article','tag'),
    
        )
        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',on_delete=models.CASCADE)
        tag=models.ForeignKey(verbose_name='标签',to='Tag',to_field='nid',on_delete=models.CASCADE)
    
        class Meta:
            unique_together=[
                ('article','tag'),
            ]
    
    class ArticleUpDown(models.Model):
        '''
        点赞表
        '''
        nid=models.AutoField(primary_key=True)
        user=models.ForeignKey('UserInfo',null=True,on_delete=models.CASCADE)
        article=models.ForeignKey('Article',null=True,on_delete=models.CASCADE)
        is_up=models.BooleanField(default=True)
        class Meta:
            unique_together=[
                ('article','user'),
            ]
    
    
    class Comment(models.Model):
        '''
    
        评论表
        '''
        nid=models.AutoField(primary_key=True)
        article=models.ForeignKey(verbose_name='评论文章',to='Article',to_field='nid',on_delete=models.CASCADE)
        user=models.ForeignKey(verbose_name='评论者',to='UserInfo',to_field='nid',on_delete=models.CASCADE)
        content=models.CharField(verbose_name='评论内容',max_length=255)
        create_time=models.DateTimeField(verbose_name='创建时间',auto_now_add=True)
        parent_comment=models.ForeignKey('self',null=True,on_delete=models.CASCADE)
    创建数据库

     

  2. 如果配置了了ImageField 或者FileField,就需要配置存放文件的文件夹,类似static
    #setting的配置
    
    MEDIA_URL='/media/'
    #配置静态文件存放 用户上传的文件
    MEDIA_ROOT=os.path.join(BASE_DIR,'media')
    
    
    # url的配置
    from django.urls import path,re_path
    from django.views.static import serve
    from Game import settings
    
    
    urlpatterns = [
    
    # 配置meadia
        re_path(r'^media/(?P<path>.*)$',serve,{'document_root':settings.MEDIA_ROOT}),
    ]
    上传文件的配置

     

  3. setting的配置
    """
    Django settings for blog project.
    
    Generated by 'django-admin startproject' using Django 2.0.7.
    
    For more information on this file, see
    https://docs.djangoproject.com/en/2.0/topics/settings/
    
    For the full list of settings and their values, see
    https://docs.djangoproject.com/en/2.0/ref/settings/
    """
    
    import os
    
    # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    DEFAULT_AVATAR='head_img/default_img/default_avatar.jpg'
    UPLOAD_TO='head_img/user_head/'
    
    # Quick-start development settings - unsuitable for production
    # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/
    
    # SECURITY WARNING: keep the secret key used in production secret!
    SECRET_KEY = 'j4q-6*)a&=j0!q#lo8ld2u!%j2(w2t%bse-6vkpz0-si=)8%14'
    
    # SECURITY WARNING: don't run with debug turned on in production!
    DEBUG = True
    
    ALLOWED_HOSTS = ['*']
    
    
    # Application definition
    
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01.apps.App01Config',
    
    ]
    
    MIDDLEWARE = [
        'django.middleware.security.SecurityMiddleware',
        'django.contrib.sessions.middleware.SessionMiddleware',
        'django.middleware.common.CommonMiddleware',
        'django.middleware.csrf.CsrfViewMiddleware',
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'django.contrib.messages.middleware.MessageMiddleware',
        'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ]
    
    ROOT_URLCONF = 'blog.urls'
    
    TEMPLATES = [
        {
            'BACKEND': 'django.template.backends.django.DjangoTemplates',
            'DIRS': [os.path.join(BASE_DIR, 'templates')]
            ,
            'APP_DIRS': True,
            'OPTIONS': {
                'context_processors': [
                    'django.template.context_processors.debug',
                    'django.template.context_processors.request',
                    'django.contrib.auth.context_processors.auth',
                    'django.contrib.messages.context_processors.messages',
                ],
            },
        },
    ]
    
    WSGI_APPLICATION = 'blog.wsgi.application'
    
    
    # Database
    # https://docs.djangoproject.com/en/2.0/ref/settings/#databases
    
    DATABASES = {
    
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': 'blog',
            'USER':'root',
            'PASSWORD':'####',
            'HOST':'##',
            'PORT':62910,
         }
    }
    
    
    # Password validation
    # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
    
    AUTH_PASSWORD_VALIDATORS = [
        {
            'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
        },
        {
            'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
        },
    ]
    
    
    # Internationalization
    # https://docs.djangoproject.com/en/2.0/topics/i18n/
    
    LANGUAGE_CODE = 'en-us'
    #当前时区
    TIME_ZONE = 'Asia/Shanghai'
    
    USE_I18N = True
    
    USE_L10N = True
    
    USE_TZ = False
    
    
    # Static files (CSS, JavaScript, Images)
    # https://docs.djangoproject.com/en/2.0/howto/static-files/
    
    STATIC_URL = '/static/'
    #配置静态文件存放 JS,CSS,IMG等
    STATICFILES_DIRS=(
        os.path.join(BASE_DIR,'static'),
    )
    MEDIA_URL='/media/'
    #配置静态文件存放 用户上传的文件
    MEDIA_ROOT=os.path.join(BASE_DIR,'media')
    
    AUTH_USER_MODEL='app01.UserInfo'
    
    
    SESSION_COOKIE_NAME = "sessionid"  # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认)
    SESSION_COOKIE_PATH ="/"  # Session的cookie保存的路径(默认)
    SESSION_COOKIE_DOMAIN = None  # Session的cookie保存的域名(默认)
    SESSION_COOKIE_SECURE = False  # 是否Https传输cookie(默认)
    SESSION_COOKIE_HTTPONLY = True  # 是否Session的cookie只支持http传输(默认)
    SESSION_COOKIE_AGE = 86400  # Session的cookie失效日期(2周)(默认)
    SESSION_EXPIRE_AT_BROWSER_CLOSE = False  # 是否关闭浏览器使得Session过期(默认)
    SESSION_SAVE_EVERY_REQUEST = True  # 是否每次请求都保存Session,默认修改之后才保存(默认)
    
    #配置登录页面
    LOGIN_URL='/Account/login/'
    
    
    #配置邮箱服务器
    
    # 邮箱类型
    EMAIL_HOST='smtp.exmail.qq.com' # 如果是163 该成smtp.163.com
    # 邮箱端口号
    EMAIL_PORT=465
    # 用户名
    EMAIL_HOST_USER=''
    # 授权码
    EMAIL_HOST_PASSWORD=''
    
    # 设置默认用户
    # DEFAULT_FROM_EMAIL=EMAIL_HOST_USER
    
    # 是否启用SSL
    EMAIL_USE_SSL=True
    
    '''
    Django邮箱使用方法
        from django.core.mail import send_mail
        from blog import setting
        send_mail('邮件标题','邮件内容','发送邮件的用户','[接收邮件的用户1,接收邮件的用户2....]')
    '''
    setting配置

     

  4. 基于PIL动态生成数字+字母验证码
    from PIL import Image, ImageDraw, ImageFont
    from io import BytesIO
    import random
    
    
    def get_random_color():
        # 使用random模块 随机生成RGB颜色
        return (random.randint(0, 255),random.randint(0, 255),random.randint(0, 255))
    
    
    def new_img_code(request):
        # 使用PIL 模块中的 Image生成一个  颜色模式:RGB 大小:270*40 背景色:随机生成的图片
        img = Image.new('RGB', (270, 40), color=get_random_color())
    
        # 使用PIL模块中的 ImageDraw 往图片中写入文字或者其他
        draw = ImageDraw.Draw(img)
    
        # 如果写入文字,可以在这里设置文字的字体和大小
        fonts_ShangWei = ImageFont.truetype('static/fonts/HYShangWeiShouShuW.ttf', size=30)
        # 创建一个变量用来存储验证码
        verification_code = ''
        # 我们打算生成五个随机字符
        for i in range(5):
            # 使用random模块随机生成0-9 的数字
            number = str(random.randint(0, 9))
    
            # 利用chr内置函数生成 A-Z 大写字母 chr函数可以将ASCII值转换成对应字符
            super_char = chr(random.randint(65, 90))
    
            # 利用chr内置函数生成 a-z 小写字母 chr函数可以将ASCII值转换成对应字符
            low_char = chr(random.randint(97, 122))
    
            # 使用random模块的choice功能在 生成的三个字符中随机选取一个
            choice_char = random.choice([number, super_char, low_char])
            verification_code += choice_char
    
            # 将选取到的字符写入图片,参数((x,y),字体颜色,字体样式)
            draw.text((i * 50 + 20, 5), choice_char, get_random_color(), font=fonts_ShangWei)
    
        # 将验证码保存到session中
        request.session['verification_code'] = verification_code
        '''
        request.session['verification_code']=verification_code 执行的过程
        1.生成随机字符串
        2.在客户浏览器生成一个键值对,保存生成的随机字符串,生成的key可以在setting中自己设置
        3.将session保存到数据库中(表名:django-session)。
            存储方式
            session-key    session-data
            随机字符串       {'verification_code':验证码}
    
       '''
        # 生成噪点,噪线
        # 设定图片的宽高,用来定位噪线的位置
    
        # draw.line():直线的绘制,第一个参数指定的是直线的端点坐标,形式为(x0, y0, x1, y1),第二个参数指定直线的颜色;
        #
        # draw.rectangle():矩形绘制,第一个参数指定矩形的对角线顶点(左上和右下),形式为(x0, y0, x1, y1),第二个指定填充颜色,第三个参数指定边界颜色;
        #
        # draw.arc():(椭)圆弧的绘制,第一个参数指定弧所在椭圆的外切矩形,第二、三两个参数分别是弧的起始和终止角度, 第四个参数是填充颜色,第五个参数是线条颜色;
        #
        # draw.chord():弦的绘制,和弧类似,只是将弧的起始和终止点通过直线连接起来;
        #
        # draw.pieslice():圆饼图的绘制,和弧与弦类似,只是分别将起始和终止点与所在(椭)圆中心相连;
        #
        # draw.ellipse():椭圆的绘制,第一个参数指定椭圆的外切矩形, 第二、三两个参数分别指定填充颜色和线条颜色,当外切矩形是正方形时,椭圆即为圆;
        #
        # draw.polygon():绘制多边形,第一个参数为多边形的端点,形式为(x0, y0, x1, y1, x2, y2,……),第二、三两个参数分别指定填充颜色和线条颜色;
        #
        # draw.text():文字的绘制,第一个参数指定绘制的起始点(文本的左上角所在位置),第二个参数指定文本内容,第三个参数指定文本的颜色,第四个参数指定字体(通过ImageFont类来定义)。
        # draw.point():绘制一个点,第一个参数制定点的坐标,形式为[x,y],第二个参数制定点的颜色
    
        width = 270
        height = 40
        for i in range(5):
            x1 = random.randint(0, width)
            x2 = random.randint(0, width)
            y1 = random.randint(0, height)
            y2 = random.randint(0, height)
            draw.line((x1, y1, x2, y2), fill=get_random_color())
        for i in range(50):
            draw.point([random.randint(0, width),random.randint(0, height)],
                       fill=get_random_color())
            x = random.randint(0, width)
            y = random.randint(0, height)
            draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
        # 利用IO模块创建一个文件句柄,将文件直接存放进内存
        f = BytesIO()
        # 将图片存放进内存,后缀名为png
        img.save(f, 'png')
        # 读出文件数据
        data = f.getvalue()
        return data
    验证码生成

     

  5. 注册功能的实现(使用事件功能实现数据库出错回滚)
    #注册
    class register(View):
        def post(self,request):
            head_img=request.FILES
    
            form = form_verification.register(request.POST)
            res={'user':None,'msg':None,'code':0}
            if form.is_valid():
                # print(form.cleaned_data)  # 所有干净的字段以及对应的值
                user_info=form.cleaned_data
                file_obj = head_img.get('avatar')  # avatar是form表单中的name
    
                # 将用户信息存入数据库
                if file_obj:
                    # 如果用户有上传头像,则将头像数据存进extra_fields,如果没有在创建数据的时候会存入默认数据
                    t = time.time()
                    file_obj.name='%s_%s_%s'%(t,user_info.get('user_name'),file_obj.name)
    
                    extra_fields={}
                    extra_fields['avatar']=file_obj
                #使用django的事件函数 如果其中一条sql出错 两条语句操作都无效(from django.db import transaction)
                with transaction.atomic():
                    models.Blog.objects.create(title=user_info.get('blog_name'),site_name=user_info.get('user_name'))
                    models.UserInfo.objects.create_user(username=user_info.get('user_name'),
                                                               password=user_info.get('pwd'), email=user_info.get('email'),
                                                               phone=user_info.get('phone'),**extra_fields)
                    res['user']=form.cleaned_data.get('user_name')
                    res['code']=1
            else:
                res['msg']=form.errors
                res['code']=-1
            return JsonResponse(res)
    注册

     

  6. 在CBV中无法正常使用Django提供的用户认证装饰器需要自行处理(详情:https://www.cnblogs.com/wtil/p/9350291.html)
    from django.contrib.auth.decorators import login_required
    from django.utils.decorators import method_decorator
    from django.http import JsonResponse
    
    # 在用户get或者post请求时如果用户没有登录,则跳转到登录页面
    class LoginRequiredBlog(object):
        @method_decorator(login_required(login_url='/app01/login/'))
        def dispatch(self,request,*args,**kwargs):
            return super(LoginRequiredBlog,self).dispatch(request,*args,**kwargs)
    
    
    
    # 判断ajax是否登录
    def chack():
        def _wrapper(func):
            def __wrapper(self,request, *args, **kwargs):
                    if request.user.is_authenticated:
                        return func(self,request,*args, **kwargs)
                    else:
                        return JsonResponse({'state':'OK'})
            return __wrapper
        return _wrapper
    判断是否登录

     

  7. 使用Django自定义标签的功能实现代码的重用(@register.inclusion_tag(这里面填写的是待渲染模板)) 待渲染模板不是视图HTML
    from django import template
    
    #变量名称不能变 Django固定名称
    register=template.Library()
    from app01 import models
    from django.db.models import Count
    
    def get_tag(user,blog):
        # 每个分类对应的文章数
        CategoryList = models.Category.objects.filter(blog=blog).values('pk').annotate(
            article_count=Count('article__title')).values('nid','title', 'article_count',)
        # 每个标签对应的文章数
        TagsList = models.Tag.objects.filter(blog=blog).values('pk').annotate(
            article_count=Count('article__title')).values('title', 'article_count','nid')
        # 每个年月对应的文章数
        DateList = models.Article.objects.filter(user=user).extra(
            select={'temp_date': "date_format(create_time,'%%Y-%%m')"}).values('temp_date').annotate(
            date_count=Count('title')).values('temp_date', 'date_count')
    
        return {'TagsList': TagsList, 'DateList': DateList, 'CategoryList': CategoryList, 'blog': blog}
    
    
    #使用此方法可以实现的功能是
    # get_tag_data(user,blog)获取此函数的返回值,然后将返回值返回到@register.inclusion_tag('inclution/LeftDive.html')指定的
    #模板然后进行渲染
    @register.inclusion_tag('inclution/LeftDive.html')
    def get_tag_data(user,blog):
        return get_tag(user,blog)
    
    @register.inclusion_tag('inclution/TagAndCategory.html')
    def GetTagCategory(user,blog):
        return get_tag(user,blog)
    自定义标签

    使用方法

    <div class="panel panel-default">
        <div class="panel-heading">我的标签</div>
        <div class="panel-body">
            <ul class="list-group">
                {% for item in TagsList %}
                    <li class="list-group-item">
                        <a href="/app01/{{ blog.site_name }}/tags/{{ item.title }}"> {{ item.title }}({{ item.article_count }})</a>
                    </li>
                {% endfor %}
            </ul>
        </div>
    </div>
    <div class="panel panel-default">
        <div class="panel-heading">我的分类</div>
        <div class="panel-body">
            <ul class="list-group">
                {% for item in CategoryList %}
                    <li class="list-group-item">
                        <a href="/app01/{{ blog.site_name }}/category/{{ item.title }}"> {{ item.title }}({{ item.article_count }})</a>
                    </li>
                {% endfor %}
            </ul>
        </div>
    </div>
    <div class="panel panel-default">
        <div class="panel-heading">时间档案</div>
        <div class="panel-body">
            <ul class="list-group">
                {% for item in DateList %}
                    <li class="list-group-item">
                        <a href="/app01/{{ blog.site_name }}/date/{{ item.temp_date }}"> {{ item.temp_date }}({{ item.date_count }})</a>
                    </li>
                {% endfor %}
            </ul>
        </div>
    </div>
    待渲染的模板
            <div class="col-md-3">
    {#            导入自定义的标签#}
                {% load  MyTag%}
    {#            然后使用自定义的函数 传入需要的参数 user blog#}
                {% get_tag_data user blog %}
                
            </div>
    使用

     自定义标签 --> 带渲染模板 --> 真正使用模板

  8. 实现树状评论的ajax
    //显示树状结构评论
    
    $.ajax({
        url:'/app01/treecomment/',
        type:'post',
        data:{'article_id':$("#article_id").attr("class")},
        dataType:'json',
        headers:{'X-CSRFTOKEN':token},
        success:function (data) {
            data=data.data
            $(".temp_msg").fadeOut(3)
            $.each(data,function (index,item) {
                // 格式化时间
                var date = new Date(item[4]);
                Y = date.getFullYear() + '-';
                M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
                D = date.getDate() + ' ';
                h = date.getHours() + ':';
                m = date.getMinutes() + ':';
                s = date.getSeconds();
                item[4]=Y+M+D+h+m+s
                //格式化时间结束
                var tree={}
                //这里使用的功能类似于python的字符串格式化
                //树状评论实现的思路:
                //    1.每一条评论都有自己的ul
                //    2.子评论的数据中带有父评论的ID,然后找到父评论将子评论插入到父评论的ul中
                //    3.在css样式用加入样式使评论往右偏移10px,由于子评论在父评论中,会使子评论比父评论多往右偏移10px
                var comment = `
                    <ul class="list-group" id="${item[0]}">
                        <li class="list-group-item">
                            <a href="#" class="layer">#${index + 1}</a><!--显示楼层-->
                            <span class="comment_date">${item[4]}</span><!--显示时间-->
                            <a id="a_comment_author_${item[0]}" href="/app01/${item[2]}" target="_blank">${item[2]}</a><!--评论者-->
                            <a href="javascript:Reply($('#${item[0]}'));" id="${item[0]}" user="${item[2]}" class="Reply">回复</a>
                        </li>
                        <li class="list-group-item">
                            <div id="comment_body_${item[0]}" class="blog_comment_body">${item[3]}</div><!--显示评论内容-->
                       </li>
                        <li class="list-group-item" id="ChildComment"></li>
                    </ul>
    `
                if(!item[5]) {
                    $(".feedbackNoItems").append(comment)
                }else {
                    $("#"+item[5]+">#ChildComment").append(comment)
                }
            })
        }
    })
    树状评论

     

  9. 使用BeautifulSoup实现简单的XSS攻击
    from bs4 import BeautifulSoup
    
    def defense(content):
    
        # html.parser是Python内置的一个解析器,当传入后会解析成一个结构的文档
        soup = BeautifulSoup(content, 'html.parser')
    
        tags = {
            'p': ['class'],  # 设置p标签仅允许class属性
            'strong': ['id', ]  # 设置strong标签仅允许id属性
        }
    
        for tag in soup.find_all():  # find_all()方法可以找到所有标签(子子孙孙查找)
            # 1. 只允许有p标签和strong标签
            if tag.name in tags:
                pass
            else:
                tag.hidden = True  #将标签中的内容隐藏
                tag.clear()  #将标签中的内容清空
                tag.decompose() #删除标签
                continue  # 如果标签名不符合就不执行下面的for循环了
            # 2. p标签只允许class属性,strong标签只允许id属性
            # 用户提交的所有属性
            input_attrs = tag.attrs  # {'class': 'c1', 'id': 'i1'}
            # 允许对应标签的指定属性有哪些
            valid_attrs = tags[tag.name]  # ['class']
    
            # 字典在for循环迭代时是不可以删除某个项的,这里利用list转换
            for k in list(input_attrs.keys()):
                if k in valid_attrs:
                    pass
                else:
                    del tag.attrs[k]
    
    
        # 获取处理好的标签跟文字
        content = soup.decode()
        # 去除标签,只留文字
        desc=soup.text
    
        return {'content':content,'desc':desc}
    
    
    if __name__ == '__main__':
        content = """
        <p class='c1' id='i1'>
           asdfaa<span style="font-family:NSimSun;" class='c1'>sdf<a>a</a>sdf</span>sdf
        </p>
        <p>
           <strong class='c2' id='i2' a=888>asdf</strong>
           <script>alert(123)</script>
        </p>
        <h2>
           asdf
        </h2>
        """
        text=defense(content)
        print(text)
    XSS

     

  10. 用户上传头像并预览
    $('#avatar').change(function () {
    
        if($(this).val()!=''){
            //读取用户上传的,文件对象
            var file =$(this)[0].files[0];
            // new一个文件读取器
            var readfile=new FileReader();
            //使用读取器读取文件路径
            readfile.readAsDataURL(file);
            //由于读取器是单独的进程,一步读取,所以需要使用readfile.onload来等来读取器读取完成,之后在进行img地址的更新
            readfile.onload=function () {
                //img地址更新
                $('.avatar_img').attr('src',readfile.result)
    
            }
            
        }
    
    })
    
    //这个方法只支持IE10 或者其他高级浏览器,如果要兼容低级浏览器需要用其他方法
    //其他方法:
        //1.先用Ajax将图片上传到服务器
        //2.服务器返回图片地址
        //3.用JS代码将地址渲染到img标签
        //注意:如果用户最后并没有注册,需要将服务器上的图片删除,以免浪费空间
    头像预览

     

  11. 判断浏览器是否支持某一函数(在遇到低版本浏览器不兼容的函数时使用)
    function bindAvatar(){
            if(window.URL.createObjectURL){
                    //如果if判断通过则表示浏览器中有window.URL.createObjectURL函数
                    //如果判断其他函数,只需要修改window.URL.createObjectURL
    }
        
    }
    View Code

     

  12. Form表单
    from django.forms import widgets
    from django import forms
    from app01 import models
    import sys
    #使用钩子必须导入这个ValidationError错误,如果字段校验失败必须抛出这个错误
    from django.core.exceptions import ValidationError
    wid_text=widgets.TextInput(attrs={"class":"form-control"})
    wid_pwd=widgets.PasswordInput(attrs={"class":"form-control"})
    wid_email=widgets.EmailInput(attrs={"class":"form-control"})
    error_messages={'required':'该字段不能为空.....','max_length':'输入内容过长','min_length':'输入内容过短'}
    
    
    class register(forms.Form):
    
        user_name=forms.CharField(min_length=3,
                                  label='用户名',
                                  widget=wid_text,
                                  error_messages=error_messages,
                             )
        blog_name=forms.CharField(
                                  label='博客名',
                                  widget=wid_text,
                                  error_messages=error_messages,
                             )
        pwd=forms.CharField(min_length=6,
                            label='密码',
                            widget=wid_pwd,
                            error_messages=error_messages,)
    
        r_pwd=forms.CharField(min_length=6,
                              label='确认密码',
                              widget=wid_pwd,
                              error_messages=error_messages,)
    
        email=forms.EmailField(label='邮箱',
                               widget=wid_email,
                               error_messages=error_messages,)
    
        phone=forms.CharField(min_length=11,
                              label='手机号',
                              widget=wid_text,
                              error_messages=error_messages,)
    
    
    
    
        #局部钩子,在源码中规定函数的命名方法(clean_字段名)
        def clean_user_name(self):
            # 当定义字段时的规则判断完成时会把通过的字段放在cleaned_data中,这里我们将name取出来进行判断是否纯数字
            val = self.cleaned_data.get("user_name")
    
            if models.UserInfo.objects.filter(username=val).first():
                raise ValidationError('该用户已被注册....')
            if not val.isdigit():
    
                return val
            else:
                #校验失败抛出错误 在错误字典(errors)中添加  'name':'错误信息'这样一个键值对
                raise ValidationError("用户名不能是纯数字!")
    
        def clean_phone(self):
            val=self.cleaned_data.get('phone')
            if len(val)==11:
                return val
            else:
                raise ValidationError('手机号长度必须为11位')
    
        def clean_pwd(self):
            val=self.cleaned_data.get('pwd')
            if not val.isdigit():
                return val
            else:
                raise ValidationError('密码不能纯数字')
    
        # 全局钩子
        def clean(self):
            pwd = self.cleaned_data.get("pwd")
            r_pwd = self.cleaned_data.get("r_pwd")
            if pwd and r_pwd:
                if pwd == r_pwd:
                    return self.cleaned_data
                else:
                    # 如果验证没有通过,会在错误字典(errors)中添加 '__all__':'错误信息' 这样的一个键值对
                    raise ValidationError('两次密码不一致!')
            else:
                return self.cleaned_data
    form验证

     

  13. 在Form中添加自己想要的参数
    #在form中是不包含request参数的,但是我们可以通过构造函数自己添加
    #编写构造函数
    def __init__(self,request,*args,**kwargs):
        super(register,self).__init__(*args,**kwargs)
        self.request=request
    
    #这样写完之后就可以在其他方法中使用取到request中的数据了, #super(register,self).__init__(*args,**kwargs)中的register是你创建form类的类名
    
    #调用方法 register(request,request.POST)
    在form中添加自己想要的参数

     

  14. 组合筛选
    思路:
          1. 我们使用url来传递
    
    
             汽车之家组合筛选 https://car.autohome.com.cn/price/list-0_5-0-0-0-0-0-0-0-0-0-0-0-0-0-0-1.html
    
    
           (0_5(汽车价格区间)-0(汽车类型)-0-0-0-0-0(汽车产地)-0-0-0-0-0-0-0-0-1)不一一列举,类似这样的方法来传递参数
    
    
            python中的url接收 re_path(r'^index-(?P<type_id>\d+)-(?P<category_id>\d+)-(?P<tag_id>\d+)')
    
          2.实现url动态生成
    
            因为我们在点击筛选的时候不是用Ajax来实现,而是页面的刷新实现,所以我们在配置a标签的href时,可以重后台传递数值到url中
    
            比如,你上传了这样一个url  (index-1-2-2)  后台获取的时候 **kwargs 得到 type_id=1,category_id=2,tag_id=2 处理结束之后
    
            return  render(request, 'index/index.html', {'type_id':type_id,'category_id':category_id,'tag_id':tag_id})
            前台的模板可以这样写 
            type:<a href='/index-1-{{category_id}}-{{tag_id}}/'>这是类别1</a>
    
            category:<a href='/index-{{type_id}}-1-{{tag_id}}/'>这是分类1</a>
    
            tag:<a href='/index-{{type_id}}-{{category_id}}-1/'>这是标签1</a>
    组合筛选

     

  15. kindeditor的使用(富文本编辑框)
    var token=document.cookie.split('=')[1]
    KindEditor.ready(function(K) {
                    // #content这个是页面上textarea的ID 
                    window.editor = K.create('#content',{
                        // 配置编辑框的高度
                        height:'400px',
                        // 服务器接收文件的地址  也就是上传文件的地址
                        uploadJson:'/app01/upload/',
                        // 在这个里面可以添加一些其他 需要上传到服务器的字段,这里添加的是CSRF
                        extraFileUploadParams:{'csrfmiddlewaretoken':token},
                        // 上传文件的文件名
                        filePostName:'ArticleImg',
                        // 这里可以选则需要的功能
                        item:[
            'source', '|', 'undo', 'redo', '|', 'preview', 'print', 'template', 'code', 'cut', 'copy', 'paste',
            'plainpaste', 'wordpaste', '|', 'justifyleft', 'justifycenter', 'justifyright',
            'justifyfull', 'insertorderedlist', 'insertunorderedlist', 'indent', 'outdent', 'subscript',
            'superscript', 'clearhtml', 'quickformat', 'selectall', '|', 'fullscreen', '/',
            'formatblock', 'fontname', 'fontsize', '|', 'forecolor', 'hilitecolor', 'bold',
            'italic', 'underline', 'strikethrough', 'lineheight', 'removeformat', '|', 'image', 'multiimage',
              'insertfile', 'table', 'hr', 'emoticons',  'pagebreak',
            'anchor', 'link', 'unlink', '|', 'about'
    ],
                        //添加了这几句之后可以在jQuery中使用 .val 的方式来获取编辑框的内容
            afterCreate : function() {
             this.sync();
            },
            afterBlur:function(){
                this.sync();
            }
                    });
    
            });
    富文本编辑框的使用

     

    class upload(View):
        @chack()
        def post(self,request):
            # kindeditor 编辑框 上传文件之后需要返回内容 error 1 表示错误,0表示成功  url 是用来返回图片地址,实现预览.通过http://127.0.0.1:8000/app01/upload/?dir=image地址的参数dir可以判断上传的是什么文件类型,地址是kindeditor自动生成的
            res={"error":1,"url":"#"}
            file_obj = request.FILES.get('ArticleImg')  # avatar是form表单中的name
            FileName='media/ArticleImg/%s_%s_%s'%(time.time(),request.user.username,file_obj.name)
            with open(FileName, 'wb') as f:  # file_obj.name取到的是客户端上传的文件名
                for line in file_obj:
                    f.write(line)
            res["error"] = 0
            res["url"] = '/app01/%s'%(FileName)
    
            return JsonResponse(res)
    服务端配置

     

  16. 前端JavaScript自定义一个字符串格式化
    <script>
        String.prototype.Format=function(arg){
            /*
                this,当前字符串
                arg,Format 方法传入的参数
                return 返回处理后的内容
            */
            var temp =this.replace(/\{(\w+)\}/g,function (k,kk) {
                /*
                    replace 替换函数  
                        第一个参数可以是要替换的字符串,也可以是正则表达式用来匹配字符串
                        第二个参数可以是用来替换的字符串,也可以是一个函数(
                            函数:他会将正则匹配到的字符串,当作参数传入函数 str=my name is {name} , age is {age}
                                    匹配到 {name} {age} 传入 k 我们在\w+ 上加了括号 所以会将里面的内容获取到 name age 传入kk
                                    传入参数的的时候不是两个一起传入,而是循环传入,第一次传入name 得到返回值之后在传入age
                                    函数有返回值 函数返回什么 就会用什么去替换 匹配到的字符 比如name返回小和尚  age返回18
                                    最后的字符串变成  my name is 小和尚 , age is 18           
                        )  
                 */
                return arg[kk]            
            })
    
        }
    
        /* 使用方法 */
        var v1='i am {name},age is {age}'
        v1.Format({'name':'小和尚','age':18})
    
    </script>
    自定义字符串格式化

     

   

 

 

 

posted on 2018-08-08 11:16  信奉上帝的小和尚  阅读(760)  评论(0编辑  收藏  举报

导航