09新闻列表页

新闻列表页功能

分析

  • 业务流程处理
    • 判断前端传的标签分类id是否为空,是否为整数,是否超过范围
    • 判断前端传的当前文章页数是否为空,是否为整数,是否超过范围
  • 请求方法:get
  • url定义:/news/
  • 请求参数:url路径参数
参数 类型 前端是否必须传 描述
tag_id 整数 标签分类id
page 整数 当前文章页数
  • 向前端返回的数据格式为json格式,返回实例如下:
{
    "data": {
        "total_pages": 61,
        "news": [
            {
                "digest": "在python用import或者from...import或者from...import...as...来导入相应的模块,作用和使用方法与C语言的include头文件类似。其实就是引入...",
                "title": "import方法引入模块详解",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python基础",
                "update_time": "2018年12月17日 14:48"
            },
            {
                "digest": "如果你原来是一个php程序员,你对于php函数非常了解(PS:站长原来就是一个php程序员),但是现在由于工作或者其他原因要学习python,但是p...",
                "title": "给曾经是phper的程序员推荐个学习网站",
                "author": "python",
                "image_url": "/media/jichujiaochen.jpeg",
                "tag_name": "Python基础",
                "update_time": "2018年12月17日 14:48"
            }
        ]
    },
    "errno": "0",
    "errmsg": ""
}

分页

对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
select_related 返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询

settings.py

  • 在项目根目录下创建一个media文件夹,用于存放新闻图片以及用户上传的文件
  • 在settings.py文件中添加下面配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media')

urls.py

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path('',include('apps.news.urls')),
    path('users/',include('apps.users.urls')),
    path('doc/',include('apps.doc.urls')),
    path('course/',include('apps.course.urls')),
    path('',include('apps.verifications.urls')),

]+static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

在这里插入图片描述

  • 添加测试数据
    mysql -u 用户名 -p -D 数据库名 < 文件名
    mysql -uroot -p -D web_prv < tb_news_20181217.sql

apps/news/views.py

import logging

from django.shortcuts import render
from django.views import View
from django.core.paginator import Paginator,EmptyPage   #不存在的页码报错

from apps.news import models
from apps.news import constants

from utils.json_fun import to_json_data
logger = logging.getLogger('django')

def index(request):
    return render(request,'news/index.html')


class IndexView(View):
    """

    """
    def get(self,request):
        # tags = models.Tag.objects.filter(is_delete=False)
        #通过表中的is_delete判断是否被删除
        tags = models.Tag.objects.only('id','name').filter(is_delete=False) #only确定要查询的字段,其他的不查
        # context = {
        #     'tags':tags,
        # }
        #将当前方法下的变量都传进locals,python内置函数
        # return render(request,'news/index.html',context=context)
        return render(request,'news/index.html',locals())

#ajax请求
#传参 tag_id  page
#后台的返回  图片,标题,摘要,作者,标签,更新时间,文章id
#请求方式:GET
#/news/
#url查询字符串   ?tag_id=1&page=2    /news/?tag_id=1&page=2

class NewsListView(View):
    """
    对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
    select_related 返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。
    它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询

    """
    def get(self,request):
        """
        # 1.获取参数
        # 2.校验参数
        # 3.从数据库拿数据
        # 4.分页
        # 5.序列化输出
        #6.返回给前端
        :param request:
        :return:
        """
        #1.获取参数,校验参数
        #如果通过不正当手段导致int转换不成功时,返回最新资讯给前端
        try:
            # 如果没传或者格式不对得不到数据,为0,则把最新资讯id设置为0,当刚进入首页没进行选择标签时起作用,即默认为最新资讯
            tag_id = int(request.GET.get('tag_id',0))   #get到的是str类型,存入数据库是int类型
        except Exception as e:
            logger.error('标签错误:\n{}'.format(e))
            tag_id = 0
        try:
            page = int(request.GET.get('page',1))
        except Exception as e:
            logger.error('页码错误:\n{}'.format(e))
            page = 1
        #3.从数据库中拿数据
        #title,digest(摘要),image_url,update_time
        #select_related用于多表查询,是优化措施,参数是关联外键的字段,id为主键,无论怎样都会查到,所以不需要列出来
        #News中的tag字段对应Tag标签中的name字段,News中的author字段对应Users中的username字段
        news_queryset = models.News.objects.select_related('tag','author').only('title','digest',\
                        'image_url','update_time','tag__name','author__username')
        #is_delete:是否被逻辑删除,tag_id=0在queryset中表示为空
        news = news_queryset.filter(is_delete=False,tag_id=tag_id) or news_queryset.filter(is_delete=False)
        #4.分页
        paginator = Paginator(news,constants.PER_PAGE_NEWS_COUNT)   #第一个参数是数据,第二个参数是每页的新闻个数
        try:
            news_info = paginator.page(page)    #对应页的新闻
        except Exception as e:
            logger.error('用户访问页数大于总页数')
            news_info = paginator.page(paginator.num_pages) #展示最大页码的新闻
        #5.序列化输出
        news_info_list = []
        for n in news_info:
            news_info_list.append({
                'id':n.id,
                'title':n.title,
                'digest':n.digest,
                'image_url':n.image_url,
                'update_time':n.update_time.strftime('%Y-%m-%d %H:%M:%S'),
                'tag_name':n.tag.name,  #tag的name属性
                'author':n.author.username,
            })
        data = {
            'news':news_info_list,
            'total_page':paginator.num_pages    #总页码数
        }
        #6.返回给前端
        return to_json_data(data=data)

apps/news/urls.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
  __title__ = ''
  __author__ = 'xiaoge'
  __mtime__ = '2019/5/10'
  # code is far away from bugs with the god animal protecting
      I love animals. They taste delicious.
               ┏┓      ┏┓
              ┏┛┻━━━━━━┛┻┓
              ┃        ☃ ┃
              ┃  ┳┛ ┗┳   ┃
              ┃    ┻     ┃
              ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃ 神兽保佑  ┣┓
                ┃永无BUG!┏┛
                ┗┓┓┏━┳┓┏┛
                ┃┫┫  ┃┫┫
               ┗┻┛  ┗┻┛
  """
from django.urls import path, re_path


from apps.news import views

app_name = 'news'

urlpatterns = [
    # path('',views.index,name='index'),
    path('',views.IndexView.as_view(),name='index'),
    path('news/',views.NewsListView.as_view(),name='news_list')
]

报错

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`web_prv`.`tb_news`, CONSTRAINT `tb_news_author_id_4af218b2_fk_tb_users_id` FOREIGN KEY (`author_id`) REFERENCES `tb_users` (`id`))

在这里插入图片描述

  • 原因:我的Users表中的username的id分别是5和7(因为之前删过几个用户,id为1,2,3,4的让我删了),author是News外键关联Users,tag是外键关联Tag,下图是News的字段顺序,所以 author_id 应该和 Users 的 id 一样,改为下面这样就 ok 了
INSERT INTO `tb_news` VALUES (1, '2018-12-17 14:48:21.373325', '2018-12-17 14:48:21.373341', 0, 'Python 中__new__方法详解及使用', '__new__ 的作用在Python中__new__方法与__init__方法类似,但是如果两个都存在那么__new__闲执行。在基础类object中,__new__被定义成了一   ', '<div class=\"content\">\n                <h2>__new__ 的作用</h2><p>在Python中__new__方法与__init__方法类似,但是如果两个都存在那么__new__闲执行。</p><p>在基础类object中,__new__被定义成了一个静态方法,并且需要传递一个参数cls。Cls表示需要实例化的类,此参数在实例化时 由Python解析器自动提供。</p><p>new()是在新式类中新出现的方法,它作用在构造方法init()建造实例之前,可以这么理解,在Python 中存在于类里面的构造方法init()负责将类的实例化,而在init()调用之前,new()决定是否要使用该init()方法,因为new()可以调用其他类的构造方法或者直接返回别 的对象来作为本类 的实例。 </p><h3>new()方法的特性: </h3><p>new()方法是在类准备将自身实例化时调用。 </p><p>new()方法始终都是类的静态方 法,即使没有被加上静态方法装饰器。</p><h2>实例</h2><pre class=\"brush:python;toolbar:false\">class Person(object):\r\n \r\n    def __init__(self, name, age):\r\n        self.name = name\r\n        self.age = age\r\n    \r\n    def __new__(cls, name, age):\r\n        if 0 &lt; age &lt; 150:\r\n            return object.__new__(cls)\r\n            # return super(Person, cls).__new__(cls)\r\n        else:\r\n            return None\r\n \r\n    def __str__(self):\r\n        return \'{0}({1})\'.format(self.__class__.__name__, self.__dict__)\r\n \r\nprint(Person(\'Tom\', 10))\r\nprint(Person(\'Mike\', 200))</pre><p>结果:</p><pre class=\"brush:bash;toolbar:false\">Person({\'age\': 10, \'name\': \'Tom\'})\r\nNone</pre><h2>Python3和 Python2中__new__使用不同</h2><h3>Python2的写法</h3><p>注意 Python 版本大于 等于2.7才支持</p><pre class=\"brush:python;toolbar:false\">class Singleton(object):\r\n    def __new__(cls,*args, **kwargs):\r\n        if not hasattr(cls,\'_inst\'):\r\n            print(cls)\r\n            cls._inst = super(Singleton, cls).__new__(cls,*args,**kwargs)\r\n        return cls._inst</pre><p><br></p><h3>Python3的写法</h3><pre class=\"brush:python;toolbar:false\">class Singleton(object):\r\n    def __new__(cls,*args, **kwargs):\r\n        if not hasattr(cls,\'_inst\'):\r\n            print(cls)\r\n            cls._inst = super(Singleton, cls).__new__(cls)\r\n        return cls._inst</pre><p>如果Python3的写法跟Python2写法一样,那么倒数第二行会报错\"TypeError: object() takes no parameters\"</p><p><br></p>            </div>', 641, '/media/jichujiaochen.jpeg', 5, 1);
  • News 的表
    在这里插入图片描述
  • Users 的表
    在这里插入图片描述
  • Tag 的表
    在这里插入图片描述

csrf

  • csrf_token是随机生成的,每次都变
  • django.middleware.csrf.CsrfViewMiddleware:django自带的中间键,客户端用户发送POST,服务器端会在cookie中设置csrf_token,客户端发送的请求需要携带上csrf_token,用于验证,html的form表单中也要加上{% csrf_token %}
    在这里插入图片描述
  • 也可以在后台设置,比如想要在登录视图中加入
from django.views.decorators.csrf import ensure_csrf_cookie
from django.utils.decorators import method_decorator
class LoginView(View):
    """
    /users/login/
    """
    # 类装饰器
    @method_decorator(ensure_csrf_cookie) #在post请求时就携带上csrf_token,所以在post之前设置
    def get(self,request):
        return render(request,'users/login.html')

    def post(self,request):
        #1.获取参数
        json_data = request.body    #获取的是bytes格式
        if not json_data:
            return to_json_data(errno=Code.PARAMERR,errmsg=error_map[Code.PARAMERR])

        dict_data = json.loads(json_data.decode('utf8'))    #或者'utf-8'  将bytes格式解码成字符串,再转成json格式

        #2.校验:账号,账号格式,是否为空,账号和密码,数据库
        form = LoginForm(data=dict_data,request=request)    #类的实例化

        #3.返回给前端
        if form.is_valid():
            return to_json_data(errmsg='恭喜您!登陆成功!')
        else:
            #定义一个错误信息列表
            err_msg_list = []
            for item in form.errors.get_json_data().values():
                err_msg_list.append(item[0].get('message'))

            err_msg_str = '/'.join(err_msg_list)    #拼接错误信息为一个字符串
            return to_json_data(errno=Code.PARAMERR,errmsg=err_msg_str)
#!/home/xiaoge/env python3.6
# -*- coding: utf-8 -*-
"""
  __title__ = ' MyMiddleware'
  __author__ = 'xiaoge'
  __mtime__ = '2019/6/7 下午9:21'
  # code is far away from bugs with the god animal protecting
      I love animals. They taste delicious.
               ┏┓      ┏┓
              ┏┛┻━━━━━━┛┻┓
              ┃        ☃ ┃
              ┃  ┳┛ ┗┳   ┃
              ┃    ┻     ┃
              ┗━┓      ┏━┛
                ┃      ┗━━━┓
                ┃ 神兽保佑  ┣┓
                ┃永无BUG!┏┛
                ┗┓┓┏━┳┓┏┛
                ┃┫┫  ┃┫┫
               ┗┻┛  ┗┻┛
"""
from django.utils.deprecation import MiddlewareMixin    #用于继承的中间键的基类
from django.middleware.csrf import get_token


class MyMiddleware(MiddlewareMixin):
    """

    """
    def process_request(self,request):
        get_token(request)
  • 在settings.py中设置自定义的中间键
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'utils.MyMiddleware.MyMiddleware'	#加入自定义的中间键
]

猜你喜欢

转载自blog.csdn.net/xiaogeldx/article/details/90766106