基于类的视图的介绍

Django之前一直用的视图函数,但是视图函数有一定的限制,不易扩展或者自定义。而基于类的视图就是为了解除视图函数的限制。它使用Mixin等基类使得开发更加便捷。

在基于类的视图中,我们不必像视图函数中那样,针对不同的请求方法,写分支语句。而是只需要重构实例方法即可。

视图函数中处理HTTP GET 的代码看上去将像:

from django.http import HttpResponse
def my_view(request):
    if request.method == 'GET':# <view logic>
    return HttpResponse('result')

在基于类的视图中,它将变成:

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

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

    def post(self, request):
        # <view logic>
        return HttpResponse('result')

因为Django 的URL 解析器将请求和关联的参数发送给一个可调用的函数而不是一个类,所以基于类的视图有一个as_view() 类方法用来作为类的可调用入口。该as_view 入口点创建类的一个实例并调用dispatch() 方法。dispatch 查看请求是GET 还是POST 等等,并将请求转发给相应的方法,如果该方法没有定义则引发HttpResponseNotAllowed。 使用内建的通用类的视图,会自带一些变量,如user,csrftoken等等。
下面是我写的一个实例:

class ChangePwdView(View):
    template_name = 'dashboard/changepasswd.html'
    form_class = PasswordChangeForm

    def get(self, request, *args, **kwargs):
        form = PasswordChangeForm(request.user)
        return render(request,self.template_name,{'form':form})

    def post(self, request, *args, **kwargs):
        form = PasswordChangeForm(request.user,request.POST)
        if form.is_valid():
            form.save()
            auth.logout(request)
            return HttpResponseRedirect('/account/login/')

        return render(request,self.template_name,{'form':form})

    @method_decorator(login_required(login_url='/account/login/'))
    def dispatch(self, *args, **kwargs):
        return super(ChangePwdView, self).dispatch(*args, **kwargs)

在开发中,经常使用基于类的内建通用视图,会进一步简化开发过程。 内建的显示(对象)视图主要有ListView,DetailView,TemplateView。

ListView是定义多个Django对象,返回一个查询集。通常要定义query_set,以及get_context_data(获取额外上下文).
DetailView是定义单个Django对象,返回单个查询结果,并显示其详细信息。

下面是我项目中的一个例子:

class BaseMixin(object):

    def get_context_data(self,*args,**kwargs):
        context = super(BaseMixin, self).get_context_data(**kwargs)
        try:
            context['website_name'] = u'千与千寻'
            context['categories'] = Category.available_categories()
            context['hottest_articles'] = Article.get_hottest_articles()
            context['latest_articles'] = Article.get_latest_articles()
        except Exception:
            logger.error('load inital context fails')

        return context


class IndexView(BaseMixin,ListView):
    template_name = 'index.html'
    paginate_by = NUM_PER_PAGE
    context_object_name = 'article_list'

    def get_queryset(self):
        self.keyword = self.request.GET.get('keyword')
        if not self.keyword:
            self.article_list =  Article.objects.filter(status=0)
        else:
            query_sql =( Q(title__icontains=self.keyword) | Q(content__icontains=self.keyword))
            self.article_list = Article.objects.filter(query_sql,status=0)
        return self.article_list

    def get_context_data(self,*args,**kwargs):
        context = super(IndexView,self).get_context_data(**kwargs)
        length = len(self.article_list)
        context['article_list_length'] = length
        context['keyword'] = self.keyword
        context['logion'] = get_logion()
        number = length if length < 5 else 5
        context['carousel_page_list'] = random.sample(self.article_list,number)
        return context

get_queryset方法用来过滤查询集合,get_context_data用来获得额外的上下文。 BaseMinxin的用法在Django中比较常见,功能类似于utils里面的功用函数。

除了显示视图之外,还有编辑视图,FormView,CreateView,UpdateVIew,DeleteView等等。 当我们要提交表单的时候,可以使用FormView类。 我分别写了一个普通的用于处理表单的视图函数和一个FormView子类,对比看一下:

def Login(request):
    errors = []
    if request.method == 'POST':
        form = UserLoginForm(request.POST)
        if form.is_valid():
            cd = form.cleaned_data
            username,password = cd['username'],cd['password']
            if not username:
                errors.append(u'请输入用户名')
            if not password:
                errors.append(u'请输入密码')
            user = auth.authenticate(username=username,password=password)
            if user is not None:
                auth.login(request,user)
                #this will rewrite html
                return HttpResponseRedirect("/")
            else:
                errors.append(u'无效的用户名或密码,请重新登录。')
        else:
            pass
    else:
        form = UserLoginForm()
    return render(request,'dashboard/login.html',{'form':form,'errors':errors})


class LoginFormView(FormView):
    template_name = 'dashboard/viewlogin.html'
    form_class = UserLoginForm
    success_url = '/'


    def form_valid(self, form):
        cd = form.cleaned_data
        username,password = cd['username'],cd['password']
        if not username:
            errors.append(u'请输入用户名')
        if not password:
            errors.append(u'请输入密码')
        user = auth.authenticate(username=username,password=password)
        if user is not None:
            auth.login(self.request,user)
        return super(LoginFormView,self).form_valid(form)

写的LoginFormView要比普通的视图函数简单得多,只需要重构一个form_valid即可。 它会自动获得Post数据后获得的表单。

关于编辑视图,我就拿一个官网例子说明一下吧。

from django.views.generic.edit import CreateView
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from models import Book
from forms import BookForm

class AddBook(CreateView):
    form_class = BookForm
    model = Book
    template_name = 'addbook.html'  #这里是你的模板文件名

这个视图就可以实现Book的添加,如此来看真的很简单,只需要Book的Model模型,Book的表单,就可以实现。

--------EOF---------
微信分享/微信扫码阅读