BBS版本二


BBS项目

表设计

一个项目中最重要的不是业务逻辑的书写
而是前期的表设计,只要将表设计好了,后续的功能书写才会一帆风顺
'''

# bbs表设计
    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)  # 自关联
        # parent		ForeignKey(to="self",null=True)  # orm提供的语法自关联
        
        评论分为根评论和子评论(可以评论别人评论的)
        根评论:是评论文章内容的评论
        字评论:是回复别人的评论
       # 外键关系:是一对多的
'''

数据库表创建及同步

'''因为django自带的数据库不好用, 本次项目使用的是MySql数据库'''

from django.db import models
from django.contrib.auth.models import AbstractUser


# 用户表
class UserInfo(AbstractUser):
    phone = models.CharField(verbose_name='手机号', max_length=32, blank=True)
    # 头像
    avatar = models.FileField(verbose_name='头像', upload_to='static/img', default='static/img/public.png')
    '''
    给avatar字段传文件对象,该文件会自动存储到avatar文件下,然后avatar字段只保存文件路径
    '''
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True)

    # 一对一站点表:用户表和站点表是一对一关系
    blog = models.OneToOneField(verbose_name='关联站点表', to='Blog', null=True)

    def __str__(self):
        return self.username

    class Meta:
        verbose_name_plural = '用户表'


# 站点表
class Blog(models.Model):
    site_name = models.CharField(verbose_name='站点名称', max_length=64)
    site_title = models.CharField(verbose_name='站点标题', max_length=64)
    site_theme = models.CharField(verbose_name='站点样式', max_length=64)  # 存css/js文件路径

    class Meta:
        verbose_name_plural = '站点表'

    def __str__(self):
        return self.site_name


# 标签表
class Tag(models.Model):
    name = models.CharField(verbose_name='文章标签', max_length=32)

    # 一对多站点表:标签表和站点表是一对多关系
    blog = models.ForeignKey(verbose_name='关联站点表', to='Blog')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '标签表'


# 分类表
class Category(models.Model):
    name = models.CharField(verbose_name='分类名称', max_length=32)

    # 一对多站点表:分类表和站点表是一对多关系
    blog = models.ForeignKey(verbose_name='关联站点表', to='Blog')

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = '分类表'


# 文章表
class Article(models.Model):
    title = models.CharField(verbose_name='文章标题', max_length=128)
    desc = models.CharField(verbose_name='文章简介', max_length=512)
    content = models.TextField(verbose_name='文章内容')
    create_time = models.DateTimeField(verbose_name='创建时间', 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(verbose_name='关联站点表', to='Blog')
    tags = models.ManyToManyField(to='Tag', through='Article2Tag',
                                  through_fields=('article', 'tag'),
                                  verbose_name='关联第三张表'
                                  )
    category = models.ForeignKey(verbose_name='关联分类表', to='Category')

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = '文章表'


# 自己创建的第三张表,用于扩建字段
class Article2Tag(models.Model):
    article = models.ForeignKey(to='Article')
    tag = models.ForeignKey(to='Tag')

    class Meta:
        verbose_name_plural = '第三张表'


# 点赞点踩表
class UpAndDown(models.Model):
    user = models.ForeignKey(verbose_name='关联用户表', to='UserInfo')
    article = models.ForeignKey(verbose_name='关联文章表', to='Article')
    is_up = models.BooleanField()  # 存的是0/1

    class Meta:
        verbose_name_plural = '点赞点踩表'


# 评论表
class Comment(models.Model):
    user = models.ForeignKey(verbose_name='关联用户表', to='UserInfo')
    article = models.ForeignKey(verbose_name='关联文章表', to='Article')
    content = models.CharField(verbose_name='评论内容', max_length=512)
    create_time = models.DateTimeField(verbose_name='评论时间', auto_now_add=True)

    # 根评论和子评论一对多的关系,自关联
    parent_id = models.ForeignKey(to='self', null=True)  # self就是关联自己表

    class Meta:
        verbose_name_plural = '评论表'

总路由

from django.conf.urls import url
from django.contrib import admin
from app01 import views
from django.views.static import serve
from BBS1 import settings

urlpatterns = [
    url(r'^admin/', admin.site.urls),

    # 注册功能
    url(r'^register/', views.register, name='reg'),
    # 登录功能
    url(r'^login/', views.login, name='lo'),
    # 图片验证码
    url(r'^get_code/', views.get_code, name='gc'),
    # 首页
    url(r'^home/', views.home),
    url(r'^$', views.home),
    # 修改密码
    url(r'^set_password/', views.set_password, name='set_pwd'),
    # 退出登录
    url(r'^logout/', views.logout, name='out'),
    # 点赞点踩
    url(r'^up_or_down/', views.up_or_down),
    # 评论功能
    url(r'^comment/', views.comment),

    # 侧边栏筛选功能
    url(r'^(?P\w+)/(?Pcategory|tag|archive)/(?P.*)/', views.site),
    # 文章详情页
    url(r'^(?P\w+)/article/(?P\d+)/', views.archive_detail),
    # 个人站点
    url(r'(?P\w+)/$', views.site, name='site'),

    # 开设后端文件夹资源
    url(r'media/(?P.*)', serve, {'document_root': settings.MEDIA_ROOT}),
]

注册功能

'''
如果自己的项目只用到一个forms组件,那么可以创建一个py文件书写
但是,项目需要使用多个forms组件,建议创建一个文件夹在文件夹根据forms组件功能的不同创建不同的py文件
'''

用forms组件渲染注册页面

# 书写针对用户表的forms组件代码
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位'
                               },
                               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位'
                               },
                               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位'
                                       },
                                       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

实时展示头像


注册页面搭建




    
    Title
    
    {% load static %}
{#    #}
{#    #}
    


注册页面

{% csrf_token %} {% for form in form_obj %}
{# 拿到input框id值#} {{ form }} {{ form.errors.0 }}
{% endfor %}

注册功能实现

from django.shortcuts import render
from app01.myforms import MyRegForm
from app01 import models
from django.http import JsonResponse

# Create your views here.

def register(request):
    '''前后端交互,最好定义一个字典'''
    back_dic = {'code': 1000, 'msg': ''}
    # 1.生成一个不传参数的对象
    form_obj = MyRegForm()
    # 2.接收前端数据
    if request.method == 'POST':
        # 检测数据是否合法,并把数据添加到空对象中
        form_obj = MyRegForm(request.POST)

        # 3.判断数据是否合法
        if form_obj.is_valid():
            # print(form_obj.cleaned_data)  # {'username': 'meng', 'password': '111', 'confirm_password': '111', 'email': '123@qq.com'}
            # 将校验通过的数值赋值给一个变量
            clean_data = form_obj.cleaned_data
            # 将字典里的confirm_password键值对删除
            clean_data.pop('confirm_password')  # pop() 函数用于移除列表中的一个元素
            # 获取用户头像
            file_obj = request.FILES.get('avatar')
            '''针对用户头像一定要判断是否传值,不能直接添加到字典里'''
            if file_obj:
                clean_data['avatar'] = file_obj  # 添加到字典里
            # 4.保存到数据库里,以普通用户的形式存入
            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())

'''
注册功能总结:
	1.先搭建一个注册页面,并渲染出基本功能
	2.给注册按钮绑定点击事件,发送ajax请求
	3.根据返回来的数据,做出相应的逻辑判断
	4.美化注册功能样式
'''
'''
业务逻辑总结:
	1.先获取数据
	2.比对数据是否正确
	3.添加到数据库
	4.书写错误逻辑
'''

登录功能

'''书写img标签的src属性:	1.图片路径	2.src	3.图片二进制数据'''

随机验证码功能

'''
图片相关的模块:
    pip3 install pillow
'''
from PIL import Image, ImageDraw, ImageFont

'''
Image;生成图片
ImageDraw:能够在图片上乱涂乱花
ImageFont:控制字体的样式
'''

from io import BytesIO, StringIO

'''
内存管理器模块
BytesIO:临时存储数据,返回的数据是二进制
StringIO:临时存储数据,返回的数据是字符串
'''
import random


def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)


def get_code(request):
    img_obj = Image.new('RGB', (430, 35), get_random())  # 先产生一张图片
    img_draw = ImageDraw.Draw(img_obj)  # 产生一个画笔对象
    img_font = ImageFont.truetype('static/font/111.ttf', 30)  # 字体样式

    # 随机验证码
    code = ''
    for i in range(4):
        random_upper = chr(random.randint(65, 90))
        random_lower = chr(random.randint(97, 122))
        random_int = str(random.randint(0, 9))
        # 从上面随机选择一个
        tmp = random.choice([random_upper, random_lower, random_int])
        # 将产生随机字符串写入到图片上
        img_draw.text((i * 60 + 60, -2), tmp, get_random(), img_font)
        # 拼接随机字符串
        code += tmp
    print(code)
    # 把随机验证码存储起来,方便其他视图函数比对使用
    request.session['code'] = code
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())




*********************html*************************
# 点击图片刷新验证码

'''利用的是前端url更改就会,重新发送一遍get请求,达到刷新的效果'''

搭建登录功能页面




    
    Title
    
    



登录

登录功能实现


def login(request):
    back_dic = {'code': 1000, 'msg': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('code')
        # 1.统一转成小写,校验验证码是否正确
        if request.session.get('code').lower() == code.lower():
            # 2.校验用户名和密码是否正确
            user_obj = auth.authenticate(request, username=username, password=password)
            if user_obj:
                # 保存用户状态
                auth.login(request, user_obj)
                back_dic['url'] = '/home/'
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或者密码错误'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码错误'
        return JsonResponse(back_dic)
    return render(request, 'login.html')

首页

修改密码功能

from django.contrib.auth.decorators import login_required  # 登录认证模块
@login_required
def set_password(request):
    back_dic = {'code': 1000, 'msg': ''}
    if request.method == 'POST':
        old_password = request.POST.get('old_password')
        new_password = request.POST.get('new_password')
        confirm_password = request.POST.get('confirm_password')
        # 校验原密码是否正确
        is_right = request.user.check_password(old_password)
        if is_right:
            if new_password == confirm_password:
                request.user.set_password(new_password)
                request.user.save()
                back_dic['msg'] = '修改成功'
                back_dic['url'] = '/login/'
            else:
                back_dic['code'] = 1001
                back_dic['msg'] = '两次密码不一致'
        else:
            back_dic['code'] = 1002
            back_dic['msg'] = '原密码错误'
    return JsonResponse(back_dic)

退出登录

@login_required
def logout(request):
    auth.logout(request)
    return redirect('/home/')

admin后台管理

'''
    django提供了一个可视化的界面,用来对模型表进行数据的增删改查
    如果想要使用admin后台管理操作模型表
    必须先去admin.py注册需要操作的哪些表
    admin会给每一个注册了的模型表自动生成增删改查四条url
'''
# admin.py
from django.contrib import admin
from app01 import models
# Register your models here.

admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Category)
admin.site.register(models.Article)
admin.site.register(models.Article2Tag)
admin.site.register(models.UpAndDown)
admin.site.register(models.Comment)

# admin默认展示英文名称,想要变成中文,去models中定义一个类
    class Meta:
        verbose_name_plural = '用户表'
        
'''
自己录入数据顺序:
	文章表
	用户表
	标签表
	标签和文章表
'''

用户头像展示

'''
1.网址所使用的静态文件默认放在static文件夹下
2.用户上传的静态文件也应该单独放在一个文件夹下
'''

# media配置
	该配置可以让用户上传的所有文件都固定存放在某一个指定的文件下
    # 1.配置用户上传的文件存储位置
        MEDIA_ROOT = os.path.join(BASE_DIR, 'media')  # 会自动创建多级目录

    # 2.开设后端指定文件夹资源
        需要去urls.py配置
        from django.views.static import serve
        from BBS1 import settings
        # 开设后端文件夹资源
        url(r'media/(?P.*)',serve,{'document_root':settings.MEDIA_ROOT})  # 固定写法
        
# home.py
    ...  # 拼接路径

首页页面搭建




    
    Title
    
    
    




焦点

今日热点

焦点

今日热点

焦点

今日热点
{% for article_obj in article_queryset %}

{{ article_obj.title }}

{{ article_obj.desc }}

{# xiaxveliang 2021-12-15 18:59 0 0 33#} {{ article_obj.blog.userinfo.username }}   {{ article_obj.create_time|date:'Y-m-d H:i' }}   {{ article_obj.up_num }}   {{ article_obj.down_num }}

{% endfor %}

焦点

今日热点

焦点

今日热点

焦点

今日热点

首页功能实现

def home(request):
    # 查询网站所有的文章
    article_queryset = models.Article.objects.all()
    return render(request, 'home.html', locals())

个人站点

图片防盗链

# 如何避免别的网站直接通过本网站的url访问本网站资源

# 简单的防盗
	原理:
    	判断请求是从哪个网站发出的,如果是本网站正常访问,如果是其他网站直接拒绝
        请求头里面有一个专门记录请求来自于哪个网址的参数:refer
        	Referer: http://127.0.0.1:8001/  # 不是自己的网站请求,直接拒绝

个人站点css样式

# 在static文件夹下css根据站点名不同创建css样式py文件
然后在页面上引入:
	  # 动态引入

侧边栏展示功能


文章分类

{% for category in category_list %}

{{ category.0 }}({{ category.1 }})

{% endfor %}

文章标签

{% for tag in tag_list %}

{{ tag.0 }}({{ tag.1 }})

{% endfor %}

日期归档

{% for date in date_list %}

{{ date.0|date:'Y年m月' }}({{ date.1 }})

{% endfor %}
from django.db.models import Count
from django.db.models.functions import TruncMonth
def site(request, username):
    # 1.先校验个人站点是否存在
    user_obj = models.UserInfo.objects.filter(username=username).first()
    # 2.用户不存在,返回一个404页面
    if not user_obj:
        return render(request, '404.html')
    blog = user_obj.blog  # 获取个人站点
    # 3.查询当前个人站点的所有文章
    article_list = models.Article.objects.filter(blog=blog)

    # 侧边栏搭建
    # 1.查询当前用户所有的分类已经分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')  # 当前用户所有的分类
    # print(category_list)  # 

    # 2.查询当前用户所有的标签及标签下的文章数
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')
    # print(tag_list)  # 

    # 3.按照年月统计所有的文章
    '''这里要在settins.py配置:USE_TZ = False'''
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values_list('month').annotate(count_num=Count('pk')).values_list('month', 'count_num')
    # print(date_list)  # 
    return render(request, 'site.html', locals())

侧边栏筛选功能

def site(request, username,**kwargs):
    # 1.先校验个人站点是否存在
    user_obj = models.UserInfo.objects.filter(username=username).first()
    # 2.用户不存在,返回一个404页面
    if not user_obj:
        return render(request, '404.html')
    blog = user_obj.blog  # 获取个人站点
    # 3.查询当前个人站点的所有文章
    article_list = models.Article.objects.filter(blog=blog)

    '''侧边栏筛选功能'''
    # 判断kwargs是否有值
    if kwargs:
        # print(kwargs)  # {'condition': 'tag', 'param': '1'}
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        # 判断用户到底想要按照哪个条件筛选数据
        if condition == 'category':
            article_list = article_list.filter(category_id=param)
        elif condition == 'tag':
            article_list = article_list.filter(tags__id=param)
        else:
            year, month = param.split('-')  # 解压赋值 2020-11 [2020,11]
            article_list = article_list.filter(create_time__year=year,create_time__month=month)


    # 侧边栏搭建
    # 1.查询当前用户所有的分类已经分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')  # 当前用户所有的分类
    # print(category_list)  # 

    # 2.查询当前用户所有的标签及标签下的文章数
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name', 'count_num', 'pk')
    # print(tag_list)  # 

    # 3.按照年月统计所有的文章
    '''这里要在settins.py配置:USE_TZ = False'''
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values_list('month').annotate(count_num=Count('pk')).values_list('month', 'count_num')
    # print(date_list)  # 
    return render(request, 'site.html', locals())

# site.html
    

{{ category.0 }}({{ category.1 }})

{{ tag.0 }}({{ tag.1 }})

{{ date.0|date:'Y年m月' }}({{ date.1 }})

文章详情页

# 注意事项:
    1.url设计
        /username/article/1
    2.添加完路由测试下,是否造成路由冲突
    3.文章详情页和个人站点基本一致,可以使用模板继承
    4.侧边栏的渲染需要传入数据才能渲染,并且该在很多页面都需要使用
    	1)直接拷贝  # 不推荐
        2)将侧边栏制作成inclusion_tag
        '''
        步骤:
        	1.在该应用下创建一个名为:templatetags文件夹
        	2.在该文件内创建一个任意名称的py文件
        	3.在该文件中固定写两行代码
        	from django import template
        	register = template.Library()
        	# 自定义过滤器
        '''

做一个继承的母版
{% extends 'base.html' %}
{% block content %}
    

{{ article_obj.title }}

{{ article_obj.content| safe }}
{% endblock %} '''实现所有文章都可以跳转''' # 在site中补上文章详情的链接

{{ article_obj.title }}

# 在home中补上文章详情的链接

{{ article_obj.title }}

制作一个inclusion_tag

# 制作成,在其他页面也可以使用侧边栏
from django import template
from app01 import models
from django.db.models import Count
from django.db.models.functions import TruncMonth

register = template.Library()


@register.inclusion_tag('left_menu.html')
def left_menu(username):
    # 构造侧边栏需要的数据

    # 1.先校验个人站点是否存在
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog  # 获取个人站点

    # 侧边栏搭建
    # 1.查询当前用户所有的分类已经分类下的文章数
    category_list = models.Category.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list(
        'name', 'count_num', 'pk')  # 当前用户所有的分类
    # print(category_list)  # 

    # 2.查询当前用户所有的标签及标签下的文章数
    tag_list = models.Tag.objects.filter(blog=blog).annotate(count_num=Count('article__pk')).values_list('name','count_num', 'pk')
    # print(tag_list)  # 

    # 3.按照年月统计所有的文章
    '''这里要在settins.py配置:USE_TZ = False'''
    date_list = models.Article.objects.filter(blog=blog).annotate(month=TruncMonth('create_time')).values_list(
        'month').annotate(count_num=Count('pk')).values_list('month', 'count_num')
    # print(date_list)  # 

    return locals()

# 在把侧边栏的3份放到一个自定义html文件中,然后在base文件中引用
        
{% load mytag %} {% left_menu username %}

点赞点踩

# 如何区分是点赞还是点踩
'''
1.给两个标签绑定一个点击事件
		// 给所有的action类绑定点击事件
        $('.action').click(function () {
            alert($(this).hasClass('diggit'))  // 判断是否有这个属性,true是赞,反之踩
        })
'''

{% block content %}
    

{{ article_obj.title }}

{{ article_obj.content | safe }}
{# 点赞点踩样式#}
{{ article_obj.up_num }}
{{ article_obj.down_num }}
{# 点赞点踩样式结束#} {# 文章评论样式开始#} {% if request.user.is_authenticated %}

 发表评论

{% else %}
  • 注册
  • 登录
  • {% endif %} {# 文章评论样式结束#} {% endblock %} {% block js %} {% endblock %} def up_or_down(request): ''' 1.校验用户是否登录 2.自己不能给自己点赞点踩 3.当前用户是否已经点赞,点过之后不能在点赞 4.操作数据库 ''' if request.is_ajax(): back_dic = {'code': 1000, 'msg': ''} # 1.判断当前用户是否登录 if request.user.is_authenticated(): article_id = request.POST.get('article_id') is_up = request.POST.get('is_up') is_up = json.loads(is_up) # 前端传的数字符串js格式,需要反序列化 # print(is_up) # True # 1.1获取文章对象 article_obj = models.Article.objects.filter(pk=article_id).first() # 文章对象 # print(article_obj.pk) # 主键值 # 2.判断当前文章是否是自己的 if not article_obj.blog.userinfo == request.user: # article_obj.blog.userinfo:文章属于哪个用户,request.user:当前用户 # 3.校验当前用户是否点赞 is_click = models.UpAndDown.objects.filter(user=request.user, article_id=article_obj) if not is_click: # 4.入库 # 判断当前用户点了赞还是点了踩,从而给哪个字段加一 if is_up: models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1) back_dic['msg'] = '点赞成功' else: models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1) back_dic['msg'] = '点踩成功' # 操作点赞点踩表 models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up) else: back_dic['code'] = 1130 back_dic['msg'] = '已点赞' else: back_dic['code'] = 1131 back_dic['msg'] = '不能给自己点赞' else: back_dic['code'] = 1132 back_dic['msg'] = '请先登录' return JsonResponse(back_dic)

    文章评论

    {% extends 'base.html' %}
    
    {% block css %}
        
    {% endblock %}
    
    {% block content %}
        

    {{ article_obj.title }}

    {{ article_obj.content | safe }}
    {# 点赞点踩样式#}
    {{ article_obj.up_num }}
    {{ article_obj.down_num }}
    {# 点赞点踩样式结束#} {# 评论楼渲染开始#}
      {% for comment in comment_list %}
    • #{{ forloop.counter }}楼 {{ comment.create_time|date:'Y-m-d H:i' }} {{ comment.user.username }} 回复
      {# 判断当前评论是否是子评论,如果是需要渲染人名#} {% if comment.parent_id %}

      @{{ comment.parent.user.username }}

      {% endif %}

      {{ comment.content }}

    • {% endfor %}
    {# 评论楼渲染结束#} {# 文章评论样式开始#} {% if request.user.is_authenticated %}

     发表评论

    {% else %}
  • 注册
  • 登录
  • {% endif %} {# 文章评论样式结束#} {% endblock %} {% block js %} {% endblock %} def comment(request): if request.is_ajax(): back_dic = {'code': 1000, 'msg': ''} if request.method == 'POST': # 判断当前用户是否登录 if request.user.is_authenticated: article_id = request.POST.get('article_id') content = request.POST.get('content') parent_id = request.POST.get('parent_id') # print(parent_id) print(content) print(article_id) # 直接存储数据 with transaction.atomic(): # 修改普通字段 models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1) # 存储评论内容 models.Comment.objects.create(user=request.user, article_id=article_id, content=content, parent_id=parent_id) back_dic['msg'] = '评论成功' else: back_dic['code'] = 130 back_dic['msg'] = '请先登录' return JsonResponse(back_dic)

    相关