您的位置:首页 > 编程语言 > Go语言

Django学习笔记之Class-Based-View

2017-09-25 17:07 357 查看
Django写的多了,有些问题才逐渐认识到。

比如有一个view比较复杂,调用了很多其他的函数。想要把这些函数封装起来,怎么办?

当然,可以用注释#------view------这样将函数隔离开,这种方法太low了,简直是在骗自己,连封装都算不上。
Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。
所以Django在后来加入了Class-Based-View。可以让我们用类写View。
这样做的优点主要下面两种:
(1)提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承);
(2)可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性;

要理解django的class-based-view(以下简称cbv),首先要明白django引入cbv的目的是什么。在django1.3之前,generic view也就是所谓的通用视图,使用的是function-based-view(fbv),亦即基于函数的视图。有人认为fbv比cbv更pythonic,但是,python的一大重要的特性就是面向对象。而cbv更能体现python的面向对象。cbv是通过class的方式来实现视图方法的。class相对于function,更能利用多态的特定,因此更容易从宏观层面上将项目内的比较通用的功能抽象出来。 cbv的实现原理通过看django的源码就很容易明白,大体就是由url路由到这个cbv之后,通过cbv内部的dispatch方法进行分发,将get请求分发给cbv.get方法处理,将post请求分发给cbv.post方法处理,其他方法类似。并且,cbv里引入了mixin的概念。Mixin就是写好了的一些基础类,然后通过不同的Mixin组合成为最终想要的类。
所以,理解cbv的基础是,理解Mixin。Django中使用Mixin来重用代码,一个View Class可以继承多个Mixin,但是只能继承一个View(包括View的子类),推荐把View写在最右边,多个Mixin写在左边。Mixin也是比较复杂的技术 。

如何正确使用 CBVs (Class-based views)

1. CBVs的使用原则

代码越少越好

永远不要重复代码

View应当只包含呈现逻辑, 不应包括业务逻辑

保持view逻辑清晰简单

不要将CBVs用作403, 404, 500的错误处理程序

保持mixin简单明了

2. 如何使用mixin

在编程中mixin是指为继承它的class提供额外的功能, 但它自身却不能单独使用的类. 在具有多继承能力的编程语言中, mixin可以为类增加额外功能或方法. 在Django中, 我们可以使用mixin为CBVs提供更多的扩展性, 当然在类继承过程中, 我们推荐以下原则:

Django自身提供的View永远在最右边

mixin依次在以上view的左边

mixin永远继承自Python的object类型

在这里顺便推荐一个很好的django库: django-braces. 该库中提供众多django的mixin, 可以方便我们日常使用.

以下是一个简单地例子, TemplateView是django自身提供的基本View, 因此在最右边; FreshFruitMixin则在TemplateView左边; FreshFruitmixin继承自object:

from django.views.generic import TemplateView

class FreshFruitMixin(object):

def get_context_data(self, **kwargs):
context = super(FreshFruitMixin, self).get_context_data(**kwargs)
context["has_fresh_fruit"] = True
return context

class FruitFlavorView(FreshFruitMixin, TemplateView):
template_name = "fruit_flavor.html"

3. 如何使用Django自身的CBV

CBVs在功能上的可扩展性, 牺牲的是简单性, 一个CBV最多的时候拥有8个import关系. (如果希望进一步了解这些继承关系, 可以使用Classy Class-Based Views进行查看.) 所以要弄懂那个View最适合当下的场景对于开发人员也是一个挑战. 为了减少CBVs的使用难度, 我们将这些View和基本的用法列在下表中, 为了显示方便, 名字前的django.views.generic前缀皆省去:

名字目的例子
View基本View, 可以在任何时候使用见后面详细介绍
RedirectView重新定向到其他URL将访问"/log-in/"的用户重新定向到"/login/"
TemplateView显示Django HTML template一般网站中使用模板显示的页
ListView显示对象列表文章列表页
DetailView显示对象详情文章详细页
FormView提交From网站联系我们或emai订阅form
CreateView创建对象创建新文章页
UpdateView更新对象修改文章页
DeleteView删除对象删除文章页
Generic date views显示一段时间内的对象按时间归类的博客

4. CBVs的使用技巧

a. 限定访问权限

在django tutorial中介绍了如何一起使用django.contrib.auth.decorators.login_required和CBV, 这是一个典型的错误例子.

还好, 我们有django-braces. 在django-braces中已经提供了一个LoginRequiredMixin:

# myapp/views.py
from django.views.generic import DetailView

from braces.views import LoginRequiredMixin

from .models import Article

class ArticleDetailView(LoginRequiredMixin, DetailView):
model = Article

b. 在form提交成功后执行代码

当需要在form提交成功后执行自定义的代码时, 可以使用form_valid()方法, form_valid()方法返回的是django.http.HttpResponseRedirect:

# myapp/views.py
from django.views.generic import CreateView

from braces.views import LoginRequiredMixin

from .models import Article

class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
fields = ('title', 'slug', 'content')

def form_valid(self, form):
# 自定义的代码逻辑写在这里
return super(ArticleCreateView, self).form_valid(form)

c. 在form提交不成功后执行代码

当需要在form提交不成功后执行自定义的代码时, 可以使用form_invalid()方法, form_invalid()方法返回的也是django.http.HttpResponseRedirect:

# myapp/views.py
from django.views.generic import CreateView

from braces.views import LoginRequiredMixin

from .models import Article

class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article

def form_invalid(self, form):
# 自定义的代码逻辑写在这里
return super(ArticleCreateView, self).form_invalid(form)

5. CBV和form如何结合使用

下面我们介绍一下常见的django form和CBV结合使用的模式, 首先我们定义一个Article model方便举例:

# myapp/models.py
from django.db import models
from django.core.urlresolvers import reverse

STATUS = {
(0, 'zero'),
(1, 'one'),
}

class Article(models.Model):
title = model.CharField(max_length=255)
slug = model.SlugField()
review_num = models.IntegerField(default=0, choices=STATUS)

def get_absolute_url(self):
return reverse("article_detail", kwargs={"slug": self.slug})

a. Views和ModelForm

下面的例子中, 我们利用django.contrib.messages和CBVs构建一套创建, 更新和显示一篇article的view, 包括:

ArticleCreateView: 用于创建新article

ArticleUpdateView: 用于更新article

ArticleDetailView: 用于确认创建或更新后的article

# myapp/views.py
from django.contrib import messages
from django.views.generic import CreateView, UpdateView, DetailView

from braces.views import LoginRequiredMixin

from .models import Article

class ArticleActionMixin(object):
@property
def success_msg(self):
return NotImplemented

def form_valid(self, form):
messages.info(self.request, self.success_msg)
return super(ArticleActionMixin, self).form_valid(form)

class ArticleCreateView(LoginRequiredMixin, ArticleActionMixin, CreateView):
model = Article
fields = ('title', 'slug', 'review_num')
success_msg = "Article Created!"

class ArticleUpdateView(LoginRequiredMixin, ArticleActionMixin, UpdateView):
model = Article
fields = ('title', 'slug', 'review_num')
success_msg = "Article Updated!"

class ArticleDetailView(DetailView):
model = Article

接下来是template

{# templates/myapp/article_detail.html #}
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li>{ message } </li>
</ul>
{% endif %}

b. Views和Form

下面我们以搜索article功能为例子, 介绍一下CBV和form的常见使用样式, 在article列表页中点击搜索按钮, 显示搜友符合条件的article列表:

# myapp/views.py
from django.views.generic import ListView

from .models import Article

class ArticleListView(ListView):
model = Article

def get_queryset(self):
queryset = super(ArticleListView, self).get_queryset()

q = self.request.GET.get('q')

if q:
return queryset.filter(title__icontains=q)

return queryset

然后可以使用include以下tenplate呈现搜索form:

{# templates/myapp/_article_search.html #}
<form action="{% url "article_list" %} method="GET"">
<input type="text" name="q"></>
<button type="submit">搜索</>
</form>

6. 单独使用View

只用django.views.generic.View, 而不用FBV来构建所有django项目中的view也是可行的, 这也没有你所想象的那么复杂. 使用View的好处是, 我们不需要写许多内套式的if语句, 我们可以直接覆盖使用View的get(), post()等方法:

from django.shortcuts import get_object_or_404, render, redirect
from django.views.generic import View

from braces.views import LoginRequiredMixin

from .forms import ArticleForm
from .models import Article

class ArticleView(LoginRequiredMixin, View):

def get(self, request, *args, **kwargs):
article = get_object_or_404(Article, pl=kwargs['slug'])
return render(request,
"myapp/article_detail.html",
{"article": article}
)

def post(sele, request, *args, **kwargs):
article = get_object_or_404(Article, pl=kwargs['slug'])
form = ArticleForm(request.POST)
if form.is_valid():
form.save()
return redirect("myapp:article", article.slug)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: