August Rush

一个还在努力成长的小火汁!

游龙当归海,海不迎我自来也。

We create our own demons.

You can reach me at augustrush0923@gmail.com
Django 通用类视图详解
发布:2020年03月28日 | 作者:augustrush | 阅读量: 2013

View视图

django.views.generic.base.View是主要的类视图,所有的类视图都是继承自他。如果我们写自己的类视图,也可以继承自他。然后再根据当前请求的method,来实现不同的方法。

例如:这个视图只允许get请求方式,那么就可以在这个类中定义get(self,request,*args,**kwargs)方法。

from django.http import HttpResponse
from django.views.generic import View

class InfoListView(View):
    def get(self, request, *args, **kwargs):
        return HttpResponse('Info list View')

当要访问这个类视图时便只能用get请求方式来访问了。 除了get,post方法,View还支持一下方法 ['get','post','put','patch','delete','head','options','trace']

如果用户访问了View中没有定义的方法。如你的类视图支持get方法,却出现了post请求,那么View会吧这个请求转发给http_method_not_allowed. 栗子:

class InfoDetaialView(View):
    def post(self, request, id_card):
        return HttpResponse('编号%s的信息' % id_card)
    def http_method_not_allowed(self, request, *args, **kwargs):
        return HttpResponse("当前使用的是%s请求,但是不支持此请求" % request.method)

当用户以post以外的请求方式请求时,就会返回http_method_not_allowed方法中返回的东西。

其实不管是get请求还是post请求,都会走django.views.generic.base.Viewdispatch(request,*args,**kwargs)方法,所以如果实现这个方法,将能够对所有请求都处理到。

TemplateView视图

django.views.generic.base.TemplateView,这个类视图是专门用来返回模版的。在这个类中,有两个属性是经常需要用到的,一个是template_name,这个属性是用来存储模版的路径,TemplateView会自动的渲染这个变量指向的模版。另外一个是get_context_data,这个方法是用来返回上下文数据的,也就是在给模版传的参数的

栗子: 新建一个aboutMe.html的文件,

<body>
    This is about me page.
</body>

然后直接在urls中渲染这个模板:

from django.views.generic import TemplateView

# 如果渲染的模板不需要传递任何参数,那么建议在urls中使用TemplateView直接进行渲染
path('about/',TemplateView.as_view(template_name='aboutMe.html')),

而我们又想使用这个TemplateView进行渲染模板,又想传递少许参数,这个时候我们就可以定义一个类,继承至TemplateView

views.py中新建一个类继承至TemplateView

from django.views.generic import TemplateView

class AboutMeView(TemplateView):
    template_name = 'aboutMe.html'

    def get_context_data(self, *args, **kwargs):
        context = super(AboutMeView, self).get_context_data()
        context.update({
            'name': 'AugustRush',
            'age': '18', 
        })

我们也需要在aboutMe.html文件中接收传入的数据

<body>
    这是关于我们的页面
    name = {{ name }}
    age = {{ age }}
</body>

然后将上面的映射注释掉,添加新的映射

path('about/',views.AboutView.as_view()),

这里我们就传递了两个参数nameage过去。

ListView视图

在网站开发中,经常会出现需要列出某个表中的一些数据作为列表展示出来。比如文章列表,图书列表等等。在Django中可以使用ListView来帮我们快速实现这种需求。

栗子:

首先添加url映射

path('list/',views.InfoListView.as_view()),

然后我们新建一个list.html的文件,写入代码:

<ul>
    {% for article in articles %}
        <li>{{ article.title }}</li>
    {% endfor %}
</ul>

接下来我们新建一个类视图:

class InfoListView(ListView):
    model = models.Article  # 重写model类属性,指定模型的列表
    template_name = 'list.html'     # 指定这个列表的模板。
    context_object_name = 'articles'    # 指定这个列表模型在模板中的参数名称。
    paginate_by = 10    # 每一页需要展示多少条数据
    ordering = 'create_time'   # 以时间进行排序展示

输入网址,就能查看效果了,并且当前页面只有10条数据。 而如果我们向访问后面的页面,我们只需要在输入的网址后面以GET的方式添加一个page=x的参数就行了

例如:我们的网址是

http://127.0.0.1:8000/list/

那么我想访问第二页,我就应该这样输入网址

http://127.0.0.1:8000/list/?page=2

以此类推,后面的也是一样的了,因为我们只添加了101条数据,所以只有11页,而我们如果让page=12的话,就会找不到页面。如果我们在网址后面没有传递page参数,默认返回的就是第一页。

而如果我们不想使用page作为参数,而是换一个p作为参数,那么就可以使用page_kwarg这个属性了 示例:将默认参数page改为p

class InfoListView(ListView):
    model = models.Article  # 重写model类属性,指定模型的列表
    template_name = 'article_list.html'     # 指定这个列表的模板。
    context_object_name = 'articles'    # 指定这个列表模型在模板中的参数名称。
    paginate_by = 10    # 每一页需要展示多少条数据
    ordering = 'create_time'    # 以时间进行排序展示
    page_kwarg = 'p'    # 指定换页面的参数 默认为page 以GET的方式传递参数

这样,我们就不能使用page作为参数了,而是只能使用p作为参数了。

还有2个比较常用的方法get_context_dataget_queryset

  1. get_context_data:获取上下文的数据。并且可以添加自己的参数,例如下面的username在上面定义的类中添加这个方法
class InfoListView(ListView):
    model = models.Article # 重写model类属性,指定模型的列表
    template_name = 'article_list.html' # 指定这个列表的模板。
    context_object_name = 'articles' # 指定这个列表模型在模板中的参数名称。
    paginate_by = 10  # 每一页需要展示多少条数据
    ordering = 'create_time'   # 以时间进行排序展示
    page_kwarg = 'p'   # 指定换页面的参数 默认为page 以GET的方式传递参数
    # 重写父类的get_context_data方法,添加自己的参数
    def get_context_data(self,**kwargs):
        # 我们需要先继承至父模板的get_context_data方法,否则我们有很多方法将不能使用
        context = super(InfoListView,self).get_context_data(**kwargs)
        # 然后添加自定义的参数
        context['username'] = 'augustrush'
        print(context)
        return context

我们就能在控制台看到打印出来的东西了。 里面包含了很多东西,有我们常用的paginator类、page_obj类和我们自定义的一些参数等。

  1. get_queryset:如果你提取数据的时候,并不是要把所有数据都返回,那么你可以重写这个方法。将一些不需要展示的数据给过滤掉。
class InfoListView(ListView):
    # 设置需要返回的数据
    def get_queryset(self):
        # 没有重写方法默认返回所有的数据
        # return models.Article.objects.all()
        return models.Article.objects.filter(id__lte=93)

总结:上面类中的属性和方法说明:

model:重写model类属性,指定这个列表是给哪个模型的。

queryset:跟model一样,二选一。设定基础的数据集,Model的设定没有过滤的功能。

template_name:指定这个列表的模板。

paginate_by:指定这个列表一页中展示多少条数据。

context_object_name:指定这个列表模型在模板中的参数名称。

ordering:指定这个列表的排序方式。

page_kwarg:获取第几页的数据的参数名称。默认是page。

get_context_data:获取上下文的数据。

get_queryset:如果你提取数据的时候,并不是要把所有数据都返回,那么你可以重写这个方法。将一些不需要展示的数据给过滤掉。

Paginator和Page类

PaginatorPage类都是用来做分页的。他们在Django中的路径为django.core.paginator.Paginatordjango.core.paginator.Page

Paginator常用属性和方法:

  1. count:总共有多少条数据。

  2. num_pages:总共有多少页。

  3. page_range:页面的区间。比如有三页,那么就range(1,4)。

class InfoListView(ListView):
    model = models.Article  # 重写model类属性,指定模型的列表
    template_name = 'article_list.html'     # 指定这个列表的模板。
    context_object_name = 'articles'    # 指定这个列表模型在模板中的参数名称。
    paginate_by = 10    # 每一页需要展示多少条数据
    ordering = 'create_time'    # 以时间进行排序展示
    page_kwarg = 'p'    # 指定换页面的参数 默认为page 以GET的方式传递参数

    # 重写父类的get_context_data方法,添加自己的参数
    def get_context_data(self,**kwargs):
        # 我们需要先继承至父模板的get_context_data方法,否则我们有很多方法将不能使用
        context = super(ArticleListView,self).get_context_data(**kwargs)
        # 然后添加自定义的参数
        context['username'] = 'augustrush'
        # print(context)
        paginator = context.get('paginator')  # paginator类

        print(paginator.count)      # 获取数据的个数
        # print(paginator.num_pages)    # 获取页数
        # print(paginator.page_range)   #或缺页数的范围,要前不要后

        return context

    # 设置需要返回的数据
    # def get_queryset(self):
    #     # 没有重写方法默认返回所有的数据
    #     # return models.Article.objects.all()
    #     return models.Article.objects.filter(id__lte=89)

Page类

page类的常用属性和方法:

  1. has_next: 是否有下一页

  2. has_previous: 是否有上一页

  3. next_page_number: 下一页的页码数

  4. previous_page_number: 上一页的页码数

  5. number:当前页的页码数 只有这一个是属性,其他都是方法

  6. start_index:当前这一页的第一条数据的索引值。

  7. end_index:当前这一页的最后一条数据的索引值。

class InfoListView(ListView):
    model = models.Article # 重写model类属性,指定模型的列表
    template_name = 'article_list.html' # 指定这个列表的模板。
    context_object_name = 'articles' # 指定这个列表模型在模板中的参数名称。
    paginate_by = 10  # 每一页需要展示多少条数据
    ordering = 'create_time'   # 以时间进行排序展示
    page_kwarg = 'p'   # 指定换页面的参数 默认为page 以GET的方式传递参数

    # 重写父类的get_context_data方法,添加自己的参数
    def get_context_data(self,**kwargs):
        # 我们需要先继承至父模板的get_context_data方法,否则我们有很多方法将不能使用
        context = super(ArticleListView,self).get_context_data(**kwargs)
        # 然后添加自定义的参数
        context['username'] = 'augustrush'
        # print(context)
        paginator = context.get('paginator')  # paginator类
        page_obj = context.get('page_obj')   # page_obj类

        # print(paginator.count)  # 获取数据的个数
        # print(paginator.num_pages)  # 获取页数
        # print(paginator.page_range) #或缺页数的范围,要前不要后

        print(page_obj.number)
        print(page_obj.start_index())
        print(page_obj.end_index())
        print(page_obj.has_next())
        print(page_obj.has_previous())
        print(page_obj.next_page_number())
        print(page_obj.previous_page_number())
        return context

    # 设置需要返回的数据
    # def get_queryset(self):
    #     # 没有重写方法默认返回所有的数据
    #     # return models.Article.objects.all()
    #     return models.Article.objects.filter(id__lte=89)

给类视图添加装饰器

在开发中,有时候需要给一些视图添加装饰器。如果用函数视图那么非常简单,只要在函数的上面写上装饰器就可以了。但是如果想要给类添加装饰器,那么可以通过以下两种方式来实现:

需求:在访问个人中心页面的时候,如果没有登录,我们就让它跳转到登录页面,登录之后才能访问个人中心。

这里我们就是用get请求来模拟是否登录了,即如果我们在访问网址的时候使用get方法传递了一个username的参数,我们就认为已经登录, 否则的话就没有登录。

1. 装饰类视图中的dispatch方法:

首先在view中定义个人中心的类视图和登录页面的函数视图:

# 个人中心的类视图
class ProfileView(View):
    # 只能通过GET请求来访问
    def get(self, request):
        return HttpResponse("个人中心页面")

    # 因为所有的请求方法在类视图中最终都会通过diapatch这个方法,
    # 所以我们需要重写dispatch方法,并且继承至父类的dispatch方法。
    def dispatch(self, request, *args, **kwargs):
        return super(Profileview,self).dispatch(request,*args,**kwargs)

# 登录视图
def login(request):
    return HttpResponse("登录页面")

在上面我们已经说到了所有的请求方法在类视图中最终都会通过diapatch这个方法,所以我们需要重写dispatch方法,并且继承至父类的dispatch方法。然后再使用装饰器将dispatch装饰,就达到了对类视图进行装饰的目地了,所以我们需要先写一个装饰器:

def login_required(func):
    def wrapper(request, *args, **kwargs)
        username = request.Get.get('username')
        # 判断是否得到username这个值,如果得到了,就认为已经登录成功了,否则就没有登录
        if username:
            return func(request, *args, **kwargs)
        else:
            # 没有登录 重定向到登录页面
            return redirect('login')
    return wrapper

然后我们就将我们写的装饰器对dispatch进行装饰,这里我们需要使用到django中对类视图装饰的方法了:

from django.utils.decorators import method_decorator

对类视图进行装饰我们一般都使用这个方法。

然后我们使用这个方法对dispatch进行装饰,在dispatch函数的上面一行写入:

@method_decorator(login_required)

装饰完之后ProfileView这个类视图的完整代码为:

# 个人中心的类视图
class Profileview(View):
    # 只能通过GET请求来访问
    def get(self,request):
        return HttpResponse('个人中心页面')

    # 因为所有的请求方法在类视图中最终都会通过diapatch这个方法,
    # 所以我们需要重写dispatch方法,并且继承至父类的dispatch方法。
    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(Profileview,self).dispatch(request,*args,**kwargs)

这样我们就实现了第一种方法对类视图进行装饰了。 就可以输入url进行测试了,例如我的个人中心网址是:

http://127.0.0.1:8000/class/prfile/

当我进行访问的时候,他就会给我们跳转至登录页面,因为我们没有传入一个值username进去,就认为我们没有登录,所以我们想要访问个人中心就需要传入username进去。

http://127.0.0.1:8000/class/prfile/?username=username

这样,我们就能访问到我们的个人中心了,说明我们的装饰器也起到了相应的效果了。

2. 直接装饰在整个类上:

但是上面的方法并不太好,因为我们在写类视图的时候,绝大多数就不会去重写dispatch方法,比如说我使用get请求,那么我就重写get方法就行了,使用post请求,重写post方法,而不用去重写dispatch方法。

那么怎样直接装饰在整个类上面呢,非常简单,只需要在类视图上面添加语句代码就行了

# 个人中心的类视图
@method_decorator(login_required,name='dispatch')
class Profileview(View):
    # 只能通过GET请求来访问
    def get(self,request):
        return HttpResponse('个人中心页面')

    # 因为所有的请求方法在类视图中最终都会通过diapatch这个方法,
    # 所以我们需要重写dispatch方法,并且继承至父类的dispatch方法。
    # @method_decorator(login_required)
    # def dispatch(self, request, *args, **kwargs):
    #     return super(Profileview,self).dispatch(request,*args,**kwargs)

这样,我们就不用重写dispatch方法了,只需要在后面添加一个参数name指定装饰的类中的哪一个方法既可以了。

当然,如果我们有多个装饰器,我们还可以指定一个列表,

@method_decorator([login_required,xx_required],name='dispatch')


  • 标签云

  • 支付宝扫码支持一下

  • 微信扫码支持一下



基于Nginx+Supervisord+uWSGI+Django1.11.1+Python3.6.5构建

京ICP备20007446号-1 & 豫公网安备 41100202000460号

网站地图 & RSS | Feed