heirenxilou

一、表设计

1.用户表

继承AbstractUser
扩展
phone 电话号码
avatar 用户头像
create_time 创建时间

外键字段
一对一个人站点表

 

2.个人站点表

site_name 站点名称
site_title 站点标题
site_theme    站点样式

 

3.文章标签表

name    标签名

外键字段
一对多个人站点


4.文章分类表

name    分类名

外键字段
一对多个人站点


5.文章表

title    文章标题
desc    文章简介
content    文章内容
create_time 发布时间

 


数据库字段设计优化(******)

(虽然下述的三个字段可以从其他表里面跨表查询计算得出,但是频繁跨表效率)
up_num 点赞数
down_num 点踩数
comment_num 评论数

外键字段
一对多个人站点
多对多文章标签
一对多文章分类

6.点赞点踩表

记录哪个用户给哪篇文章点了赞还是点了踩
user    ForeignKey(to="User")    
article    ForeignKey(to="Article")    
is_up    BooleanField()

7.文章评论表

记录哪个用户给哪篇文章写了哪些评论内容
user    ForeignKey(to="User")    
article    ForeignKey(to="Article")
content    CharField()
comment_time    DateField()
# 自关联
parent    ForeignKey(to="Comment",null=True)    
# ORM专门提供的自关联写法    
parent    ForeignKey(to="self",null=True)
7.评论表

根评论和子评论
to=\'self\'

 

 modles.py

from django.contrib.auth.models import AbstractUser


class UserInfo(AbstractUser):
    phone = models.BigIntegerField(verbose_name=\'手机号\',null=True)
    # 头像
    avatar = models.FileField(upload_to=\'avatar/\',default=\'avatar/default.png\',verbose_name=\'用户头像\')
    """
    给avatar字段传文件对象 该文件会自动存储到avatar文件下 然后avatar字段只保存文件路径avatar/default.png
    """
    create_time = models.DateField(auto_now_add=True)

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


class Blog(models.Model):
    site_name = models.CharField(verbose_name=\'站点名称\',max_length=32)
    site_title = models.CharField(verbose_name=\'站点标题\',max_length=32)
    # 简单模拟 带你认识样式内部原理的操作
    site_theme = models.CharField(verbose_name=\'站点样式\',max_length=64)  # 存css/js的文件路径


class Category(models.Model):
    name = models.CharField(verbose_name=\'文章分类\',max_length=32)
    blog = models.ForeignKey(to=\'Blog\',null=True)


class Tag(models.Model):
    name = models.CharField(verbose_name=\'文章标签\',max_length=32)
    blog = models.ForeignKey(to=\'Blog\', null=True)


class Article(models.Model):
    title = models.CharField(verbose_name=\'文章标题\',max_length=64)
    desc = models.CharField(verbose_name=\'文章简介\',max_length=255)
    # 文章内容有很多 一般情况下都是使用TextField
    content = models.TextField(verbose_name=\'文章内容\')
    create_time = models.DateField(auto_now_add=True)

    # 数据库字段设计优化
    up_num = models.BigIntegerField(verbose_name=\'点赞数\',default=0)
    down_num = models.BigIntegerField(verbose_name=\'点踩数\',default=0)
    comment_num = models.BigIntegerField(verbose_name=\'评论数\',default=0)

    # 外键字段
    blog = models.ForeignKey(to=\'Blog\', null=True)
    category = models.ForeignKey(to=\'Category\',null=True)
    tags = models.ManyToManyField(to=\'Tag\',
                                  through=\'Article2Tag\',
                                  through_fields=(\'article\',\'tag\')
                                  )


class Article2Tag(models.Model):
    article = models.ForeignKey(to=\'Article\')
    tag = models.ForeignKey(to=\'Tag\')


class UpAndDown(models.Model):
    user = models.ForeignKey(to=\'UserInfo\')
    article = models.ForeignKey(to=\'Article\')
    is_up = models.BooleanField()  # 传布尔值 存0/1


class Comment(models.Model):
    user = models.ForeignKey(to=\'UserInfo\')
    article = models.ForeignKey(to=\'Article\')
    content = models.CharField(verbose_name=\'评论内容\',max_length=255)
    comment_time = models.DateTimeField(verbose_name=\'评论时间\',auto_now_add=True)
    # 自关联
    parent = models.ForeignKey(to=\'self\',null=True)  # 有些评论就是根评论

 

二、注册功能

注册功能用到的技术点有以下

  • forms组件

  • 用户头像前端实时展示

  • ajax

我们之前写forms代码是直接写到views里面的 

为了贯彻面向对象思想,我们会把forms组件代码写到单独的一个py文件里,如果说这个项目从头到位就使用一个form组件,直接写在app下面的任意一个自己写的文件里就行,不过记得不要叫forms,这样在导入Django库里的form时会出现问题,可以叫myforms.py或者其他,只要补交forms就行

如果你项目中用到多个不同的forms组件时,那最好还是自己建一个文件夹,然后把对应的forms组件写上对应的名字,然后放在这个文件夹下面,这样也比较清晰

myforms.py

from django import forms
from app01 import models


class MyRegForm(forms.Form):
    username = forms.CharField(label=\'用户名\', min_length=3, max_length=8,
                               error_messages={
                                   \'required\': \'用户名不能为空\',
                                   \'min_length\': "用户名最少3位",
                                   \'max_length\': "用户名最大8位"
                               },
                               # 还需要让标签有bootstrap样式
                               widget=forms.widgets.TextInput(attrs={\'class\': \'form-control\'})
                               )

    password = forms.CharField(label=\'密码\', min_length=3, max_length=8,
                               error_messages={
                                   \'required\': \'密码不能为空\',
                                   \'min_length\': "密码最少3位",
                                   \'max_length\': "密码最大8位"
                               },
                               # 还需要让标签有bootstrap样式
                               widget=forms.widgets.PasswordInput(attrs={\'class\': \'form-control\'})
                               )

    confirm_password = forms.CharField(label=\'确认密码\', min_length=3, max_length=8,
                                       error_messages={
                                           \'required\': \'确认密码不能为空\',
                                           \'min_length\': "确认密码最少3位",
                                           \'max_length\': "确认密码最大8位"
                                       },
                                       # 还需要让标签有bootstrap样式
                                       widget=forms.widgets.PasswordInput(attrs={\'class\': \'form-control\'})
                                       )
    email = forms.EmailField(label=\'邮箱\',
                             error_messages={
                                 \'required\': \'邮箱不能为空\',
                                 \'invalid\': \'邮箱格式不正确\'
                             },
                             widget=forms.widgets.EmailInput(attrs={\'class\': \'form-control\'})
                             )

    # 钩子函数
    # 局部钩子:校验用户名是否已存在
    def clean_username(self):
        username = self.cleaned_data.get(\'username\')
        # 去数据库中校验
        is_exist = models.UserInfo.objects.filter(username=username)
        if is_exist:
            # 提示信息
            self.add_error(\'username\', \'用户名已存在\')
        return username

    # 全局钩子:校验两次是否一致
    def clean(self):
        password = self.cleaned_data.get(\'password\')
        confirm_password = self.cleaned_data.get(\'confirm_password\')
        if not password == confirm_password:
            self.add_error(\'confirm_password\', \'两次密码不一致\')
        return self.cleaned_data

views.py的register视图函数

def register(request):
    form_obj = MyRegForm()
    if request.method == \'POST\':
        back_dic = {"code": 1000, \'msg\': \'\'}
        # 校验数据是否合法
        form_obj = MyRegForm(request.POST)
        # 判断数据是否合法
        if form_obj.is_valid():
            # print(form_obj.cleaned_data)  # {\'username\': \'jason\', \'password\': \'123\', \'confirm_password\': \'123\', \'email\': \'123@qq.com\'}
            clean_data = form_obj.cleaned_data  # 将校验通过的数据字典赋值给一个变量
            # 将字典里面的confirm_password键值对删除
            clean_data.pop(\'confirm_password\')  # {\'username\': \'jason\', \'password\': \'123\', \'email\': \'123@qq.com\'}
            # 用户头像
            file_obj = request.FILES.get(\'avatar\')
            """针对用户头像一定要判断是否传值 不能直接添加到字典里面去"""
            if file_obj:
                clean_data[\'avatar\'] = file_obj
            # 直接操作数据库保存数据
            models.UserInfo.objects.create_user(**clean_data)
            back_dic[\'url\'] = \'/login/\'
        else:
            back_dic[\'code\'] = 2000
            back_dic[\'msg\'] = form_obj.errors
        return JsonResponse(back_dic)
    return render(request,\'register.html\',locals())

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container-fluid">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <h1 class="text-center">注册</h1>
            <form id="myform">  <!--这里我们不用form表单提交数据 知识单纯的用一下form标签而已-->
                {% csrf_token %}
                {% for form in form_obj %}
                    <div class="form-group">
                        <label for="{{ form.auto_id }}">{{ form.label }}</label>
                        {{ form }}
                        <span style="color: red" class="pull-right"></span>
                    </div>
                {% endfor %}
                <div class="form-group">
                    <label for="myfile">头像
                        {% load static %}
                        <img src="{% static \'img/default.png\' %}" id=\'myimg\' alt="" width="100" style="margin-left: 10px">
                    </label>
                    <input type="file" id="myfile" name="avatar" style="display: none" >
                </div>

                <input type="button" class="btn btn-primary pull-right" value="注册" id="id_commit">
            </form>
        </div>
    </div>
</div>

<script>
    $("#myfile").change(function () {
        // 文件阅读器对象
        // 1 先生成一个文件阅读器对象
        let myFileReaderObj = new FileReader();
        // 2 获取用户上传的头像文件
        let fileObj = $(this)[0].files[0];
        // 3 将文件对象交给阅读器对象读取
        myFileReaderObj.readAsDataURL(fileObj)  // 异步操作  IO操作
        // 4 利用文件阅读器将文件展示到前端页面  修改src属性
        // 等待文件阅读器加载完毕之后再执行
        myFileReaderObj.onload = function(){
             $(\'#myimg\').attr(\'src\',myFileReaderObj.result)
        }
    })

    $(\'#id_commit\').click(function () {
        // 发送ajax请求     我们发送的数据中即包含普通的键值也包含文件
        let formDataObj = new FormData();
        // 1.添加普通的键值对
        {#console.log($(\'#myform\').serializeArray())  // [{},{},{},{},{}]  只包含普通键值对#}
        $.each($(\'#myform\').serializeArray(),function (index,obj) {
            {#console.log(index,obj)#}  // obj = {}
            formDataObj.append(obj.name,obj.value)
        });
        // 2.添加文件数据
        formDataObj.append(\'avatar\',$(\'#myfile\')[0].files[0]);

        // 3.发送ajax请求
        $.ajax({
            url:"",
            type:\'post\',
            data:formDataObj,

            // 需要指定两个关键性的参数
            contentType:false,
            processData:false,

            success:function (args) {
                if (args.code==1000){
                    // 跳转到登陆页面
                    window.location.href = args.url
                }else{
                    // 如何将对应的错误提示展示到对应的input框下面
                    // forms组件渲染的标签的id值都是 id_字段名
                    $.each(args.msg,function (index,obj) {
                        {#console.log(index,obj)  //  username        ["用户名不能为空"]#}
                        let targetId = \'#id_\' + index;
                        $(targetId).next().text(obj[0]).parent().addClass(\'has-error\')
                    })
                }
            }
        })
    })
    // 给所有的input框绑定获取焦点事件
    $(\'input\').focus(function () {
        // 将input下面的span标签和input外面的div标签修改内容及属性
        $(this).next().text(\'\').parent().removeClass(\'has-error\')
    })
</script>
</body>
</html>

三、登录功能 

登录功能的技术点有以下

  • 实现图片验证码

  • ajax

分类:

技术点:

相关文章: