美文网首页
Django2_08_CBV基础

Django2_08_CBV基础

作者: 运维开发_西瓜甜 | 来源:发表于2020-04-22 18:20 被阅读0次

本文链接:https://www.jianshu.com/p/230c652fc5a2
作者:西瓜甜

一、认识基本的 CBV

CBV 就是 Class Base View ,基于类的视图。

基于类的视图提供另一种将视图实现为 Python 对象而不是函数的方法。它们不能替代基于函数的视图,但与基于函数的视图相比,它们是有某些不同和优势的。

与特定的 HTTP 方法(GET, POST, 等等)关联的代码组织能通过单独的方法替代条件分支来解决。
面向对象技术(比如 mixins 多重继承)可用于将代码分解为可重用组件。

本质上来说,基于类的视图允许你使用不同的类实例方法响应不同 HTTP 请求方法,而不是在单个视图函数里使用有条件分支的代码。

因此在视图函数里处理 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 import View

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

类视图工作流程:

因为 Django 的 URL 解析器期望发送请求和相关参数来调动函数而不是类,基于类的视图有一个 as_view() 类方法,当一个请求到达的 URL 被关联模式匹配时,这个类方法返回一个函数。这个函数创建一个类的实例,调用 setup() 初始化它的属性,然后调用 dispatch() 方法。 dispatch 观察请求并决定它是 GETPOST,等等。如果它被定义,那么依靠请求来匹配方法,否则会引发 HttpResponseNotAllowed

# urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

说明:
CBV 的基于方法的视图返回值和基于函数的视图(FBV)返回值是相同的,既某种形式的 HttpResponse

内置通用显示视图

示例 model

from django.db import models
from django.utils import timezone
# Create your models here.

from users.models import UsersProfile

class ArticlePost(models.Model):
    '''
    博客文章数据模型
    '''
    # 文章作者。参数 on_delete 用于指定数据删除的方式
    author = models.ForeignKey(UsersProfile, related_name="users", verbose_name="作者 ID", on_delete=models.CASCADE)

    # 文章标题。models.CharField 为字符串字段,用于保存较短的字符串,比如标题
    title = models.CharField("标题", max_length=100)

    # 文章正文。保存大量文本使用 TextField
    body = models.TextField("正文")
    # 文章浏览量  PositiveIntegerField是用于存储正整数的字段
    views_counts = models.PositiveIntegerField("浏览量", default=0)
    # 文章创建时间。参数 default=timezone.now 指定其在创建数据时将默认写入当前的时间
    created = models.DateTimeField("创建时间", auto_now_add=True)

    # 文章更新时间。参数 auto_now=True 指定每次数据更新时自动写入当前时间
    updated = models.DateTimeField("更新时间", auto_now=True)

    # 内部类 class Meta 用于给 model 定义元数据

    class Meta:
        # ordering 指定模型返回的数据的排列顺序
        # '-created' 表明数据应该以倒序排列
        ordering = ('-created',)
        verbose_name = '文章'
        verbose_name_plural = verbose_name
        db_table = "article_post"

    # 函数 __str__ 定义当调用对象的 str() 方法时的返回值内容
    def __str__(self):
        # return self.title 将文章标题返回
        return f"{self.title}"


class Tag(models.Model):
    """
    文章标签模型
    """
    name = models.CharField("标签名称", max_length=12)
    aticle = models.ManyToManyField(ArticlePost, related_name="tag")
    class Meta:
        verbose_name = '标签'
        verbose_name_plural = '标签'
        db_table = 'tag'

    def __str__(self):
        return f"{self.name}"

ListView

返回一个模型对象列表。
可以指定模板文件路径名
自定义返回给前端模板语言中使用的对象的变量名

视图

class ArticlePostListView(ListView):
    # 指定获取数据的 model
    model = ArticlePost

    # 指定模板语言中使用的变量名
    context_object_name = 'postList'

    # 指明模板名称, 默认是 `model所在的应用名/model 名称的小写_list.html`
    template_name = 'blog/post-list.html'

说明:
模板文件要放置在设置好的 templates 正确目录下。

路由

    path('post_list/', ArticlePostListView.as_view(), name='postList'),


模板

这里使用的是 Bootstrap4

<table class="table table-hover">
    <thead>
      <tr>
        <th scope="col">#</th>
        <th scope="col">文章标题</th>
        <th scope="col">作者名字</th>
        <th scope="col">浏览量</th>
        <th scope="col">创建日期</th>
      </tr>
    </thead>
    <tbody>
      {% for article in postList %}
      <tr>
        <th scope="row">{{ forloop.counter }}</th>
        <td>{{ article.id }}</td>
        <td>{{ article.author.username }}</td>
        <td>{{ article.views_counts }}</td>
        <td>{{ article.created }}</td>
      </tr>
    {% endfor %}
    </tbody>
  </table>

对返回的对象列表进行过滤

指定 model = ArticlePost 实际上是
queryset = ArticlePost.objects.all() 的简写。
所以,通过使用 queryset 属性可以对返回的对象列表进行过滤控制。

例如下面的示例只是返回了以id 字段进行倒序排列的对象列表

class ArticlePostListView(ListView):
    queryset = ArticlePost.objects.order_by("-id")
    context_object_name = 'postList'
    template_name = 'blog/post-list.html'

其他的都不需要改变

动态过滤

大多数情况下,我们会通过 URL 给视图传递参数,视图通过这个参数作为过滤条件。

当基于类的视图被调用的时候,各种常用的东西被存储在 self 上,而且请求 (self.request) 会根据(self.args) 从 URLconf 中抓取位置参数,根据 (self.kwargs) 抓取基于名称的关键字参数。

示例
URL
path('post_list/<author>/', ArticleAuthorPostListView.as_view(), name='postAuthorList'),



VIEW

当然,仅仅这样做还不够,接下来需要在视图中覆盖 LlistView 中的 了解我get_queryset() 方法。这个方法用于返回 queryset 属性的值。
我们可以在这个方法里添加更多的逻辑。

from django.shortcuts import get_object_or_404
from django.views import View

from blog.models import ArticlePost,
from users.models import UsersProfile


class ArticleAuthorPostListView(ListView):
    context_object_name = 'postList'
    template_name = 'blog/post-list.html'

    def get_queryset(self):
          # 从用户 model 中获取到对应的作者对象
          self.author = get_object_or_404(UsersProfile, username=self.kwargs['author'])

          # 根据作者对象返回其所有的文章
          return ArticlePost.objects.filter(author__username=self.author)

前端
模板中的路由写法
<a class="nav-link" href="{% url 'cbv:postAuthorList' 'author2' %}">ListAuthorView</a>


返回的模板还是使用上例中使用的模板

获取 GET 请求的参数

http://127.0.0.1:8080/cbv/post_list/author2/?id=1

# views.py
uid = self.request.GET.get("id")

添加额外的数据

重写父类的 get_context_data 方法可以给返回前端模板的数据中增加额外的数据。

get_context_data(** kwargs)
会返回一个用于显示上下文数据的对象。
是包含以下内容的字典:

  • object:此视图正在展示的对象(self.object)。
  • context_object_name 上下文对象的名字

示例,在返回 model 中对象的同时,返回当前的时间。

from django.utils import timezone
from django.views.generic import  ListView


class ArticlePostListView(ListView):
    queryset = ArticlePost.objects.order_by("-id")
    context_object_name = 'postList'
    template_name = 'blog/post-list.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context

模板中使用

context['now'] 中的 now 就是模板中使用的变量名

<span>Date: {{ now  }}</span>

DetailView

用于通过条件过滤,获取到的 Model 中的单个对象,就是表中的一条数据。
同样可以指定返回的模板文件和模板语言中使用的数据变量名。
可以添加额外的数据。
注意需要从 URLconf 中捕获到参数,一般是用于基于主键的查找。

注意和 ListView 中添加过滤的区别,同样都是过滤,但是 DetailView 返回的是单个对象,就是 一条数据
而 ListView 始终返回一组数据,即便这组数据中只有一条数据,返回的形式任然是一个包含了列表的 Qeuryset 对象。

Example myapp/urls.py:

    path('post_detail/<slug:pk>/', ArticlePostDetailView.as_view(), name='postDetail'),

Example myapp/views.py:

from django.views.generic import DetailView

class ArticlePostDetailView(DeleteView):
    model = ArticlePost
    context_object_name = 'postDetail'
    template_name = 'blog/post-detail.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['now'] = timezone.now()
        return context

Example myapp/article_detail.html:

<h1> {{ postDetail.title}}</h1>
<p>{{ postDetail.author.username }}</p>
<p>日期: {{ now |date }}</p>
<p>{{ postDetail.body }}</p>

注意: <slug:pk> 中的 pk, 是 URLconf 传给视图的一个关键字参数, 比如 pk=1
DetailView 中的 get_object 方法会依据该值执行基于主键的查找。
相当于 model.Mymodelname.objects.filter(id=pk)

通过非主键字段进行过滤(扩展知识)

如果找不到参数 pk,它会在 URLconf 查找一个 slug_url_kwarg 指定的参数,默认值是 slug,并使用 slug_field 的值, 默认是 slug。执行一个slug 查找, 这也表名了会进行非主键的过滤查找 。
相当于 model.Mymodelname.objects.filter(slug=slug)

依据上面的意思,一般我们的 model 中不会存在 slug 字段,因此,我们往往会对 slug_field 的值进行更改,改为自己 model 中存在的,假设想使用此字段进行过滤查找的情况下。比如下面我们使用字段 updated, 可以这样修改:

    path('post_detail/<slug:slug>/', ArticlePostDetailView.as_view(), name='postDetail'),

class ArticlePostDetailView(DeleteView):
    ...
   # 设置 model 中的字段名
    slug_field = 'updated'
    ...

这样的情况下,查询数据库会是这样的

ArticlePost.objects.filter(updated=sulg)
> 注:slug 会是 url 中传递过来的值,比如 2019-07-01

注意: DetailView 视图的 get_object 方法只允许返回单个对象。
因此, 必须保证这个字段和其对应的值,只能在数据库表中查的一条数据。

添加额外的动作

有时候,经常会在访问一个视图的时候,可能想更新操作的 model 中的某个字段。

比如在 ArticlePost 中有个 last_accessed 字段,用来记录这条数据最后一次被访问的时间

class ArticlePostDetailView(DeleteView):
    model = ArticlePost
    context_object_name = 'postDetail'
    template_name = 'blog/post-detail.html'
    queryset = ArticlePost.objects.all()

    def get_object(self):
        obj = super().get_object()
        # Record the last accessed date
        obj.last_accessed = timezone.now()
        obj.save()
        return obj

验证类

Form

基本示例:

计划是这样的,使用 FormView 来验证一个用户注册的数据有效性。

1. 前端页面效果图

image.png

2. 前端代码

基于 Bootstrap4

        <form action="{% url 'users:register' %}" method="post">
            {% csrf_token %}
            <div class="form-row">
              <div class="form-group col-md-6">
                <label for="inputEmail4">用户名</label>
                <input type="text" name="username" value="{{ form.username.value|default:'' }}" class="form-control" id="inputEmail4" placeholder="用户名">
                {% if form.errors.username %}
                <span class="alert alert-danger" role="alert">
                    {{ form.errors.username.0 }}
                </span>
                {% endif %}
              </div>
              <div class="form-group col-md-6">
                <label for="inputPassword4">密码</label>
                <input type="password"  name="password" class="form-control" id="inputPassword4" placeholder="密码">
                {% if form.errors.password %}
                <span class="alert alert-danger" role="alert">
                    {{ form.errors.password.0 }}
                </span>
                {% endif %}
              </div>
            </div>
            <div class="form-group">
              <label for="inputAddress">邮箱地址</label>
              <input type="email" value="{{ form.email.value|default:''  }}" name="email" class="form-control" id="inputAddress" placeholder="邮箱地址">
              {% if form.errors.email %}
              <span class="alert alert-danger" role="alert">
                  {{ form.errors.email.0 }}
              </span>
              {% endif %}
            </div>
            <div class="form-group">
              <label for="inputAddress2">手机号</label>
              <input type="text" name="mobile"  value="{{ form.mobile.value|default:''  }}" class="form-control" id="inputAddress2" placeholder="手机号">
             {% if form.errors.mobile %}
              <span class="alert alert-danger" role="alert">
                  {{ form.errors.mobile.0 }}
              </span>
            {% endif %}
            </div>
            <button type="submit" class="btn btn-primary">注册</button>
          </form>
<input type="email" value="{{ form.email.value|default:''  }}" name="email" class="form-control" id="inputAddress" placeholder="邮箱地址">


{{ form.email.value|default:'' }} 假如验证不通过,会保存上次输入的值
其中 default:'' 是个过滤器,当第一次 GET 请求时,并没有向表单中输入任何值,Django 回返回 None, 这里会把 None 改为空字符串 ''
{{ form.errors.email.0 }} 假如这个数据提交的是错误的,这里会显示具体的错误信息。

错误示例如下图

image.png

4. 这个 form 类是这样的

注意:

这里的 类属性必须和前端 input 标签中的 name 属性同名。
比如
<input type="password" name="password">name 的值是 password
下面类中的属性也要是 password

# registor_forms.py
from django import forms


class RegiterUserForm(forms.Form):
    username = forms.CharField(
      # 必须的值
      required=True,
    )
    password = forms.CharField(
        required=True,
    )
    email = forms.EmailField(
        required=True,
    )
    mobile = forms.CharField(
      min_length=11,
      max_length=11,
      required=True,
    )

使用 FormView 构建视图

之后可以使用 FormView 结合刚才的 RegiterUserForm 构建这个用于验证用户注册的视图:

# views.py
from django.urls import reverse_lazy  # 用于路由反转,把名称空间解析为 URL
from users.users_forms.register_form import RegiterUserForm
from django.views.generic.edit import FormView

class UserRegisterView(FormView):
      # GET 请求时,返回此 模板
      template_name = 'register.html'

      # 自定义的Form 类
      form_class = RegiterUserForm

      # 提交数据成功后跳转到此 url
      success_url = reverse_lazy('users:login')

      def form_valid(self, form):
        # 数据有效,存库
        UsersProfile.objects.create(**form.cleaned_data)
        return super().form_valid(form)

      def form_invalid(self, form):
        # 数据无效返回原来的模板,并且携带原来提交的数据
        return super().form_invalid(form)

注意:
FormView 继承了 TemplateResponseMixin ,因此可以在这里使用 template_name
form_valid() 默认实现了简单的重定向至 success_url

从模型中创建表单

假如你对已有的 model 进行表单的验证,无需像之前使用 forms.Form 中那样定义每个字段的属性,应用在 model 中已经定义过了。

使用 ModelForm 可以让这件事做起来易如反掌。

model 示例

假设有如下 model, 用于记录用户的留言

from django.utils import timezone
from django.db import models

class UserAsk(models.Model):
    name = models.CharField('姓名', max_length=20)
    mobile = models.CharField('手机', max_length=11)
    article_name = models.CharField('文章名', max_length=50)
    content = models.TextField('留言内容')
    add_time = models.DateTimeField('添加时间', default=timezone.now)

    class Meta:
        verbose_name_plural = '用户留言'
        db_table = 'user_ask'

    def __str__(self):
        return self.name

创建表

# 项目根目录下执行如下命令
# 1. 生成数据移植文件
on git:master x$ python3 manage.py makemigrations

# 2. 创建表结构
on git:master x$ python3 manage.py migrate 

接下来创建一个自定义的 Form 类,需要继承 forms.ModelForm

from django import forms

from blog.models import UserAsk

class UserAskForm(forms.ModelForm):
    class Meta:
        model = UserAsk
        fields = ['name', 'mobile', 'content', 'article_name']
  • model 属性指定自己的 model
  • fieldes 属性指定要要验证哪些字段

这里还可添加一些额外的自定义验证,比如验证手机号的合法性

clean_<fieldname>() 方法是在表单子类上调用的,其中 <fieldname> 替换为表单字段属性的名称。
此方法可以实现对此字段除了 Form 本身验证之外的任何特定验证需求,而与该所属字段属性的类型无关。
此方法不传递任何参数。您需要在 self.cleaned_data 中获取该字段的值,并记住此时它将是一个Python对象,而不是表单中提交的原始字符串(因为上面的常规表单验证方法已经验证过一次数据,并对数据类型进行正确转换了)。

例如,如果要验证名为 serialnumber 的字段的字符内容是否唯一,则 clean_serialnumber()方法 将是执行此操作的正确方法名称。
此方法的返回值将替换已 cleaned_data 中的现有值,因此它必须是已 cleaned_data 中字段的值(即使此方法未更改它)或新的已清理值。

import re

from django import forms

from blog.models import UserAsk


class UserAskForm(forms.ModelForm):
        class Meta:
            model = UserAsk
            fields = ['name', 'mobile', 'article_name','content']

        # 针对指定字段添加自定义验证
        # 方法名格式要求:  clean_字段名
        def clean_mobile(self):
            """
            验证手机号是否合法
            :return: 合法的数据或者错误信息
            """
            mobile = self.cleaned_data['mobile']
            PRGEX_MOBILE = r'^1[358]\d{9}|^147\d{8}|^176\d{8}$'
            regex = re.compile(PRGEX_MOBILE)
            if regex.match(mobile):
                return mobile
            else:
                raise forms.ValidationError(
                    '无效的手机号',
                    code='mobile_invalid'
                )

使用

路由

form .views import  UserAskView
    path('user_ask', UserAskView.as_view(), name='user_ask'),

视图

class UserAskView(FormView):
    # GET 请求时,返回此 模板
    template_name = 'blog/user_ask.html'

    # 自定义的Form 类
    form_class = UserAskForm

    # 提交数据成功后跳转到此 url
    success_url = reverse_lazy('cbv:user_ask')

    def form_valid(self, form):
        # 数据有效
        # 使用 save 方法可直接存入数据库
        form.save()
        return super().form_valid(form)

    def form_invalid(self, form):
        # 数据无效返回原来的模板,并且携带原来提交的数据
        print(form.errors)
        return super().form_invalid(form)

save() 方法
每个 ModelForm 都有一个 save() 方法,此方法根据绑定到表单的数据创建并保存数据库对象。

HTML 模板


关于更新

ModelForm 的子类可接受一个现有的模型实例作为关键字参数 instance 的值;如果提供了,则 save() 会更新这个实例。如果没有,则 save() 会创建一个对应模型的新实例。

>>> from myapp.models import Article
>>> from myapp.forms import ArticleForm

# Create a form instance from POST data.
>>> f = ArticleForm(request.POST)

# Save a new Article object from the form's data.
>>> new_article = f.save()

# Create a form to edit an existing Article, but use
# POST data to populate the form.
>>> a = Article.objects.get(pk=1)
>>> f = ArticleForm(request.POST, instance=a)
>>> f.save()

save() 方法接受一个可选参数 commit ,它的值是 True 或者 False 。默认是 True。
调用 save() 的时候使用 commit=False ,那么它会返回一个尚未保存到数据库的对象。

ModelForm 与普通的表单工作方式一样。例如,用 is_valid() 方法来检查合法性,用 is_multipart() 方法来确定表单是否需要multipart文件上传(之后是否必须将 request.FILES 传递给表单),等等。

选择要使用的字段

Django 强烈建议您使用 fields 属性来显式设置所有应在表单中编辑的字段。
这是处于安全考虑。

  1. 明确指明使用所有的字段进行验证
        fields = '__all__'
  1. 明确的排除不用验证的字段
        exclude = ['article_name']

覆盖默认字段的属性

自定义小部件

如果您希望 Author 的 name 属性的 CharField 由 <textarea> 代替默认的 <input type="text"> 来表示,您可以重写字段的部件:

from django.forms import ModelForm, Textarea
from myapp.models import Author

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        widgets = {
            'name': Textarea(attrs={'cols': 80, 'rows': 20}),
        }

自定义错误信息

from django.utils.translation import gettext_lazy as _

class AuthorForm(ModelForm):
    class Meta:
        model = Author
        fields = ('name', 'title', 'birth_date')
        labels = {
            'name': _('Writer'),
        }
        help_texts = {
            'name': _('Some useful help text.'),
        }
        error_messages = {
            'name': {
                'max_length': _("This writer's name is too long."),
            },
        }

提供初始值

与普通表单一样,可以在实例化表单时通过指定 initial 参数来指定表单的初始值。以这种方式提供的初始值会覆盖表单字段的初始值以及对应模型实例的初始值。例如:

>>>> article = Article.objects.get(pk=1)
>>> article.headline
'My headline'
>>> form = ArticleForm(initial={'headline': 'Initial headline'}, instance=article)
>>> form['headline'].value()
'Initial headline'</pre>

装饰基于类的视图

因为基于类的视图不是函数,所以根据你是使用 as_view() 还是创建子类,装饰它们的工作方式会有不同。

在 URLconf 中装饰

装饰基于类的视图最简单的方式是装饰 as_view() 方法的结果。最方便的地方是在你部署视图的 URLconf 中执行此操作。

from django.contrib.auth.decorators import login_required, permission_required
from django.views.generic import TemplateView

from .views import VoteView

urlpatterns = [
    path('about/', login_required(TemplateView.as_view(template_name="secret.html"))),
    path('vote/', permission_required('polls.can_vote')(VoteView.as_view())),
]

这个方式在每个基本实例上应用装饰器。如果你想装饰视图的每个实例,你需要采用不同方式。

装饰类

装饰基于类的视图的每个实例,你需要装饰类定义本身。为此,你可以将装饰器应用到类的 dispatch()方法。

类上的方法与独立函数完全不同,因此你不能应用函数装饰器到类的方法上——需要先将它转换为方法装饰器。method_decorator 装饰器会将函数装饰器转换为方法装饰器,这样它就被用在实例方法上。举例:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)

或者,更简洁的说,你可以用装饰类来代替,并作为关键参数 name 传递要被装饰的方法名:

from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView


@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

若果在一个地方使用了多个装饰器,可以定义一个含有多个函数装饰器的列表,之后使用它,而不是多次调用
method_decorator

这两个类是等价的:

# good
decorators = [never_cache, login_required]

@method_decorator(decorators, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'


# bad
@method_decorator(never_cache, name='dispatch')
@method_decorator(login_required, name='dispatch')
class ProtectedView(TemplateView):
    template_name = 'secret.html'

装饰器将按照它们传递给装饰器的顺序来处理请求。在这个例子里,never_cache() 将在 login_required() 之前处理请求。

特殊视图跳过 CSRF 验证

from django.http import JsonResponse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views import View


@method_decorator(csrf_exempt, name='dispatch')
class AssetView(View):
    def get(self, request):
        return JsonResponse({"get": "GET"})

    def post(self, request):
        data = json.loads(request.body.decode())
        print(data)
        return JsonResponse({"error": "ok"})

相关文章

  • Django2_08_CBV基础

    本文链接:https://www.jianshu.com/p/230c652fc5a2作者:西瓜甜 一、认识基本的...

  • 机械设备安装技术

    设备基础种类及应用 垫层基础允许产生沉降:大型储罐 浅基础扩展基础联合基础:轧机独立基础 深基础桩基础:适用于需要...

  • 基础,基础,基础

    如果有人现在问我,JAVA该怎么学,我会告诉他不要急于求成,少看视频,多练,多思考。但说到这里有人可能会反...

  • 【Android】知识点汇总,坚持原创ing

    Android基础 Java基础 Java基础——Java内存模型和垃圾回收机制 语法基础 语法基础——C语法基础...

  • Java 基础

    Java 基础01Java开发入门 Java 基础02Java编程基础 Java 基础03面向对象 Java 基础...

  • 零基础学画画从入门到放弃

    零基础应该怎么学画画?零基础那就从基础开始学啊!基础是什么?造型基础和色彩基础。 造型基础就是用点线面组成起码能让...

  • 面试题汇总

    1.Java基础面试问题 Java基础之基础问题 Java基础之面向对象 Java基础之数据结构 Java基础之I...

  • 基础基础还是基础

    这次去面试,还是被基础给打趴下了。 对于PHP7的新特性没有了解。 对于TP的新特性没有了解。 再一个就是独立完成...

  • 零基础学UI设计需要美术基础吗?

    零基础学UI设计需要美术基础吗?零基础学UI设计需要美术基础吗?零基础学UI设计需要美术基础吗?零基础学UI设计需...

  • 基础基础!

    人生中第一个自主设计的实验方案终于得到认可^O^在设计方案过程中认识到基础知识以及细心的重要性,还有半个学期可以努...

网友评论

      本文标题:Django2_08_CBV基础

      本文链接:https://www.haomeiwen.com/subject/kemycctx.html