一、表结构关系图

  

 

 二、建表

from django.db import models

# Create your models here.

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    """
    用户信息表
    """
    phone = models.CharField(max_length=11, null=True, unique=True)  # 手机号
    avatar = models.FileField(upload_to="avatars/", default="avatars/default.png")  # 头像

    blog = models.OneToOneField(to="Blog", null=True)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name = "用户信息"
        verbose_name_plural = verbose_name


class Blog(models.Model):
    """
    博客信息
    """
    title = models.CharField(max_length=64)  # 个人博客标题
    theme = models.CharField(max_length=32)  # 博客主题

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "博客"
        verbose_name_plural = verbose_name


class Category(models.Model):
    """
    个人博客文章分类
    """
    title = models.CharField(max_length=32)  # 分类标题
    blog = models.ForeignKey(to="Blog")  # 外键关联博客,一个博客站点可以有多个分类

    def __str__(self):
        return "{}-{}".format(self.blog.title, self.title)

    class Meta:
        verbose_name = "文章分类"
        verbose_name_plural = verbose_name


class Tag(models.Model):
    """
    标签
    """
    title = models.CharField(max_length=32)  # 标签名
    blog = models.ForeignKey(to="Blog")  # 所属博客

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "标签"
        verbose_name_plural = verbose_name


class Article(models.Model):
    """
    文章
    """
    title = models.CharField(max_length=50)  # 文章标题
    desc = models.CharField(max_length=255)  # 文章描述
    create_time = models.DateTimeField(auto_now_add=True)  # 创建时间
    category = models.ForeignKey(to="Category", null=True)  # 文章分类
    user = models.ForeignKey(to="UserInfo")  # 作者
    tags = models.ManyToManyField(  # 文章的标签
        to="Tag",
        through="Article2Tag",
        through_fields=("article", "tag"),
    )

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = "文章"
        verbose_name_plural = verbose_name


class ArticleDetail(models.Model):
    """
    文章详情表
    """
    content = models.TextField()  # 文章内容
    article = models.OneToOneField(to="Article")

    class Meta:
        verbose_name = "文章详情"
        verbose_name_plural = verbose_name


class Article2Tag(models.Model):
    """
    文章和标签的多对多关系表
    """
    article = models.ForeignKey(to="Article")
    tag = models.ForeignKey(to="Tag")

    def __str__(self):
        return "{}-{}".format(self.article, self.tag)

    class Meta:
        unique_together = (("article", "tag"),)
        verbose_name = "文章-标签"
        verbose_name_plural = verbose_name


class ArticleUpDown(models.Model):
    """
    点赞表
    """
    user = models.ForeignKey(to="UserInfo", null=True)
    article = models.ForeignKey(to="Article", null=True)
    is_up = models.BooleanField(default=True)  # 点赞还是踩灭

    def __str__(self):
        return "{}-{}".format(self.user_id, self.article_id)

    class Meta:
        unique_together = (("article", "user"),)  # 同一个人只能给一篇文章点一次赞
        verbose_name = "点赞"
        verbose_name_plural = verbose_name


class Comment(models.Model):
    """
    评论表
    """
    article = models.ForeignKey(to="Article")
    user = models.ForeignKey(to="UserInfo")
    content = models.CharField(max_length=255)  # 评论内容
    create_time = models.DateTimeField(auto_now_add=True)

    parent_comment = models.ForeignKey("self", null=True)  # 自己关联自己

    def __str__(self):
        return self.content

    class Meta:
        verbose_name = "评论"
        verbose_name_plural = verbose_name
models.py

  1.多对多三种实现方式

  2.联合唯一

 

三、登录

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <link rel="stylesheet" href="/static/css/bootstrap/bootstrap3.3.7-min.css">
    <link rel="stylesheet" href="/static/css/login.css">
</head>
<body>

<div class="container col-md-4 col-md-push-3">
    <form class="form-horizontal" novalidate>
        {% csrf_token %}
        <div class="form-group">
            <label class="col-sm-3 control-label" for="inputUser">用户名:</label>
            <div class="col-sm-9">
                <input type="text" class="form-control" id="inputUser" placeholder="请输入用户名" autofocus="">
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-3 control-label" for="inputPwd">密 码:</label>
            <div class="col-sm-9">
                <input type="password" class="form-control" id="inputPwd" placeholder="请输入密码">
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-3 control-label" for="inputVcode">验证码:</label>
            <div class="col-sm-4">
                <input type="text" class="form-control" id="inputVcode" placeholder="请输入验证码">
            </div>
            <div class="col-sm-5" id="divImgVcode">
                <img src="{% url 'v_code' %}" alt="" id="imgVcode">
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-3 col-sm-3">
                <div class="checkbox">
                    <label>
                        <input type="checkbox" id="remember-me"> 记住我
                    </label>
                </div>
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-push-4 col-sm-7">
                <button type="button" class="btn btn-block btn-lg btn-info" id="btnLogin">登录</button>
                <p id="pErrMsg"></p>
            </div>
        </div>
    </form>

</div>

<script src="/static/js/jquery/jquery3.3.1-min.js"></script>
<script src="/static/js/setAjax.js"></script>
<script>

    $("#btnLogin").click(function () {
        let username = $("#inputUser").val();
        let password = $("#inputPwd").val();
        let vCode = $('#inputVcode').val();
        $.ajax({
            url:{% url 'login' %},
            type: 'post',
            data: {
                username: username,
                password: password,
                vcode:vCode,
            },
            success: function (res) {
                if (res.code !== 0) {
                    // 认证失败
                    $("#pErrMsg").text(res.msg)
                }
                else {
                    location.href = '{% url 'index' %}'
                }
            }
        })
    });

    $('input').focus(function () {
        $('#pErrMsg').text('')
    });

    let orgUrlLenth = $('#imgVcode')[0].src.length;
    $('#imgVcode').click(function () {
        let timeStamp = new Date().valueOf();
        this.src = this.src.substring(0, orgUrlLenth) + timeStamp;
    })

</script>
</body>
</html>
login.html
from django.shortcuts import render, redirect, HttpResponse
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from PIL import Image, ImageDraw, ImageFont, ImageFilter
from django.http import JsonResponse
from django import views
from io import BytesIO
import random


# Create your views here.


class Login(views.View):

    def get(self, request):
        return render(request, 'login.html')

    def post(self, request):
        res = {'code': 0}
        username = request.POST.get('username')
        password = request.POST.get('password')
        vcode = request.POST.get('vcode')
        if vcode.upper() != request.session.get('vcode').upper():
            res['code'] = 1
            res['msg'] = '验证码有误'
        else:
            # auth认证
            user = authenticate(request, username=username, password=password)
            if user:
                # 认证成功,进行登录
                login(request, user)
            else:
                res['code'] = 1
                res['msg'] = '用户名或密码有误'
        return JsonResponse(res)


def v_code(request):
    # 随机颜色,相对较浅
    def random_color1():
        return random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)

    # 随机颜色,相对较深
    def random_color2():
        return random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)

    # 等概率随机生成 n 个数字、字母(大小写)组合的验证码列表
    def random_code(n):
        rec_list = []
        while n:
            n -= 1
            rdm_first = random.randint(0, 1)
            if rdm_first == 0:
                rec_list.append(str(random.randint(0, 9)))
            else:
                rdm_second = random.randint(0, 1)
                if rdm_second == 0:
                    rec_list.append(chr(random.randint(65, 90)))
                else:
                    rec_list.append(chr(random.randint(97, 122)))
        return rec_list

    vcode_list = random_code(4)

    # 由最终验证码组成的字符串, 存入session中用来校验
    vcode = ''.join(vcode_list)
    request.session['vcode'] = vcode

    # 生成一张图片
    img_obj = Image.new(
        'RGB',  # 生成图片的模式
        (162, 33),  # 大小
        random_color1()  # 颜色
    )

    # 生成一个画笔,指定画板为img_obj
    draw_obj = ImageDraw.Draw(img_obj)

    # 加载本地字体文件,生成字体对象
    font_obj = ImageFont.truetype('static/font/kumo.ttf', size=28)

    # 依次在画板上写上n个验证码
    for i in range(len(vcode_list)):
        draw_obj.text(
            (i * 32 + 20, 0),  # 坐标
            vcode_list[i],  # 内容
            fill=random_color2(),  # 画笔颜色,即字体颜色
            font=font_obj,  # 字体对象
        )

    # # 加干扰线
    # width = 250  # 图片宽度(防止越界)
    # height = 35
    # 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_obj.line((x1, y1, x2, y2), fill=random_color())
    #
    # # 加干扰点
    # for i in range(40):
    #     draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=random_color())

    # # 进行模糊处理
    # img_obj = img_obj.filter(ImageFilter.BLUR)

    iof = BytesIO()  # 生成IO句柄
    img_obj.save(iof, 'png')  # 把图片对象放进IO句柄里,即保存到内存中
    data = iof.getvalue()  # 从内存中读取图片数据

    # 从服务器静态文件夹内读取图片数据
    # with open('static/img/oo.png', 'rb') as f:
    #     data = f.read()

    return HttpResponse(data, content_type='image/png')


@login_required
def index(request):
    user_pro = request.user
    print(user_pro.userinfo.address)
    return HttpResponse('Index')


def log_out(request):
    logout(request)
    return redirect('/login/')
views.py
from django.contrib import admin
from django.urls import path, re_path
from Authapp import views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.Login.as_view(), name='login'),
    path('index/', views.index, name='index'),
    path('logout/', views.log_out, name='logout'),
    re_path('v-code/\d*', views.v_code, name='v_code'),
]
urls.py
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'static')
]

LOGIN_URL = '/login/' 

AUTH_PROFILE_MODULE = 'Authapp.UserInfo'
settings.py

  注意:

  1. ajax,auth,bootstrap(组长教的)       

    python基础 random.randint()  字母65-90、97-122  chr(数字)  颜色(0~255)三个一组(0,0,0)

 

  2. 制作验证码

    1.pillow     import PIL

      Image.new()  img对象.filter(ImageFilter.BLUR)        

      ImageFont.truetype()

      ImageDraw.Draw(img对象)  draw对象.text()  draw对象.line()  draw对象.arc()

      点击切换验证码实现方式:

        1.一直加?

        2.加时间戳

        3.加?删?交替      import  django.views.decorators.cache  import  never_cache (前段禁用缓存, 火狐浏览器有bug)    

    2.BytesIO

      BytesIO()

      img对象.save(IO对象,‘png等格式’)

      IO对象.getvalue()

 

    3.HttpResponse  可以为 src 提供资源

      HttpResponse(data,content_type='image/png') 返回一张图片

 

  3.前段JS   focus     blur       jQuery对象$(“ #id ”)[ 0 ] 对象DOM       length       substring()

    Date().valueOf()    p标签 .text(" ")  location.href="url"  col-sm-offset-4 (push-pull)

 

三、注册

     1. 上传头像,前端

    上传文件的input 标签

    <input type="file" accept="image/*" id="inpAvatar">

 

  2. 上传头像后台(views视图函数)

    接收文件:

    avatar_file = request.FILES.get("avatar")

    保存注册信息到数据库:

    models.UserInfo.objects.create_user(**form_obj.cleaned_data, avatar=avatar_file)

    (因为是File文件,录入数据库时,会按字段保存路径,按路径找目录保存文件)

  3. 上传头像后台(models模型层)

    UserInfo表中:

      avatar是一个FileField 字段,FileField(upload_to="avatars/",)

      avatar具体保存的是一个路径,而不是一个文件

      具体保存文件的是:这个路径对应的目录(配置了media就另当别论)

      (配置midia后)具体保存文件的目录是:在上传路径前面拼接上media后的新路径对应的目录

  3.  关于配置media(不配置就是上面的那样)

    关于static,通过settings设置别名,即让别名代表其绝对路径。放置可提供的静态文件。

      不能是所有文件都是可以访问的,涉及安全、泄密等问题。像用户上传的东西要有

      指定的目录存放。(部署时要做区分)(MEDIA_URL,MEDIA_ROOT是Django提供,也就是说别的就不认识了)

      (这样用户上传的文件是存放在media/ 下了,但是用户数据库中相关字段存的还是原来的路径,所以用时要拼接)

    1.settings.py

      MEDIA_URL = '/media/'    (# 别名)

      MEDIA_ROOT = os.path.join(BASE_DIR, 'media')     (只有一个目录,不像static可以添加多个)

        (给用户上传的所有文件(FileField、request.FILES)指定一个存放目录)

      TEMPLATES 中添加上下文环境(可选配置)(自动把 MEDIA_URL 注册到前端的模板中)

        'django.template.context_processors.media',  (这样前端拼接时候,可以做活路径)

    2.urls.py

      from django.views.static import serve

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

四、主页面、个人主页、分类标签归档页

   0.关于路由urls :各种路由都是开发者设计的。

 

   1.评论点赞,bootstrap小图标 

<span class="glyphicon glyphicon-comment">评论({{ article.comment_count }})</span>
<span class="glyphicon glyphicon-thumbs-up">点赞({{ article.up_count }})</span>

   2.模板中,时间格式化  

      模板语言,过滤器

      <span>发布于&nbsp;{{ article.create_time|date:'Y-m-d H:i:s' }}</span>

   3.二级路由 (名称空间)

    项目urls: from  django.urls  import  include      from  app01  import  urls  as  app01_urls  (别名)

    app01中urls:  from django.urls import path     from  app01  import  views

           urlpatterns = [ 各种后续path ]

  4.orm 插入原生sql语句

     1.一半一半  (利用extra函数在执行orm查询的同时,额外执行一段sql语句,子查询)

    

       2. 对象连接数据库,光标对象执行原生sql

           from  django.db  import  connection

           cusor = connection.cursor()     # 连接数据库,获得一个游标对象

           cursor.execute(" sql语句 ")  # 游标处:输入sql语句

           cursor.fetchall()  # 取查询结果

  5. 注释(分组)、聚合  

     详见 :

     https://www.cnblogs.com/jeavy/p/10926676.html

 

       QuerySet.aggregate()    -->  Django的aggregate()方法作用是对一列值 ( 比如字段 ) 进行统计计算,并以字典(Dict)格式返回统计计算结果。

                  是QuerySet 的一个终止子句(也就是返回的不再是一个QuerySet集合的时候)

                 它返回一个字典。键的名称是标识符,值是计算出来的聚合值。

                 键是 "字段名__聚合函数名" 自动生成出来的。可以向聚合子句提供一个名。

                   Hobby.objects.annotate(age__max=Max(student__age))

 

     QuerySet.annotate()    -->   加注释。加注释意味着要加字段(modle)、加列(表)。

               如果你想要对数据集先进行筛选分组(例如 filter)后再进行某些复杂的(聚合操作或排序)时,需要使用.annotate方法来注释。

               给前一个紧挨着的结果集,加注释、加列(存复杂的聚合操作结果)。

              Course.objects.filter(name__startswith='d').annotate(student_num=Count('student')).order_by('-student_num')[:5]

 

 

  6. 多条视图路由整合一条    

        路由中正则表达式(无名分组、有名分组不可混合使用)(位置传参必须要在关键字传参前面)

        re_path(r"(\d+)/mid/(\w+)",....) 写了几个分组就得匹配几个参数,没有就算匹配不到

五、文章详情页

  1.模板继承   {%  extends  "xx.html"  %}

  2.inclusion_tag  

    1. 返回一段HTML代码,用数据填充的

    2. 用法:

      1. 在app下面创建一个名为 templatetags 的Python Package

      2. 写个例如“mytags.py”:

        from  django  import  template

        register = template.Library()

        @register.inclusion_tag(file="xx.html")

        def  show_menu(*args):

            ...            (此处很多查询操作)

            return  {"k1" : "v1"}

            (xx.html 中使用 k1这个变量)

        (用这个自定义标签只是为了少书写查询语句的代码,并没有减少查询次数)

  3.点赞

    1.orm事务操作

      from django.db import  transaction

      with  transaction.atomic():

        sql1

        sql2

    2.模板语言

      1.{{ xx }}表示的是单值,不在是变量了,

        所以在js中用到这样的值了,要用引号 "{{ request.user }}"

      2.把 js 代码写入js文件中在导入的时候,因为render渲染时不会去渲染引入的js,所以要处理

        (补) 

  4.QuerySet 惰性求值        

     1.len()  及 count()        

     2,.修改单个属性,两个方法的区别     

     3.debug-tool-bar

  5.阶段问题transaction.atomic()    {% extends 'xx.html' %}    {% load  mytags %}{% func 'xx.html' %}

     分组、聚合  F、Q  js中正则包裹 /RegExp /  RegExp.test(str)  RegExp.exec(str)

      

 六、文章评论

 

 

五、代码集

def get_vcode(request):
    # 随机颜色,相对较浅
    def random_color1():
        return random.randint(64, 255), random.randint(64, 255), random.randint(64, 255)

    # 随机颜色,相对较深
    def random_color2():
        return random.randint(32, 127), random.randint(32, 127), random.randint(32, 127)

    # 等概率随机生成 n 个数字、字母(大小写)组合的验证码列表
    def random_code(n):
        rec_list = []
        while n:
            n -= 1
            rdm_first = random.randint(0, 1)
            if rdm_first == 0:
                rec_list.append(str(random.randint(0, 9)))
            else:
                rdm_second = random.randint(0, 1)
                if rdm_second == 0:
                    rec_list.append(chr(random.randint(65, 90)))
                else:
                    rec_list.append(chr(random.randint(97, 122)))
        return rec_list

    vcode_list = random_code(4)

    # 由最终验证码组成的字符串, 存入session中用来校验
    vcode = ''.join(vcode_list)
    request.session['vcode'] = vcode

    # 生成一张图片
    img_obj = Image.new(
        'RGB',  # 生成图片的模式
        (162, 33),  # 大小
        random_color1()  # 颜色
    )

    # 生成一个画笔,指定画板为img_obj
    draw_obj = ImageDraw.Draw(img_obj)

    # 加载本地字体文件,生成字体对象
    font_obj = ImageFont.truetype('static/font/kumo.ttf', size=28)

    # 依次在画板上写上n个验证码
    for i in range(len(vcode_list)):
        draw_obj.text(
            (i * 32 + 20, 0),  # 坐标
            vcode_list[i],  # 内容
            fill=random_color2(),  # 画笔颜色,即字体颜色
            font=font_obj,  # 字体对象
        )

    # # 加干扰线
    # width = 250  # 图片宽度(防止越界)
    # height = 35
    # 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_obj.line((x1, y1, x2, y2), fill=random_color())
    #
    # # 加干扰点
    # for i in range(40):
    #     draw_obj.point([random.randint(0, width), random.randint(0, height)], fill=random_color())
    #     x = random.randint(0, width)
    #     y = random.randint(0, height)
    #     draw_obj.arc((x, y, x+4, y+4), 0, 90, fill=random_color())

    # # 进行模糊处理
    # img_obj = img_obj.filter(ImageFilter.BLUR)

    iof = BytesIO()  # 生成IO句柄
    img_obj.save(iof, 'png')  # 把图片对象放进IO句柄里,即保存到内存中
    data = iof.getvalue()  # 从内存中读取图片数据

    # 从服务器静态文件夹内读取图片数据
    # with open('static/img/oo.png', 'rb') as f:
    #     data = f.read()

    return HttpResponse(data, content_type='image/png')
生成随机验证码

 

  

 

 

 

 

 

 

ok

https://www.cnblogs.com/jeavy/p/10926676.html

posted on 2018-08-13 09:53  kingon  阅读(192)  评论(0编辑  收藏  举报