liuqimeng

需要哪些表

UserInfo

class UserInfo(AbstractUser):
    """
    用户信息表
    """
    nid = models.AutoField(primary_key=True)
    phone = models.CharField(max_length=11, null=True, unique=True)
    avatar = models.FileField(upload_to="avatars/", default="avatars/default.png", verbose_name="头像")
    create_time = models.DateTimeField(auto_now_add=True)
    blog = models.OneToOneField(to="Blog", to_field="nid", null=True)

    def __str__(self):
        return self.username
用户信息表

Blog

class Blog(models.Model):
    """
    博客信息
    """
    nid = models.AutoField(primary_key=True)
    title = models.CharField(max_length=64)  # 个人博客标题
    site = models.CharField(max_length=32, unique=True)  # 个人博客后缀
    theme = models.CharField(max_length=32)  # 博客主题

    def __str__(self):
        return self.title
个人站点

Category

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

    def __str__(self):
        return self.title
文章分类

Tag

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

    def __str__(self):
        return self.title
标签表

Article

class Article(models.Model):
    """
    文章
    """
    nid = models.AutoField(primary_key=True)
    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", to_field="nid", null=True)
    user = models.ForeignKey(to="UserInfo", to_field="nid")
    tags = models.ManyToManyField(  # 中介模型
        to="Tag",
        through="Article2Tag",
        through_fields=("article", "tag"),  # 注意顺序!!!
    )
    # 评论数
    comment_count = models.IntegerField(default=0)
    # 点赞数
    top_count = models.IntegerField(default=0)
    # 点踩数
    bottom_count = models.IntegerField(default=0)

    def __str__(self):
        return self.title
文章表

ArticleDetail

class ArticleDetail(models.Model):
    """
    文章详情表
    """
    nid = models.AutoField(primary_key=True)
    content = models.TextField()
    article = models.OneToOneField(to="Article", to_field="nid")
文章详情表

Article2Tag

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


    def __str__(self):
        return "{}-{}".format(self.article.title,self.tag.title)
    class Meta:
        unique_together = (("article", "tag"),)
       
文章和标签的第三张表

ArticleUpDown

class ArticleUpDown(models.Model):
    """
    点赞表
    """
    nid = models.AutoField(primary_key=True)
    user = models.ForeignKey(to="UserInfo", null=True)
    article = models.ForeignKey(to="Article", null=True)
    is_up = models.BooleanField(default=True)

    class Meta:
        unique_together = (("article", "user"),)
        verbose_name_plural = "文章点赞"
点赞表

Comment

class Comment(models.Model):
    """
    评论表
    """
    nid = models.AutoField(primary_key=True)
    article = models.ForeignKey(to="Article", to_field="nid")
    user = models.ForeignKey(to="UserInfo", to_field="nid")
    content = models.CharField(max_length=255)  # 评论内容
    create_time = models.DateTimeField(auto_now_add=True)
    parent_comment = models.ForeignKey("self", null=True,blank=True)


    def __str__(self):
        return self.content
评论表

注册

基于forms和ajax注册

url(r\'^register/\', views.register),
urls.py

现在APP下面创建forms.py文件,存放form类,方便日后导入调用

from django import forms
from django.core.exceptions import ValidationError
from blog import models


# 注册form类
class RegForm(forms.Form):
    username = forms.CharField(
        max_length=16,
        label="用户名",
        error_messages={
            "max_length": "最长16位",
            "required": "不能为空"
        },
        widget=forms.widgets.TextInput(attrs={"class": "form-control"})
    )
    password = forms.CharField(
        max_length=16,
        label="密码",
        error_messages={
            "max_length": "最长16位",
            "required": "不能为空"
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"})
    )
    re_password = forms.CharField(
        max_length=16,
        label="确认密码",
        error_messages={
            "max_length": "最长16位",
            "required": "不能为空"
        },
        widget=forms.widgets.PasswordInput(attrs={"class": "form-control"})
    )
    email = forms.EmailField(
        label="邮箱",
        widget=forms.widgets.EmailInput(attrs={"class": "form-control"}),
        error_messages={
            "required": "不能为空",
            "invalid": "邮箱格式不正确!"
        }
    )

    # 局部钩子 校验用户名
    def clean_username(self):
        username = self.cleaned_data.get("username")
        obj = models.UserInfo.objects.filter(username=username)
        if obj:
            raise ValidationError("用户已经注册!")
        else:
            return username

    # 局部钩子 校验邮箱
    def clean_email(self):
        email = self.cleaned_data.get("email")
        obj = models.UserInfo.objects.filter(email=email)
        if obj:
            raise ValidationError("邮箱已经存在!")
        else:
            return email

    # 全局钩子
    def clean(self):
        password = self.cleaned_data.get("password")
        re_password = self.cleaned_data.get("re_password")
        if password == re_password:
            return self.cleaned_data
        else:
            self.add_error("re_password", ValidationError("两次密码不一致"))
forms.py

局部钩子校验某个字段是否满足要求,全局钩子可以拿到所有字段的值,经常用于判断两次密码是否一致

def register(request):
    res = {"status":None,"msg":None}
    if request.method == "POST":
        form_obj = forms.RegForm(request.POST)
        if form_obj.is_valid():
            form_obj.cleaned_data.pop("re_password")
            avatar_obj = request.FILES.get("avatar")
            if avatar_obj:
                models.UserInfo.objects.create_user(**form_obj.cleaned_data,avatar=avatar_obj)
            else:
                models.UserInfo.objects.create_user(**form_obj.cleaned_data)
            res["status"] = True
            res["msg"] = "/index/"
            return JsonResponse(res)
        else:
            res["status"] = False
            res["msg"] = form_obj.errors
            return JsonResponse(res)
    form_obj = forms.RegForm()
    return render(request,\'register.html\',{"form_obj":form_obj})
views.py

登录

登录基于极验滑动验证码

技术文档

注册头像预览

<!--头像-->
                    <div class="form-group">
                        <label for="" class="col-sm-2 control-label">头像</label>
                        <div class="col-sm-10">
                            <label for="avatar">
                                <img src="/static/images/default.jpeg" alt="" width="40">
                            </label>
                                <input type="file" id="avatar" style="display: none">
                        </div>
                    </div>
register.html
$("#avatar").on("change",function () {
            var fileRead = new FileReader() //创建读文件对象
            fileRead.readAsDataURL(this.files[0]) //读取文件
            fileRead.onload = function(){
                $("img").attr("src",fileRead.result) // src可以为路径,也可以为编码的数字
            }
        })
监听input

media的配置

一般前端传的文件都放在media目录下,服务器的静态文件放在static

先在settings.py配置

# django 用户上传的文件都在media中
MEDIA_URL = "/media/"
# media配置,用户上传的文件都默认在这个文件下
MEDIA_ROOT = os.path.join(BASE_DIR,"media")
settings.py

在urls.py配置

# media
    url(r"^media/(?P<path>.*)$",serve,{"document_root":settings.MEDIA_ROOT}),
urls.py
一旦配置media,ORM文件对象指定路径的时候,无需从media开始,直接指定目录即可,因为上传的文件会默认存在media下
注意

 扩展自带的auth_user表

新建一个表,一对一关联上面的auth_user表

继承的方式

from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser):
            phone = models.CharField(max_length=11)
            addr = models.CharField(max_length=128)
相当于对默认的auth_user表做了扩展, 并且代替auth_user
在settings.py中一定要加
                AUTH_USER_MODEL = \'app名.类名\'
注意

 render()到底渲染的什么?

1.

渲染的时候注意在脚本里面,比如script标签里面,要加引号

var test = "{{obj.title}}"

渲染出来可能是数字,没关系,如果是字符,那就是变量了

2.

JS文件是引入的话,就不能用括号了,可以在页面创建个隐藏的div,讲要传的值赋值给div的任意属性,然后用JS动态找到生成

防止XSS攻击

html_doc = BeautifulSoup(article_content,"html.parser")
        for tag in html_doc:
            if tag.name in ["script","link"]:
                tag.decompose() # 删除满足条件的标签
过滤非法标签

评论楼

1楼    111
2楼    222
3楼    333
4楼    @111 444
5楼    555
6楼   @555 666
View Code

评论树

111
    444
         555
    666
222
    777
333
     888
View Code
# 评论树
def comment_tree(request,article_id):
    comment_list = models.Comment.objects.filter(article_id=article_id).values("nid","content","parent_comment_id")
    comment_list = list(comment_list)
    return JsonResponse(comment_list,safe=False) # 当序列化非字典时使用safe
views.py
 <!--评论树-->
    <p>评论列表</p>
    <div class="comment_tree"></div>
    <!--end 评论树-->

//评论树ajax
        $.ajax({
            url: "/blog/comment_tree/{{ article_obj.pk }}/",
            type: "GET",
            success: function (comment_list) {
                $.each(comment_list, function (index, dict) {
                    console.log(index, dict)
                    var s = "<div class=\'comment-item\' comment_id=" + dict.nid + "><span>" + dict.content + "</span> </div>"
                    if (dict.parent_comment_id) {
                        //存在子评论
                        $("[comment_id=" + dict.parent_comment_id + "]").append(s)
                    } else {

                        //只有根评论
                        $(".comment_tree").append(s)

                    }
                })

            }
        })
add_article.html

分类:

技术点:

相关文章: