在上一章中,你使用模型继承和通用关系来创建灵活的课程内容模型。你还使用基于类的视图,表单集和AJAX排序内容创建了一个课程管理系统。在本章中,你会学习学习以下内容:
我们从创建课程目录开始,让学生可以浏览已存在的课程,并且可以报名参加。 11.1 显示课程对于我们的课程目录,我们需要构建以下功能:
编辑 from .models import Subjectfrom django.db.models import Countclass CourseListView(TemplateResponseMixin, View): model = Course template_name = 'courses/course/list.html' def get(self, request, subject=None): subjects = Subject.objects.annotate( total_courses=Count('courses') ) courses = Course.objects.annotate( total_modules=Count('modules') ) if subject: subject = get_object_or_404(Subject, slug=subject) courses = courses.filter(subject=subject) return self.render_to_response({ 'subjects': subjects, 'subject': subject, 'courses': courses }) 这是
让我们创建一个详情视图,显示单个课程的概述。在 from django.views.generic.detail import DetailViewclass CourseDetailView(DetailView): model = Course template_name = 'courses/course/detail.html' 这个视图从Django提供的通用 编辑 from courses.views import CourseListViewurlpatterns = [ # ... url(r'^$', CourseListView.as_view(), name='course_list'),] 因为我们想在 编辑 url(r'^subject/(?P 我们定义了以下URL模式:
让我们为 course/ list.html detail.html 编辑 {% extends 'base.html' %}{% block title %} {% if subject %} {{ subject.title }} courses {% else %} All courses {% endif %}{% endblock title %}{% block content %} 这是显示可用课程列表的模板。我们创建了一个HTML列表显示所有 使用 左侧边栏包括所有主题,已经每个主题的课程总数。你可以点击任何一个主题来过滤显示的课程。 编辑 {% extends 'base.html' %}{% block title %} {{ object.title }}{% endblock title %}{% block content %} {% with subject=course.subject %} 在这个模板中,我们显示单个课程的概述和详情。在浏览器中打开 我们已经创建了展示课程的公开区域。接下来,让我们允许用户注册为学生,并报名参加课程。 11.2 添加学生注册使用以下命令创建一个新应用: python manage.py startapp students 编辑 INSTALLED_APPS = [ # ... 'students',] 11.2.1 创建学生注册视图编辑 from django.core.urlresolvers import reverse_lazyfrom django.views.generic.edit import CreateViewfrom django.contrib.auth.forms import UserCreationFormfrom django.contrib.auth import authenticate, loginclass StudentRegistrationView(CreateView): template_name = 'students/student/registration.html' form_class = UserCreationForm success_url = reverse_lazy('student_course_list') def form_valid(self, form): result = super().form_valid(form) cd = form.cleaned_data user = authenticate( username=cd['username'], password=cd['password'] ) login(self.request, user) return result 这个视图允许学生在我们网站上注册。我们使用通用的
当提交了有效的表单数据后,会执行 在 from django.conf.urls import urlfrom . import viewsurlpatterns = [ url(r'^register/$', views.StudentRegistrationView.as_view(), name='student_registration'),] 编辑 url(r'^students/', include('students.urls')), 在 templates/ students/ student/ registration.html 编辑 {% extends 'base.html' %}{% block title %} Sign up{% endblock title %}{% block content %} 最后,编辑 from django.core.urlresolvers import reverse_lazyLOGIN_REDIRECT_URL = reverse_lazy('student_course_list') 一次成功登录后,如果请求中没有 启动开发服务器,并在浏览器中打开 11.2.2 报名参加课程用户创建账户之后,他们应该可以报名参加课程。为了存储花名册,我们需要在 students = models.ManyToManyField(User, related_name='courses_joined', blank=True) 在终端中执行以下命令,为这个修改创建一个数据库迁移: python manage.py makemigrations 你会看到类似这样的输出: Migrations for 'courses': courses/migrations/0004_course_students.py - Add field students to course 然后执行以下命令,应用数据库迁移: python manage.py migrate 你会看到以下输出: Operations to perform: Apply all migrations: admin, auth, contenttypes, courses, sessionsRunning migrations: Applying courses.0004_course_students... OK 现在,我们可以用学生参加的课程关联到学生。让我们创建学生参加课程的功能。 在 from django import formsfrom courses.models import Courseclass CourseEnrollForm(forms.Form): course = forms.ModelChoiceField( queryset=Course.objects.all(), widget=forms.HiddenInput ) 我们将把这个表单用于学生报名。 编辑 from django.views.generic.edit import FormViewfrom braces.views import LoginRequiredMixinfrom .forms import CourseEnrollFormclass StudentEnrollCourseView(LoginRequiredMixin, FormView): course = None form_class = CourseEnrollForm def form_valid(self, form): self.course = form.cleaned_data['course'] self.course.students.add(self.request.user) return super().form_valid(form) def get_success_url(self): return reverse_lazy( 'student_course_detail', args=[self.course.id] ) 这是
编辑 url(r'^enroll-course/$', views.StudentEnrollCourseView.as_view(), name='student_enroll_course'), 让我们在课程概述页面添加报名按钮表单。编辑 from students.forms import CourseEnrollFormclass CourseDetailView(DetailView): model = Course template_name = 'courses/course/detail.html' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['enroll_form'] = CourseEnrollForm( initial={'course': self.object} ) return context 我们使用 {{ object.overview|linebreaks }} 替换为以下代码: {{ object.overview|linebreaks }}{% if request.user.is_authenticated %} {% else %} Register to enroll {% endif %} 这是报名参加课程的按钮。如果用户已认证,则显示报名按钮和指向 确保开发服务器正在运行,在浏览器中打开 如果你没有登录,则会看到 11.3 访问课程内容我们需要一个视图显示学生参加的课程,以及一个访问实际课程内容的视图。编辑 from django.views.generic.list import ListViewfrom courses.models import Courseclass StudentCourseListView(LoginRequiredMixin, ListView): model = Course template_name = 'students/course/list.html' def get_queryset(self): qs = super().get_queryset() return qs.filter(students__in=[self.request.user]) 这是视图列出学生报名参加的课程。它从 然后在 from django.views.generic.detail import DetailViewclass StudentCourseDetailView(DetailView): model = Course template_name = 'students/course/detail.html' def get_queryset(self): qs = super().get_queryset() return qs.filter(students__in=[self.request.user]) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # get course object course = self.get_object() if 'module_id' in self.kwargs: # get current module context['module'] = course.modules.get( id=self.kwargs['module_id'] ) else: # get first module context['module'] = course.modules.all()[0] return context 这是 编辑 url(r'^courses/$', views.StudentCourseListView.as_view(), name='student_course_list'),url(r'^course/(?P 在 course/ detail.html list.html 编辑 {% extends 'base.html' %}{% block title %}My courses{% endblock title %}{% block content %} 这个模板显示用户报名的课程。编辑 {% extends 'base.html' %}{% block title %} {{ object.title }}{% endblock title %}{% block content %} 报名的学生通过这个模板访问一个课程的内容。首先,我们构建了一个包括所有课程单元的HTML列表,并高亮显示当前单元。然后我们迭代当前单元内容,并用 11.3.1 渲染不同类型的内容我们需要提供一种方式来渲染每种类型的内容。编辑 from django.template.loader import render_to_stringfrom django.utils.safestring import mark_safeclass ItemBase(models.Model): # ... def render(self): return render_to_string( 'courses/content/{}.html'.format(self._meta.model_name), {'item': self} ) 这个方法使用 在 content/ text.html file.html image.html video.html 编辑 {{ item.content|linebreaks|safe}} 编辑 编辑
要使用 MEDIA_URL = '/media/'MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') 记住, 编辑项目的主 from django.conf import settingsfrom django.conf.urls.static import static 然后在文件结尾添加以下代码: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 现在,你的项目可用使用开发服务器上传和保存多媒体文件了。记住,开发服务器不适合生产环境使用。我们会在下一章学习如何配置生产环境。 我们还需要一个模板渲染 使用以下命令安装这个包: pip install django-embed-video 然后编辑项目的 编辑 {% load embed_video_tags %}{% video item.url 'small' %} 现在启动开发服务器,并在浏览器中访问 非常棒!你已经为渲染课程内容创建了一个通用接口,每种课程内容都已特定方式渲染。 11.4 使用缓存框架对Web应用的HTTP请求通常需要数据库访问,数据处理和模板渲染。在处理方法,它们的开销比静态网站大多了。 当网站的流量变得越来越大,有些请求的开销可能会很大。此时缓存变得很有意义。通过在HTTP请求中缓存查询,计算结果或者渲染的内容,你会之后的请求中避免昂贵的操作。这意味着服务端更短的响应时间和更少的处理。 Django包括一个健壮的缓存系统,允许你用不同级别的粒度缓存数据。你可以缓存单个查询,一个特定视图的输出,部分被渲染模板的内容,或者整个网站。内容会在默认时间内存储在缓存系统中。你可以为缓存的数据指定默认的超时时间。 当你的应用收到一个HTTP请求时,通常会这样使用缓存框架:
你可以在这里阅读Django缓存系统的详细信息。 11.4.1 可用的缓存后台Django自带数个缓存后台,分别是:
11.4.2 安装Memcached我们将使用Memcached后台。Memcached在内存中运行,并分配了一定数量的RAM。当分配的RAM满了之后,Memcached会移除最旧的数据。 从这里下载Memcached。如果你使用的是Linux,你可以使用以下命令安装Memcached: ./configure && make && make test && sudo make install 如果你使用的是Mac OS X,你可以使用 如果你使用的是Windows,你可以在这里找到Windows二进制版本。
安装Memcached后,打开终端,并使用以下命令启动: memcached -l 127.0.0.1:11211 Memcached默认在11211端口运行。但你可以使用 安装Memcached后,你需要安装它的Python绑定。使用以下命令完成安装: pip install python3-memcached 11.4.3 缓存设置Django提供了以下缓存设置:
可用使用
11.4.4 添加Memcached到项目中让我们为项目配置缓存。编辑 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', 'LOCATION': '127.0.0.1:11211', }} 我们使用 11.4.4.1 监控Memcached有一个 pip install git+git://github.com/zenx/django-memcache-status.git
编辑项目的 这张图片显示了缓存的使用情况。绿色代表空闲缓存,红色代表已使用的空间。如果你点击标题,则会显示Memcached实例的详细统计。 我们已经为项目设置了Memcached,并且可以监控它。让我们开始缓存数据! 11.4.5 缓存级别Django提供了以下缓存级别,按粒度的升序排列:
11.4.6 使用低级别的缓存API低级别缓存API允许你在缓存中保存任何粒度的对象。它位于 from django.core.cache import cache 这是使用默认缓存。它等价于 from django.core.cache import cachesmy_cache = caches['alias'] 让我们看看缓存API是如何工作的。使用 >>> from django.core.cache import cache>>> cache.set('musician', 'Django Reinhardt', 20) 我们访问默认缓存后台,并使用 >>> cache.get('musician')'Django Reinhardt' 我们从缓存中检索键。等待20秒,然后执行同样的代码: >>> cache.get('musician')None
让我们缓存一个QuerySet: >>> from courses.models import Subject>>> subjects = Subject.objects.all()>>> cache.set('all_subjects', subjects) 我们在 >>> cache.get('all_subjects')[ 我们将在视图中缓存一些查询。编辑 from django.core.cache import cache 在 subjects = Subject.objects.annotate( total_courses=Count('courses')) 替换为以下代码: subjects = cache.get('all_subjects')if not subjects: subjects = Subject.objects.annotate( total_courses=Count('courses') ) cache.set('all_subjects', subjects) 在这段代码中,我们首先尝试使用 启动开发服务器,并在浏览器中打开 看一眼 现在回到 11.4.6.1 基于动态数据缓存很多时候,你会想要基于动态数据缓存一些东西。这种情况下,你需要动态构建包含所有信息的键,来唯一识别缓存的数据。编辑 class CourseListView(TemplateResponseMixin, View): model = Course template_name = 'courses/course/list.html' def get(self, request, subject=None): subjects = cache.get('all_subjects') if not subjects: subjects = Subject.objects.annotate( total_courses=Count('courses') ) cache.set('all_subjects', subjects) all_courses = Course.objects.annotate( total_modules=Count('modules') ) if subject: subject = get_object_or_404(Subject, slug=subject) key = 'subject_{}_courses'.format(subject.id) courses = cache.get(key) if not courses: courses = all_courses.filter(subject=subject) cache.set(key, courses) else: courses = cache.get('all_courses') if not courses: courses = all_courses cache.set('all_courses', courses) return self.render_to_response({ 'subjects': subjects, 'subject': subject, 'courses': courses }) 这段代码中,我们缓存了所有课程和过滤主题的课程。如果没有给定主题,则使用 请注意,我们不能使用缓存的QuerySet来构建另一个QuerySet,因为我们缓存的是QuerySet的实际结果。所以我们不能这么做: courses = cache.get('all_courses')courses.filter(subject=subject) 相反,我们必须创建一个基本的QuerySet: 11.4.7 缓存模板片段缓存模板片段是一个更高级别的方法。你需要在模板中使用 {% cache 300 fragment_name %} ...{% endcache %}
编辑 {% load cache %} 然后找到以下代码: {% for content in module.contents.all %} {% with item=content.item %} 替换为下面的代码: {% cache 600 module_contents module %} {% for content in module.contents.all %} {% with item=content.item %} 我们使用
11.4.8 缓存视图你可以使用 让我们在视图中使用它。编辑 from django.views.decorators.cache import cache_page 然后在 url(r'^course/(?P 现在
11.4.81 使用整个站点缓存这是最高级别的缓存。它允许你缓存整个站点。 编辑项目的 MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.cache.FetchFromCacheMiddleware', # ...] 记住,在解析请求的过程中,中间件按给定的顺序执行;而在解析响应的过程中,则是逆序执行。 然后,在 CACHE_MIDDLEWARE_ALIAS = 'default'CACEH_MIDDLEWARE_SECONDS = 60 * 15 # 15 minutesCACHE_MIDDLEWARE_KEY_PRIFIX = 'educa' 在这些设置中,我们为缓存中间件使用默认缓存,并设置全局缓存超时为15分钟。我们还为所有缓存键指定一个前缀,来避免在多个项目中使用同一个Memcached后台时的冲突。现在我们的网站会缓存数据,并为所有GET请求返回缓存的数据。 我们已经测试了整个站点缓存功能。但是,整个站点缓存不适合我们,因为课程管理视图需要立即显示更新的数据。我们项目中最好的方式是缓存模板,或者用于显示课程内容的视图。 我们已经学习了Django提供的缓存数据的方法。你应该明智的定义缓存策略,优先考虑开销最大的QuerySet或者计算。 11.5 总结在这章中,我们为课程创建了公开的视图,并构建了一个学生注册和报名课程的系统。我们安装了Memcached,并实现了不同的缓存级别。 下一章我们会为项目构建RESTful API。 |
|