Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程

  这篇博客主要完成一个BBS+Blog项目,那么主要是模仿博客园的博客思路,使用Django框架进行练习。

准备:项目需求分析

  在做一个项目的时候,我们首先做的就是谈清楚项目需求,功能需求,然后才开始写,要是没有和产品经理聊清楚需求,到时候改的话就非常非常麻烦。

  那此次写项目的话,我会严格按着此次写的项目流程完成项目。那下面就是此次的项目流程。

1,项目流程

1.1,功能需求分析(和产品经理聊清楚需求)

  1,基于用户认证组件和AJAX实现登录验证(图片验证码)

  2,基于AJAX 和Forms组件实现注册功能

  3,设计系统首页(完成文章列表的渲染)

  4,设计个人站点页面

  5,文章详情页面

  6,实现一个点赞的功能

  7,实现文章的评论功能

  ——对文章的评论

  ——对评论的评论(就是子评论,反驳评论的评论)

  8,后台管理页面(后面新增文章的功能)——富文本编辑框

  9,防止XSS攻击框

1.2,设计表结构

1.3,按着每一个功能进行开发

1.4,功能测试阶段

1.5,项目部署上线(开发人员最难熬的阶段)

2,开发功能的主要设计思路

   那么下面我们要开发这个网站,而我此次是严格按照经典的软件开发所遵循的MVC设计模型。(如果不懂软件设计的MVC模式,请参考这篇博客:请点击我,后面有MVC的介绍。

  下面写的内容呢,就是我在review整个BBS+Blog项目,其实整体学完,我在这里梳理一遍,做个笔记,那么下面我的记录笔记肯定是按照Django网站开发的四件套Model(模型),URL(链接),View(视图)和Template(模板)完成的。其实这四个就对应着经典的MVC。分别是:

  • Django Model(模型):这个与经典MVC模式下的Model差不多。
  • Django URL+View(视图):这个合起来就与经典MVC下的Controller更像。原因就在于Django的URL和View合起来才能向Template传递正确的数据。用户输入提供的数据也需要Django的View来处理。
  • Django Template(模板):这个与经典MVC模式下的View一致。Django模板用来呈现Django View 传来的数据,也决定了用户界面的外观。Template里面也包含了表单,可以用来收集用户的输入。

一,Django model(模型)  ==  Model(MVC)

1,创建项目,迁移表

1.1,创建Django项目,然后建立url路径

1.2,在mysql建数据库,然后在settings中配置

import pymysql
 
pymysql.install_as_MySQLdb()


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME':'blog',           # 要连接的数据库,连接前需要创建好
        'USER':'root',        # 连接数据库的用户名
        'PASSWORD':'',        # 连接数据库的密码
        'HOST':'127.0.0.1',       # 连接主机,默认本级
        'PORT':3306            #  端口 默认3306
    }
}

 

1.3,设置时区和语言

  Django默认使用美国时间和英语,在项目的settings文件中,如下图所示:

LANGUAGE_CODE = 'en-us'
   
TIME_ZONE = 'UTC'
   
USE_I18N = True
   
USE_L10N = True
   
USE_TZ = True
      
    我们将其改为 亚洲/上海  时间和中文
  
LANGUAGE_CODE = 'zh-hans'
   
TIME_ZONE = 'Asia/Shanghai'
   
USE_I18N = True
   
USE_L10N = True
   
USE_TZ = False

  

1.4,创建模型

  这里模型表设计多表操作,不懂的可以先学习这篇博客:Django学习笔记(7):单表操作和多表操作。

1.4.1,设计表结构

  分析表结构

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第1张图片

 

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第2张图片

 

  跨表查询效率非常低。不建议使用。

  所以为了保证查询的效率,经常会牺牲增删改的效率。

1.4.2,完成表内容

  继承AbstractUser,对比继承user。

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第3张图片

 

   每个人的个人站点,可以添加个人标签,和随笔分类:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第4张图片

   一个人可以创建多个分类,一个人可以拥有多个分类,人user和分类时一对多的关系

  分类和站点的关系:一个站点blog有多个分类category,一个分类只能属于一个站点,所以站点和分类是一对多。

  站点blog 和人user是一对一的关系。(跨表查询的问题)

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第5张图片

 

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第6张图片

 

  一个博客存的最核心的数据就是文章,所以展示文章表:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第7张图片

  

   关系表,联合唯一Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第8张图片

 

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第9张图片

 

 

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第10张图片

 

1.5,迁移表

python manage.py  makemigrations

python manage.py migrate

  

2,Django URL+View  ==  Controller(MVC)

2.1  url的设计

  由于博客系统只有一个APP,所以我们这里不做分发路由。直接在根URL里面写即可。

from django.contrib import admin
from django.urls import path, re_path
from blog import views
from cnblog_review import settings
from django.views.static import serve

urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/', views.login),
    path('get_validCode_image/', views.get_validCode_image),
    re_path(r'^$', views.index),
    path('register/', views.register),
    path('logout/', views.logout),
    
    # 点赞
    path('digg/', views.digg),
    # 评论
    path('comment/', views.comment),
    # 树形评论
    path('get_comment_tree/', views.get_comment_tree),
    
    # media配置
    re_path(r'media/(?P.*)$', serve, {'document_root': settings.MEDIA_ROOT}),
    
    re_path(r'^(?P\w+)/articles/(?P\d+)$', views.article_detail),
    
    # 后台管理url
    re_path(r'cn_backend/$', views.cn_backend),
    re_path(r'cn_backend/add_article/$', views.add_article),
    
    # 关于个人站点的URL
    re_path(r'^(?P\w+)/$', views.home_site),
    
    # 关于个人站点的跳转
    re_path(r'^(?P\w+)/(?Ptag|category|archive)/(?P.*/$)', views.home_site),

]

  

2.2  登录页面的设计 

  在登录页面设计之前,我们可以参考我这两篇博客:

Django学习笔记(9)——开发用户注册与登录系统

Django学习笔记(16)——扩展Django自带User模型,实现用户注册与登录

 下面我就不多解释,直接完成登录页面。代码如下:

  views.py

def login(request):
    '''
    登录视图函数:
        get请求响应页面
        post(Ajax)请求响应字典
    :param request:
    :return:
    '''
    if request.method == 'POST':

        response = {'user': None, 'msg': None}
        user = request.POST.get('user')
        pwd = request.POST.get('pwd')
        valid_code = request.POST.get('valid_code')

        valid_code_str = request.session.get('valid_code_str')
        if valid_code.upper() == valid_code_str.upper():
            user = auth.authenticate(username=user, password=pwd)
            if user:
                # request.user == 当前登录对象
                auth.login(request, user)
                response['user'] = user.username
            else:
                response['msg'] = '用户名或者密码错误!'
        else:
            # 校验失败了
            response['msg'] = 'valid code error!'

        return JsonResponse(response)

    return render(request, 'login.html')

  login.html




    
    Title
    




登录页面

{% csrf_token %}
注册

  结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第11张图片

 

 

2.3  基于forms组件的注册页面设计

  在注册页面设计之前,我们要学习验证码的代码。

参考这篇博客:Django学习笔记(17)——验证码功能的实现

2.3.1 注册页面总体代码展示

   views.py

def get_validCode_image(request):
    """
    基于PIL模块动态生成响应状态码图片
    :param request:
    :return:
    """
    data = get_valid_code_img(request)
    return HttpResponse(data)


def index(request):
    """
    系统首页
    :param request:
    :return:
    """
    article_list = models.Article.objects.all()

    return render(request, 'index.html', locals())


def logout(request):
    """
    注销视图
    :param request:
    :return:
    """
    auth.logout(request)
    # 等同于执行了  request.session.fulsh()
    return redirect('/login/')


def register(request):
    """
    注册视图函数:
       get请求响应注册页面
       post(Ajax)请求,校验字段,响应字典
    :param request:
    :return:
    """
    if request.is_ajax():
        print(request.POST)
        form = UserForm(request.POST)

        response = {'user': None, 'msg': None}
        if form.is_valid():
            response['user'] = form.cleaned_data.get('user')

            #  生成一条用户记录
            user = form.cleaned_data.get('user')
            pwd = form.cleaned_data.get('pwd')
            email = form.cleaned_data.get('email')
            avatar_obj = request.FILES.get('avatar')
            
            extra = {}
            if avatar_obj:
                extra['avatar'] = avatar_obj
            # 要是逻辑没有用到值,我们可以不用赋值,等用到的时候,则添加
            UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj, **extra)


        else:
            print(form.cleaned_data)
            print(form.errors)
            response['msg'] = form.errors

        return JsonResponse(response)

    form = UserForm()
    return render(request, 'register.html', locals())

  register.html




    
    Title
    
    
    




注册页面

{% csrf_token %} {% for field in form %}
{{ field }}
{% endfor %}

  index.html




    
    Title
    
    
    
    






Panel heading without title
Panel content
Panel heading without title
Panel content
Panel heading without title
Panel content
{% for article in article_list %}
{{ article.title }}
{{ article.desc }}
{{ article.user.username }}     发布于  {{ article.create_time|date:'Y-m-d:H:i' }}    评论({{ article.comment_count }})   点赞({{ article.up_count }})

{% endfor %}
Panel heading without title
Panel content
Panel heading without title
Panel content

  

  结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第12张图片

 2.3.2  头像的设置

 点击头像===点击input(这里使用label标签属性方法)

  首先,我们下载一个默认头像:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第13张图片

 

   注册头像的预览方法

   1,获取用户选中的问卷对象

   2,获取文件对象的路径

   3,修改img的src,src=文件路径对象

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第14张图片

 

  取用户的标签,基于AJAX提交formdata数据

// 基于AJAX 提交数据
    $(".reg_btn").click(function () {

        var formdata = new FormData();
        formdata.append('user', $("#id_user").val());
        formdata.append('pwd', $("#id_pwd").val());
        formdata.append('re_pwd', $("#id_re_pwd").val());
        formdata.append('email', $("#id_email").val());
        formdata.append('avatar', $("#avatar")[0].files[0]);
        formdata.append('csrfmiddlewaretoken', $('[name= "csrfmiddlewaretoken"]').val());

        $.ajax({
            url:"",
            type:"post",
            data: formdata,
            processData:false,
            contentType:false,
            success:function (data) {
                console.log(data)
            }
        })
    })

  效果:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第15张图片

 

2.3.3  AJAX在注册页面显示错误信息

  views: form.errors 

  Ajax.success方法  data.msg 就是上面的errors




    
    Title

    
    



注册页面

{% csrf_token %} {% for field in form %}
{# #} {{ field }}
{% endfor %}

    结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第16张图片

   当我们用户输入后,需要清空用户名的错误信息。

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第17张图片

 

2.4  使用Admin 去录入数据

  (关于Django admin的详细内容,我们后面补充)

  这里我们基于admin 去录入文章数据。

  为了让admin界面管理我们的数据模型,我们需要先注册数据模型到admin。所以我们去 admin.py 中注册模型,代码如下:

from django.contrib import admin

# Register your models here.
from blog import models

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

  注册后,我们需要通过下面命令来创建超级用户:

python manage.py createsuperuser

  然后登陆Django的后台(http://127.0.0.1:8000/admin/),我们输入超级用户,进去如下:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第18张图片

  然后我们就可以录入数据了。

 

2.5 个人站点页面的设计

1.我的标签,随机分类,标签列表
    随机分类:     /username/category/
    我的标签:     /username/tag/
    随笔归档:     /username/archive/
   
​
2.模板继承
    {% extends 'base.html' %}
​
    {% block content %}
    {% endblock content%}}
​
3.自定义标签
    /blog/templatetags/my_tag.py
​
    @register.inclusion_tag('classification.html')
    def get_classification_style(username):
        ...
        return {} # 去渲染 menu.html
​
4.分组查询 .annotate() / extra()应用
    多表分组
        tag_list = Tag.objects.filter(blog=blog).annotate(
            count = Count('article')).values_list('title', 'count')
​
    单表分组 / DATE_FORMAT() /  extra()
        date_list = Article.objects.filter(user=user).extra(
            select={"create_ym": "DATE_FORMAT(create_time,'%%Y-%%m')"}).values('create_ym').annotate(
            c = Count('nid')).values_list('create_ym', 'c')
​
5. 时间、区域配置
     TIME_ZONE = 'Asia/Shanghai'
     USE_TZ = False

2.5.1  个人站点设计的总体代码展示

  views.py

def home_site(request, username, **kwargs):
    '''
    个人站点视图函数
    :param request:
    :return:
    '''
    print("执行的是home_site的内容")
    print('username:', username)
    user = UserInfo.objects.filter(username=username).first()
    #  判断用户是否存在
    if not user:
        return render(request, 'not_found.html')

    # 当用户存在的话 当前用户或者当前站点对应所有文章取出来
    # 1, 查询当前站点
    blog = user.blog

    # kwargs是为了区分访问的是站点页面还是站点下的跳转页面
    article_list = models.Article.objects.filter(user=user)

    if kwargs:
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        print(condition)
        print(param)
        if condition == 'category':
            print(1)
            article_list = article_list.filter(category__title=param)
        elif condition == 'tag':
            print(2)
            article_list = article_list.filter(tags__title=param)
        else:
            print(3)
            year, month, day = param.split("-")
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

    return render(request, 'home_site.html', {'username': username, 'blog': blog, 'article_list': article_list, })

  home_site.html

{% extends 'base.html' %}


{% block content %}
 
{% for article in article_list %}
{{ article.title }}
{{ article.desc }}
发布于   {{ article.create_time|date:"Y-m-d H:i" }}   评论({{ article.comment_count }})   点赞({{ article.up_count }})  

{% endfor %}
{% endblock %}

   结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第19张图片

 

2.5.2  个人站点页面的文章查询

  当博客园用户站点不存在的时候,我们发现,会返回一个下面页面:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第20张图片

  当然,我们也可以做与上面一样的页面,其代码入下:

  not_found.html




    
    Error_404_资源不存在

    

    




404. 抱歉!您访问的资源不存在!

请确认您输入的网址是否正确,如果问题持续存在,请发邮件至 [email protected] 与我们联系。

返回百度查询

  

2.5.3  个人站点页面的日期查询

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第21张图片

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第22张图片

  如何只拿出来 年和月?

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第23张图片

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第24张图片

 2.5.4  Extra函数的学习

   Django对一些复杂的函数不能一一对应,所以提供了一种extra函数。Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第25张图片

 Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第26张图片

 

2.5.5  跳转过滤功能的实现

  views.py (home_site函数)

    article_list = models.Article.objects.filter(user=user)
    if kwargs:
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        print(condition)
        print(param)
        if condition == 'category':
            print(1)
            article_list = article_list.filter(category__title=param)
        elif condition == 'tag':
            print(2)
            article_list = article_list.filter(tags__title=param)
        else:
            print(3)
            year, month, day = param.split("-")
            article_list = article_list.filter(create_time__year=year, create_time__month=month)

  home_site.html

我的标签
{% for tag in tag_list %}

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

{% endfor %}
随笔分类
{% for cate in cate_list %}

{{ cate.0 }}({{ cate.1 }})

{% endfor %}
随笔归档
{% for data in data_list %}

{{ data.0 }}({{ data.1 }})

{% endfor %}

 

 

2.6  文章详细页的设计

1.文章详情页的设计

2.文章详情页的数据构建

3.文章详情页点赞样式的完成(基本仿照博客园)

4.文章评论样式的添加(基本仿照博客园)

5.文章评论树的添加(支持对对评论的评论)

6.文章评论中邮件发送

2.6.1 总体的代码及其样式展示

  views.py

def get_classification_data(username):
    user = UserInfo.objects.filter(username=username).first()
    blog = user.blog

    cate_list = models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count("article__title")).values_list(
        "title", "c")

    tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", 'c')

    data_list = models.Article.objects.filter(user=user).extra(
        select={"y_m_date": "date_format(create_time, '%%Y-%%m-%%d')"}).values(
        'y_m_date').annotate(c=Count('nid')).values_list('y_m_date', 'c')

    return {"blog": blog, 'cate_list': cate_list, 'tag_list': tag_list, 'data_list': data_list}


def article_detail(request, username, article_id):
    print("执行的是article_detail的内容")

    user = UserInfo.objects.filter(username=username).first()
    blog = user.blog

    article_obj = models.Article.objects.filter(pk=article_id).first()

    comment_list = models.Comment.objects.filter(article_id=article_id)

    return render(request, 'article_detail.html', locals())

  article_detail.html

{% extends "base.html" %}


{% block content %}
    {% csrf_token %}
    
{% endblock %}

  结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第27张图片

 

 2.6.2  点赞,评论,评论树及其发送邮件

 

根评论:对文章的评论

子评论:对评论的评论

区别:是否有父评论

评论:   1.构建样式

    2.提交根评论

    3.显示跟评论——render显示   ——AJAX显示

    4.提交子评论

    5.显示子评论——render显示   ——AJAX显示

    6.评论树的显示

 

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第28张图片

  其代码展示

def digg(request):
    """
    点赞功能
    :param request:
    :return:
    """
    print(request.POST)

    article_id = request.POST.get("article_id")
    is_up = json.loads(request.POST.get("is_up"))  # "true"
    # 点赞人即当前登录人
    user_id = request.user.pk
    obj = models.ArticleUpDown.objects.filter(user_id=user_id, article_id=article_id).first()

    response = {"state": True}
    if not obj:
        ard = models.ArticleUpDown.objects.create(user_id=user_id, article_id=article_id, is_up=is_up)

        queryset = models.Article.objects.filter(pk=article_id)
        if is_up:
            queryset.update(up_count=F("up_count") + 1)
        else:
            queryset.update(down_count=F("down_count") + 1)
    else:
        response["state"] = False
        response["handled"] = obj.is_up

    return JsonResponse(response)


def comment(request):
    """
    提交评论视图函数
    功能:
    1 保存评论
    2 创建事务
    3 发送邮件
    :param request:
    :return:
    """
    print(request.POST)

    article_id = request.POST.get("article_id")
    pid = request.POST.get("pid")
    content = request.POST.get("content")
    user_id = request.user.pk

    article_obj = models.Article.objects.filter(pk=article_id).first()

    # 事务操作
    with transaction.atomic():
        comment_obj = models.Comment.objects.create(user_id=user_id, article_id=article_id, content=content,
                                                    parent_comment_id=pid)
        models.Article.objects.filter(pk=article_id).update(comment_count=F("comment_count") + 1)

    response = {}

    response["create_time"] = comment_obj.create_time.strftime("%Y-%m-%d %X")
    response["username"] = request.user.username
    response["content"] = content

    # 发送邮件

    from django.core.mail import send_mail
    from cnblog import settings

    # send_mail(
    #     "您的文章%s新增了一条评论内容"%article_obj.title,
    #     content,
    #     settings.EMAIL_HOST_USER,
    #     ["[email protected]"]
    # )
    ...
    import threading

    t = threading.Thread(target=send_mail, args=("您的文章%s新增了一条评论内容" % article_obj.title,
                                                 content,
                                                 settings.EMAIL_HOST_USER,
                                                 ["[email protected]"])
                         )
    t.start()
   ...
    return JsonResponse(response)


def get_comment_tree(request):
    article_id = request.GET.get("article_id")
    response = list(models.Comment.objects.filter(article_id=article_id).order_by("pk").values("pk", "content",
                                                                                               "parent_comment_id"))

    return JsonResponse(response, safe=False)

  点赞的jQuery代码展示:

$("#div_digg .action").click(function () {
              var is_up = $(this).hasClass("diggit");

                $obj = $(this).children('span');
                $.ajax({
                    url: '/digg/',
                    type: 'post',
                    data: {
                        "csrfmiddlewaretoken": $("[name='csrfmiddlewaretoken']").val(),
                        "is_up": is_up,
                        "article_id": "{{ article_obj.pk }}",

                    },
                    success:function (data) {
                        //alert(is_up);
                        console.log(data);
                        if (data.state){
                            var val = parseInt($obj.text());
                            $obj.text(val+1);
                            {#if (is_up){#}
                            {#    var val=parseInt($("#digg_count").text());#}
                            {#    $("#digg_count").text(val+1);#}
                            //}
                            {#else{#}
                            {#    var val=parseInt($("#bury_count").text());#}
                            {#    $("#bury_count").text(val+1);#}
                            //}
                        }else {
                            var val = data.handled?"您已经推荐过!":"您已经反对过!";
                            $("#digg_tips").html(val);
                            {#if (data.handled){#}
                            {#    $("#digg_tips").html("您已经推荐过!")#}
                            //}else {
                            {#    $("#digg_tips").html("您已经反对过!")#}
                            //}

                            setTimeout(function () {
                                $("#digg_tips").html()
                            }, 2000)

                        }
                    }
                })
            });

  

  结果展示

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第29张图片

 

 2.7  后台管理页面设计

1.支持文章编辑

2.支持富文本编辑器(支持渲染已有文章,并支持文本编辑器的上传功能)

3.支持删除文章(未添加,很简单,可自行添加)

4.防止Xss攻击(基于BS4)

    views.py

@login_required
def cn_backend(request):
    article_list = models.Article.objects.filter(user=request.user)

    return render(request, 'backend/backend.html', locals())


from bs4 import BeautifulSoup


@login_required
def add_article(request):
    if request.method == 'POST':
        title = request.POST.get("title")
        content = request.POST.get("content")

        # 防止XSS攻击,过滤script
        soup = BeautifulSoup(content, "html.parser")
        for tag in soup.find_all():

            print(tag.name)
            if tag.name == 'script':
                tag.decompose()

        # 构建摘要数据,获取标签字符串的文本前150个符号
        desc = soup.text[0:150] + "..."
        models.Article.objects.create(title=title, desc=desc, content=str(soup), user=request.user)
        return redirect('/cn_backend/')
    return render(request, "backend/add_article.html")


def upload(request):
    '''
        编辑器上传文件接收视图函数
        :param request:
        :return:
        '''
    print(request.FILES)
    img_obj = request.FILES.get('upload_img')
    print(img_obj.name)

    path = os.path.join(settings.MEDIA_ROOT, 'add_article_img', img_obj.name)

    with open(path, 'wb') as f:
        for line in img_obj:
            f.write(line)

    response = {
        'error': 0,
        'url': '/media/add_article_img/%s' % img_obj.name
    }
    import json
    return HttpResponse(json.dumps(response))

  add_articles.html

{% extends 'backend/base.html' %}

{% block content %}

    
{% csrf_token %}
添加文章
{% endblock %}

  结果展示:

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第30张图片

  添加文章

Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程_第31张图片

 

3,Django Template  ==  View(MVC)

   Django的模板与经典MVC模式下的View一致。Django模板用来呈现Django View传来的数据,也决定了用户界面的外观。Template里面也包含了表单,可以用来收集用户的输入。

  那这部分内容,我决定单独写一篇博客记录内容。请参考:

 

 

4,代码优化

4.1  优化思路

  1,导入包的时候,需要先导入python标准库的包,再导入第三方插件的包,最后导入我们自己定义的包。

  2,冗余代码可以优化的话,自己优化

  3,开发中所有的 print得去掉,我们测试的时候可以加。

           # if avatar_obj:
            #     user_obj = UserInfo.objects.create_user(username=user, password=pwd, 
email=email, avatar=avatar_obj)
            # else:
            #     user_obj = UserInfo.objects.create_user(username=user, password=pwd, 
email=email)


            # 代码优化
            extra = {}
            if avatar_obj:
                extra['avatar'] = avatar_obj
            # 要是逻辑没有用到值,我们可以不用赋值,等用到的时候,则添加
            UserInfo.objects.create_user(username=user, password=pwd, email=email, avatar=avatar_obj, **extra)

  

4.2 注意问题

  注意问题1:由于我们使用django自带的AbstractUser扩展之后,需要进行更改AUTH_USER_MODEL的配置。

  我们在settings.py中设置:

AUTH_USER_MODEL = 'blog.UserInfo'

  AUTH_USER_MODEL是等于APP blog下面的UserInfo表。因为UserInfo表集成的是自带的AbstractUser表。

  然后进行迁移。

   注意问题2:对于最新版的Django2.0,在使用一对一(OneToOneField)和外键(ForeignKey)时,需要加上on_delete 参数,不然就会报错。

on_delete=models.CASCADE,    
# 删除关联数据,与之关联也删除

  如果直接执行上述代码,遇到的报错如下:

TypeError: __init__() missing 1 required positional argument: 'on_delete'

  因为 on_delete 在最新版的Django中已经是位置参数了。

4.3  使用inclution_tag 优化代码

  base.html

 

  my_tags.py

from django import template

from blog import models
from django.db.models import Count
register = template.Library()

@register.simple_tag
def multi_tag(x, y):
    return x*y

@register.inclusion_tag('classification.html')
def get_classification_style(username):
    user = models.UserInfo.objects.filter(username=username).first()
    blog = user.blog

    cate_list = models.Category.objects.filter(blog=blog).values('pk').annotate(c=Count("article__title")).values_list(
        "title", "c")

    tag_list = models.Tag.objects.filter(blog=blog).values("pk").annotate(c=Count("article")).values_list("title", 'c')

    data_list = models.Article.objects.filter(user=user).extra(
        select={"y_m_date": "date_format(create_time, '%%Y-%%m-%%d')"}).values(
        'y_m_date').annotate(c=Count('nid')).values_list('y_m_date', 'c')

    return {"blog": blog, 'cate_list': cate_list, 'tag_list': tag_list, 'data_list': data_list}

 

 

4.4  头像设置的代码优化

  取用户的标签,基于AJAX提交formdata数据

// 基于AJAX 提交数据
    $(".reg_btn").click(function () {

        var formdata = new FormData();
        formdata.append('user', $("#id_user").val());
        formdata.append('pwd', $("#id_pwd").val());
        formdata.append('re_pwd', $("#id_re_pwd").val());
        formdata.append('email', $("#id_email").val());
        formdata.append('avatar', $("#avatar")[0].files[0]);
        formdata.append('csrfmiddlewaretoken', $('[name= "csrfmiddlewaretoken"]').val());

        $.ajax({
            url:"",
            type:"post",
            data: formdata,
            processData:false,
            contentType:false,
            success:function (data) {
                console.log(data)
            }
        })
    })

  代码优化:

  // 基于AJAX 提交数据
    $(".reg_btn").click(function () {
        console.log($("#form").serializeArray());

        var request_data = $("#form").serializeArray();
        $.each(request_data, function (index, data) {
           formdata.append(data.name, data.value)
        });

        formdata.append('avatar', $("#avatar")[0].files[0]);

        $.ajax({
            url:"",
            type:"post",
            data: formdata,
            processData:false,
            contentType:false,
            success:function (data) {
                console.log(data)
            }
        })
    })

  

 4.5  admin的代码优化

  我们查看之前写的admin.py的代码:

from django.contrib import admin

# Register your models here.
from blog import models

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

  那如果要是存在100个表,我们需要写100个注册吗?当然不可能,为了简化代码,我们这样写。

  首先在model.py中,把所有的表名称写入一个列表中,如下:

from django.db import models

# Create your models here.
__all__ = ['UserInfo', 'Blog', 'Category', 'Tag', 'Article', 'Article2Tag', 
       'ArticleUpDown', 'Comment']

  然后我们在admin.py中使用一个for循环进行注册。代码如下:

from django.contrib import admin

# Register your models here.
from blog import models

for table in models.__all__:
    admin.site.register(getattr(models, table))

  这样我们就可以节省代码。

 

 

 

你可能感兴趣的:(Django学习笔记(18)——BBS+Blog项目开发(2)主体思路及流程)