• 基于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>
      自定义字符串格式化

       

     
  • 相关阅读:
    mysql高可用探究(五)Heartbeat+DRBD+mysql高可用方案
    利用DELL的OMSA监控服务器的温度
    MYSQL高可用方案探究(七)参考文献
    Asp.net中网站级异常捕获
    短信猫编程注意事项
    请保持Asp.net MVC中View的可读性
    SQLite.net使用注意事项
    GSM模块SMS相关AT命令使用说明(转)
    不要再让我们听到抽胆黑熊的哭泣
    QT的正则表达式陷阱
  • 原文地址:https://www.cnblogs.com/wtil/p/9441652.html
Copyright © 2020-2023  润新知