Django经典面试问题与答案汇总(上)
1. Django的优点和缺点有哪些?
Django的优点
-
功能完善、要素齐全:自带大量企业Web开发常用工具和框架(比如分页,auth,权限管理), 适合快速开发企业级网站。
-
完善的文档:经过十多年的发展和完善,Django有广泛的实践案例和完善的在线文档。开发者遇到问题时可以搜索在线文档寻求解决方案。
-
强大的数据库访问组件:Django的Model层自带数据库ORM组件,使得开发者无须学习SQL语言即可对数据库进行操作。
-
Django先进的App设计理念: App是可插拔的,是不可多得的思想。不需要了,可以直接删除,对系统整体影响不大。
-
自带台管理系统admin:只需要通过简单的几行配置和代码就可以实现一个完整的后台数据管理控制平台
Django的缺点
-
大包大揽: 对于一些轻量级应用不需要的功能模块Django也包括了,不如Flask轻便。
-
过度封装: 很多类和方法都封装了,直接使用比较简单,但改动起来就比较困难。
-
性能劣势: 与C, C++性能上相比,Django性能偏低,当然这是python的锅,其它python框架在流量上来后会有同样问题。
-
模板问题: django的模板实现了代码和样式完全分离,不允许模板里出现python代码,灵活度对某些程序员来说可能不够。
2. 说说看Django的请求生命周期
注:最重要的是回答用户请求并不是一下子通过URL匹配就达到相应视图,返回数据也不是一下子就返回给用户,中间要经历层层中间件。这个面试题其实考的核心是中间件。
在生产环境,你还需要很清楚地描述下图流程。
3. 请列举几个Django ORM中常用的获取数据查询集(queryset)的方法
常用方法包括filter和exclude方法。字符串模糊匹配可以使用icontains, in等多种方法。随便举几个例子:
qs1 = Article.objects.filter(title__icontains='django') qs2 = Article.objects.filter(id__range=[1,9]) qs3 = Article.objects.filter(id__in=[1, 3, 6, 7, 9]) qs4 = Article.objects.filter(author=request.user).exclude(id=1)
4. 说说看Django的Queryset有哪些特性
Django的QuerySet主要有两个特性:一是惰性的(lazy),二是自带缓存。我们来看个例子。
下例中article_list试图从数据库查询一个标题含有django的全部文章列表。
article_list = Article.objects.filter(title__contains="django")
但是当我们定义article_list的时候,Django的数据接口QuerySet并没有对数据库进行任何查询。无论你加多少过滤条件,Django都不会对数据库进行查询。只有当你需要对article_list做进一步运算时(比如打印出查询结果,判断是否存在,统计查询结果长度),Django才会真正执行对数据库的查询(见下例1)。这个过程被称为queryset的执行(evaluation)。Django这样设计的本意是尽量减少对数据库的无效操作,比如查询了结果而不用是计算资源的很大浪费。
# example 1 for article in article_list: print(article.title)
在例1中,当你遍历queryset(article_list)时,所有匹配的记录会从数据库获取。这些结果会载入内存并保存在queryset内置的cache中。这样如果你再次遍历或读取这个article_list时,Django就不需要重复查询了,这样也可以减少对数据库的查询。
5. 什么是基于函数的视图(FBV)和基于类的视图(CBV)以及各自的优点
FBV(function base views) 就是在视图里使用函数处理请求。CBV(class base views) 就是在视图里使用类处理请求。Python是一个面向对象的编程语言,如果只用函数来开发,有很多面向对象的优点就错失了(继承、封装、多态)。所以Django在后来加入了Class-Based-View,可以让我们用类写View,这样做的优点主要下面两种:
-
提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
-
可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
当然基于函数的视图也有自己的优点,比如对新手更友好。
6. 如何给基于类的视图(CBV)使用装饰器
需要借助django.utils模块的method_decorator方法实现,它还支持decorators列表, 如下所示:
from django.utils.decorators import method_decorator decorators = [login_required, check_user_permission] @method_decorator(decorators, name='dispatch') class ArticleCreateView(CreateView): model = Article form_class = ArticleForm template_name = 'blog/article_manage_form.html'
7. 说说看使用基于类的视图(CBV)时get_queryset, get_context_data和get_object方法的作用
get_queryset()方法
正如其名,该方法可以返回一个量身定制的对象列表。当我们使用Django自带的ListView展示所有对象列表时,ListView默认会返回Model.objects.all()。
# Create your views here. from django.views.generic import ListView from .models import Article class IndexView(ListView): model = Article
然而这可能不是我们所需要的。当我们希望只展示作者自己发表的文章列表且按文章发布时间逆序排列时,我们就可以通过更具体的get_queryset方法来返回一个我们想要显示的对象列表。
# Create your views here. from django.views.generic import ListView from .models import Article from django.utils import timezone class IndexView(ListView): template_name = 'blog/article_list.html' context_object_name = 'latest_articles' def get_queryset(self): return Article.objects.filter(author = self.request.user).order_by('-pub_date')
get_context_data()
get_context_data可以用于给模板传递模型以外的内容或参数,非常有用。例如现在的时间并不属于Article模型。如果你想把现在的时间传递给模板,你还可以通过重写get_context_data方法(如下图所示)。因为调用了父类的方法,
# Create your views here. from django.views.generic import ListView from .models import Article from django.utils import timezone class IndexView(ListView): queryset = Article.objects.all().order_by("-pub_date") template_name = 'blog/article_list.html' context_object_name = 'latest_articles' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['now'] = timezone.now() #只有这行代码有用 return context
get_object()方法
DetailView和EditView都是从URL根据pk或其它参数调取一个对象来进行后续操作。下面代码通过DetailView展示一篇文章的详细信息。
# Create your views here. from django.views.generic import DetailView from django.http import Http404 from .models import Article from django.utils import timezone class ArticleDetailView(DetailView): queryset = Article.objects.all().order_by("-pub_date") #等同于model = Article template_name = 'blog/article_detail.html' context_object_name = 'article'
然而上述代码可能满足不了你的需求。比如你希望一个用户只能查看或编辑自己发表的文章对象。当用户查看别人的对象时,返回http 404错误。这时候你可以通过更具体的get_object()方法来返回一个更具体的对象。代码如下:
from django.views.generic import DetailView from django.http import Http404 from .models import Article from django.utils import timezone class ArticleDetailView(DetailView): queryset = Article.objects.all().order_by("-pub_date") template_name = 'blog/article_detail.html' context_object_name = 'article' def get_object(self, queryset=None): obj = super().get_object(queryset=queryset) if obj.author != self.request.user: raise Http404() return obj
8. 你能列举几个减少数据库查询次数的方法吗?
-
利用Django queryset的惰性和自带缓存的特性
-
使用select_related和prefetch_related方法在数据库层面进行Join操作
-
使用缓存