Django的view是一个可以调用的函数, 在CBV中 view class类 调用 as_view()
方法, 返回像FBV一样的可调用函数
这个方法在django.views.generic.View
中实现, 所有的 CBV 的类都要直接或者间接的继承
Django默认提供了一些基础的 generic class-based views (GCBVs)
视图类
CBV
的建议
➤ Less view code is better.Keep your views simple.
精简代码
➤ Never repeat code in views.
DRY不要重复代码
➤ Views should handle presentation logic. Try to keep business logic in models when possible,
or in forms if you must.
处理一些展示的逻辑, 业务逻辑最好放在model中或者是form中
➤ Keep your mixins simpler.
在CBV
中使用一些Mixins
公共属性
使用 mixins
构建views的建议
1 The base view classes provided by Django always go to the right.
2 Mixins go to the left of the base view.
3 Mixins should inherit from Python’s built-in object type. Keep your inheritance chain simple!
实例
from django.views.generic import TemplateView
class FreshFruitMixin: # 3
def get_context_data(self, **kwargs):
context = super(FreshFruitMixin, self).get_context_data(**kwargs)
context["has_fresh_fruit"] = True
return context
class FruityFlavorView(FreshFruitMixin, TemplateView): # 1, 2
template_name = "fruity_flavor.html"
具体的GCBV
在什么情况下使用 (需要渲染templates
)
GCBV django.views.generic 种类 |
使用目的 |
---|---|
View | Base view or handy view that can be used for anything. |
RedirectView | Redirect user to another URL |
TemplateView | Display a Django HTML template. |
ListView | List objects . |
DetailView | Display an object |
FormView | Submit a form |
CreateView | Create an object |
UpdateView | Update an object |
DeleteView | Delete an object |
Generic date views | For display of objects that occur over a range of time (Blog博客用) |
如何使用 CBV
- 控制权限
Constraining Django CBV/GCBV Access to Authenticated Users
# flavors/views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import DetailView
from .models import Flavor
class FlavorDetailView(LoginRequiredMixin, DetailView):
model = Flavor
- 在
GCBV
中增加自己的处理Performing Custom Actions on Views With Valid Forms
重写 form_valid()
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
def form_valid(self, form):
# Do custom logic here
return super(FlavorCreateView, self).form_valid(form)
- 在
GCBV
中增加自己的处理Performing Custom Actions on Views With InValid Forms
重写 form_invalid()
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
def form_invalid(self, form):
# Do custom logic here
return super(FlavorCreateView, self).form_invalid(form)
listview 中的變量
{'paginator': None, 'page_obj': None, 'is_paginated': False, 'object_list': <QuerySet [<Flavor: Flavor object>]>, 'flavor_list': <QuerySet [<Flavor: Flavor object>]>, 'view': <
flavor.views.FlavorListView object at 0x0000018A506386D8>}
- 使用
view object
对象
from django.contrib.auth.mixins import LoginRequiredMixin
from django.utils.functional import cached_property
from django.views.generic import UpdateView, TemplateView
from .models import Flavor
from .tasks import update_user_who_favorited
class FavoriteMixin:
@cached_property
def likes_and_favorites(self):
"""Returns a dictionary of likes and favorites"""
likes = self.object.likes()
favorites = self.object.favorites()
return {
"likes": likes,
"favorites": favorites,
"favorites_count": favorites.count(),
}
class FlavorUpdateView(LoginRequiredMixin, FavoriteMixin, UpdateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
def form_valid(self, form):
update_user_who_favorited(
instance=self.object,
favorites=self.likes_and_favorites['favorites']
)
return super(FlavorUpdateView, self).form_valid(form)
class FlavorDetailView(LoginRequiredMixin, FavoriteMixin, TemplateView):
model = Flavor
GCBV视图
与form
组合方式 How GCBVs and Forms Fit Together
# flavors/models.py
from django.db import models
from django.urls import reverse
class Flavor(models.Model):
STATUS_0 = 0
STATUS_1 = 1
STATUS_CHOICES=(
(STATUS_0, 'zero'),
(STATUS_1 = 'one'),
)
title = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
scoops_remaining = models.IntegerField(choices=STATUS_CHOICES,
default=STATUS_0)
def get_absolute_url(self):
return reverse("flavors:detail", kwargs={"slug": self.slug})
GCBV Views
+ModelForm
# flavors/views.py
# 1
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, DetailView, UpdateView
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
class FlavorUpdateView(LoginRequiredMixin, UpdateView):
model = Flavor
fields = ['title', 'slug', 'scoops_remaining']
class FlavorDetailView(DetailView):
model = Flavor
# 2 使用 form_valid() 和 Django 的 message 发送附加的信息给用户
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import CreateView, DetailView, UpdateView
from .models import Flavor
class FlavorActionMixin:
fields = ['title', 'slug', 'scoops_remaining']
@property
def success_msg(self):
return NotImplemented
def form_valid(self, form):
messages.info(self.request, self.success_msg)
return super(FlavorActionMixin, self).form_valid(form)
class FlavorCreateView(LoginRequiredMixin, FlavorActionMixin,CreateView):
model = Flavor
success_msg = "Flavor created!"
class FlavorUpdateView(LoginRequiredMixin, FlavorActionMixin, UpdateView):
model = Flavor
success_msg = "Flavor updated!"
class FlavorDetailView(DetailView):
model = Flavor
#
{ if messages }
<ul class="messages">
{ for message in messages }
<li id="message_"
{ if message.tags } class=""
{ endif }>
</li>
{ endfor }
</ul>
{ endif }
GCBV Views
+Form
from django.views.generic import ListView
from .models import Flavor
class FlavorListView(ListView):
model = Flavor
def get_queryset(self):
# Fetch the queryset from the parent get_queryset
queryset = super(FlavorListView, self).get_queryset()
# Get the q GET parameter
q = self.request.GET.get("q")
if q:
# Return a filtered queryset
return queryset.filter(title__icontains=q)
# Return the base queryset
return queryset
{# templates/flavors/_flavor_search.html #}
{ comment }
Usage: { include "flavors/_flavor_search.html" }
{ endcomment }
<form action="{ url "flavor_list" }" method="GET">
<input type="text" name="q" />
<button type="submit">search</button>
</form>
使用普通的django.views.generic.View
实例
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.shortcuts import render, redirect
from django.views.generic import View
from .forms import FlavorForm
from .models import Flavor
class FlavorView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# Handles display of the Flavor object
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
return render(request,
"flavors/flavor_detail.html",
{"flavor": flavor}
)
def post(self, request, *args, **kwargs):
# Handles updates of the Flavor object
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
form = FlavorForm(request.POST)
if form.is_valid():
form.save()
return redirect("flavors:detail", flavor.slug)
可以处理JSON
, PDF
或者其他 non-HTML
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.views.generic import View
from .models import Flavor
from .reports import make_flavor_pdf
class FlavorPDFView(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
# Get the flavor
flavor = get_object_or_404(Flavor, slug=kwargs['slug'])
# create the response
response = HttpResponse(content_type='application/pdf')
# generate the PDF stream and attach to the response
response = make_flavor_pdf(response, flavor)
return response