技术文“译”站前端程序员

《Django By Example》第十章 下 中文 翻译 (

2017-03-23  本文已影响1915人  夜夜月

全文链接

第一章 创建一个blog应用
第二章 使用高级特性来增强你的blog
第三章 扩展你的blog应用
第四章上 创建一个社交网站
第四章下 创建一个社交网站
第五章 在你的网站中分享内容
第六章 跟踪用户动作
第七章 建立一个在线商店
第八章 管理付款和订单
第九章上 扩展你的商店
第九章下 扩展你的商店
第十章上 创建一个在线学习平台
第十章下 创建一个在线学习平台
第十一章 缓存内容
第十二章 构建一个API

书籍出处:https://www.packtpub.com/web-development/django-example
原作者:Antonio Melé

(译者注:翻译本章过程中几次想放弃,但是既然都到第十章了,怎么能放弃!)

(以下是第十章下半章)

使用组和权限

我们已经创建了基础的视图来管理课程。但是目前所有的用户都可以使用这些视图。我们想要限制这些视图从而只有教师有权限去创建和管理课程。Django认证框架包含一个权限系统允许你去分配权限给用户和组。我们将要创建一个组给教师用户并且分配权限给他们可以创建,更新以及删除课程。

使用命令python manage.py runserver命令运行开发服务器并且在你的浏览器中打开 http://127.0.0.1:8000/admin/auth/group/add/ 来创建一个新的Group对象。添加的组名为Instructors,然后选择courses应用中的除了Subject模型的所有权限,如下所示:

django-10-3

如你所见,有三种不同的权限给每个模型:Can addcan change以及Can delete。选择好给这个组的权限之后,点击Save按钮。

Django会自动给模型创建权限,但是你也可以创建定制的权限。你可以找到更多关于添加定制权限的文档,通过访问 https://docs.djangoproject.com/en/1.8/topics/auth/customizing/#custom-permissions

打开 http://127.0.0.1:8000/admin/auth/user/add/ 然后创建一个新用户。编辑这个用户然后添加Instructors组给这个用户如下所示:

django-10-4

用户会继承他们所在组的权限,但是你也可以在管理平台上添加单独的权限给一个指定的用户。当用户的is_superuser设置为True的时候会自动拥有所有的权限。

限制使用基于类的视图

我们将要限制使用基于类的视图从而只有那些拥有合适权限的用户才能添加,修改,或者删除Course对象认证框架包含一个permission_required装饰器来限制对视图的使用。Django 1.9将会包含权限mixins给基于类的视图(译者注:到目前为止,Django版本已经是1.10.6)。然而,Django1.8还没有包含它们。因此,我们将要第三方模块提供的权限mixins,该第三方模块名为 django-braces(译者注:。。。。。。下面我是不是可以不用翻译了。。。。。。)

使用django-braces的mixins

Django-braces是一个第三方的模块,它包含一个通用mixins的采集给Django使用。这些mixins提供额外的特性给基于类的视图。你可以看到django-braces提供的所有mixins列表,通过访问 http://django-braces.readthedocs.org/en/latest/

使用pip命令安装django-braces:

pip install django-braces==1.8.1

我们将要使用以下两个django-braces提供的mixinx来限制视图的使用:

编辑courses应用的views.py文件,添加如下导入:

from braces.views import LoginRequiredMixin,
                            PermissionRequiredMixin

像下面一样让OwnerCourseEditMixin继承LoginRequiredMixin

class OwnerCourseEditMixin(OwnerMixin, LoginRequiredMixin): 
    model = Course
    fields = ['subject', 'title', 'slug', 'overview']
    success_url = reverse_lazy('manage_course_list')

之后,添加一个permission_required属性给创建,跟新,以及删除视图,如下所示:

class CourseCreateView(PermissionRequiredMixin,
                       OwnerCourseEditMixin,
                       CreateView):
    permission_required = 'courses.add_course'
   
class CourseUpdateView(PermissionRequiredMixin,
                       OwnerCourseEditMixin,
                       UpdateView):
    template_name = 'courses/manage/course/form.html'
    permission_required = 'courses.change_course'
    
class CourseDeleteView(PermissionRequiredMixin, 
                       OwnerCourseMixin,
                       DeleteView):
    template_name = 'courses/manage/course/delete.html'
    success_url = reverse_lazy('manage_course_list')
    permission_required = 'courses.delete_course'

PermissionRequiredMixin会在用户使用视图的时候检查该用户是否有指定在permission_required属性中的权限。我们的视图现在只准许有适当权限的用户使用。

让我们给以上视图创建URLs。在courses应用目录中创建新的文件命名为urls.py。添加以下代码:

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^mine/$',
        views.ManageCourseListView.as_view(),
        name='manage_course_list'),
    url(r'^create/$',
        views.CourseCreateView.as_view(),
        name='course_create'),
    url(r'^(?P<pk>\d+)/edit/$',
        views.CourseUpdateView.as_view(),
        name='course_edit'),
    url(r'^(?P<pk>\d+)/delete/$',
        views.CourseDeleteView.as_view(),
        name='course_delete'),
]

以上的URL模式是给列表,创建,编辑以及删除课程试图使用的。编辑educa项目的主urls.py文件然后包含courses应用的URL模式,如下所示:

urlpatterns = [
    url(r'^accounts/login/$', auth_views.login, name='login'),
    url(r'^accounts/logout/$', auth_views.logout, name='logout'),
    url(r'^admin/', include(admin.site.urls)),
    url(r'^course/', include('courses.urls')),
]

我们需要给这些视图创建模块。在courses应用中创建以下目录以及文件:

courses/
       manage/
           course/
               list.html
               form.html
               delete.html

编辑 courses/manage/course/list.html模板并且添加如下代码:

{% extends "base.html" %}

{% block title %}My courses{% endblock %}

{% block content %}
     <h1>My courses</h1>
     <div class="module">
       {% for course in object_list %}
         <div class="course-info">
           <h3>{{ course.title }}</h3>
           <p>
             <a href="{% url "course_edit" course.id %}">Edit</a>
             <a href="{% url "course_delete" course.id %}">Delete</a>
           </p>
         </div>
       {% empty %}
         <p>You haven't created any courses yet.</p>
       {% endfor %}
       <p>
         <a href="{% url "course_create" %}" class="button">Create new course</a>
         </p>
    </div>
{% endblock %}

这是ManageCourseListView视图的模板。在这个模板中,我们通过当前用户来排列课程。我们给每个课程都包含了编辑或者删除链接,以及一个创建新课程的链接。

使用命令python manage.py runserver命令运行开发服务器。在你的浏览器中打开 http://127.0.0.1:8000/accounts/login/?next=/course/mine/ 然后使用Instrctors组中的一个用户进行登录。登录完成后,你会被重定向到 http://127.0.0.1:8000/course/mine/ 并且你会看到如下页面:

django-10-5

这个页面将会展示所有当前用户创建的课程。

让我们创建一个给创建和更新课程视图使用的模板,该模板用来展示表单。编辑courses/manage/course/form.html模板并且输入以下代码:

   {% extends "base.html" %}
   {% block title %}
     {% if object %}
       Edit course "{{ object.title }}"
     {% else %}
       Create a new course
     {% endif %}
   {% endblock %}
   {% block content %}
     <h1>
       {% if object %}
         Edit course "{{ object.title }}"
       {% else %}
         Create a new course
       {% endif %}
     </h1>
     <div class="module">
       <h2>Course info</h2>
       <form action="." method="post">
         {{ form.as_p }}
         {% csrf_token %}
         <p><input type="submit" value="Save course"></p>
       </form>
     </div>
   {% endblock %}  

这个form.html模板被CoursecREATEvIEWcourseUpdateView视图使用。在这个模板中,我们检查是否有个object变量在上下文环境中。如果object存在上下文环境中,我们就知道我们正在更新一个存在的课程,并且我们在页面标题中使用它。如果不存在,我们就要创建一个新的Course对象。

在你的浏览器中打开 http://127.0.0.1:8000/course/mine/ 然后点击Create new course按钮。你会看到如下页面:

django-10-6

填写好表单内容然后点击Save course按钮。这个课程将会被保存并且你将会被重定向到课程列表页面。它看上去如下所示:

django-10-7

之后,点击你刚才创建的课程的Edit链接。你将会再次看到表单,但是这一次你将编辑一个已经存在的Course对象而不是创建新课程。

最后,编辑courses/manage/course/delete.html模板然后添加以下代码:

   {% extends "base.html" %}
   {% block title %}Delete course{% endblock %}
   {% block content %}
     <h1>Delete course "{{ object.title }}"</h1>
     <div class="module">
       <form action="" method="post">
         {% csrf_token %}
         <p>Are you sure you want to delete "{{ object }}"?</p>
         <input type="submit" class"button" value="Confirm">
       </form>
     </div>
   {% endblock %}

这个模板是给CourseDeleteView视图使用的。这个视图从Django提供的DeleteView视图继承而来,DeleteView视图期望用户确认删除一个对象。

打开你的浏览器,点击你的课程的Delete链接。你会看到如下确认页面:

django-10-8

点击CONFIRM按钮。这个课程将会被删除并且你再次会被重定向到课程列表页面。

教师们现在可以创建,编辑,以及删除课程。下一步,我们需要提供他们一个内容管理系统来给课程添加模块以及内容。我们将从管理课程模块开始。

使用formsets

Django自带一个抽象层用于在同一个页面中使用多个表单。这些表单的组合成为formsets。formsets能管理多个Form或者ModelForm表单实例。所有的表单都可以一次性提交并且formset会照顾到一些事情,例如,表单的初始化数据展示,限制表单能够提交的最大数字,以及所有表单的验证。

formsets包含一个is_valid()方法来一次性验证所有表单。你还可以提供初始数据给表单以及指定展示任意多的额外的空的表单。

你可以学习到更多关于formsets,通过访问 https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#model-formsets

管理课程模块

由于课程会被分为可变数量的模块,因此在这里使用formets是有意义的。在courses应用目录下创建一个forms.py文件,然后添加以下代码:

from django import forms
from django.forms.models import inlineformset_factory
from .models import Course, Module

ModuleFormSet = inlineformset_factory(Course,
                                         Module,
                                         fields=['title',
                                                 'description'],
                                         extra=2,
                                         can_delete=True)

以上就是ModuleFormSet formset。我们使用Django提供的inlineformset_factory()函数来构建它。内联formsets是在formsets之上的一个小抽象,用于方便被关联对象的操作。这个函数允许我们去给关联到一个Course对象的Module对象动态的构建一个模型formset。

我们使用以下参数去构建formset:

编辑courses应用的views.py文件并且添加如下代码:

from django.shortcuts import redirect, get_object_or_404
from django.views.generic.base import TemplateResponseMixin, View
from .forms import ModuleFormSet

class CourseModuleUpdateView(TemplateResponseMixin, View):
    template_name = 'courses/manage/module/formset.html'
    course = None
    
    def get_formset(self, data=None):
        return ModuleFormSet(instance=self.course,data=data)

    def dispatch(self, request, pk):
        self.course = get_object_or_404(Course,
                                        id=pk,
                                        owner=request.user)
        return super(CourseModuleUpdateView,
                     self).dispatch(request, pk)
    
    def get(self, request, *args, **kwargs):
        formset = self.get_formset()
        return self.render_to_response({'course': self.course,
                                        'formset': formset})
    
    def post(self, request, *args, **kwargs):
        formset = self.get_formset(data=request.POST)
        if formset.is_valid():
            formset.save()
            return redirect('manage_course_list')
        return self.render_to_response({'course': self.course,
                                        'formset': formset})        

CourseModuleUpdateView视图控制formset给一个指定的课程添加,更新,以及删除模块。这个视图继承自以下的mixins和视图:

在这个视图中,我们导入以下方法:

编辑courses应用的urls.py文件,添加以下URL模式:

url(r'^(?P<pk>\d+)/module/$',
    views.CourseModuleUpdateView.as_view(),
    name='course_module_update'),

courses/manage/模板目录中创建一个新的目录命名为module。创建一个courses/manage/module/formset.html模板并且添加以下代码:

{% extends "base.html" %}

{% block title %}
     Edit "{{ course.title }}"
{% endblock %}

{% block content %}
     <h1>Edit "{{ course.title }}"</h1>
     <div class="module">
       <h2>Course modules</h2>
       <form action="" method="post">
         {{ formset }}
         {{ formset.management_form }}
         {% csrf_token %}
         <input type="submit" class="button" value="Save modules">
       </form>
     </div>
{% endblock %}

在这个模板中,我们创建一个<form>HTML元素,在其中我们包含我们的formset。我们还通过变量{{ formset.management_form }}包含给formset使用的管理表单。这个管理表单包含隐藏的字段去控制保单的初始化,总数,最小值和最大值。如你所见,创建一个formset非常容易。

编辑courses/manage/course/list.html模板并且在课程编辑和删除链接下方添加以下链接给course_module_update使用:

<a href="{% url "course_edit" course.id %}">Edit</a>
<a href="{% url "course_delete" course.id %}">Delete</a>
<a href="{% url "course_module_update" course.id %}">Edit modules</a>

我们已经包含了用来编辑课程模板的链接。在你浏览器中打开 http://127.0.0.1:8000/course/mine/ 然后选择一个课程点击对应的Edit modules链接。你会看到一个如下的formset:

django-10-9

这个formset包含所有在这个课程中存在的Module对象的表单。在这些表单之后,有两个空的额外的表单会被展示因为我们给ModuleFormSet设置extra=2。当你保存这个formset的时候,Django将会包含另外两个额外的字段来添加新的模块。

添加内容给课程模块

现在,我们需要一个方法来添加内容给课程模块。我们有四种不同的内容类型:文本,视频,图片以及文件。我们可以考虑创建四个不同的视图去保存内容,给每个模型都对应上一个视图。然而,我们将采取更通用的方法,并创建一个处理创建或更新任何内容模型的对象的视图。

编辑courses应用的views.py文件并且添加如下代码:

from django.forms.models import modelform_factory
from django.apps import apps
from .models import Module, Content

class ContentCreateUpdateView(TemplateResponseMixin, View):
    module = None
    model = None
    obj = None
    template_name = 'courses/manage/content/form.html'
    
    def get_model(self, model_name):
        if model_name in ['text', 'video', 'image', 'file']:
            return apps.get_model(app_label='courses',
                                     model_name=model_name)
        return None
    
    def get_form(self, model, *args, **kwargs):
        Form = modelform_factory(model, exclude=['owner',
                                                    'order',
                                                    'created',
                                                    'updated'])
        return Form(*args, **kwargs)
    
    def dispatch(self, request, module_id, model_name, id=None):
        self.module = get_object_or_404(Module,
                                        id=module_id,
                                    course__owner=request.user)
        self.model = self.get_model(model_name)
        if id:
            self.obj = get_object_or_404(self.model,
                                        id=id,
                                        owner=request.user)
        return super(ContentCreateUpdateView,
              self).dispatch(request, module_id, model_name, id)

以上是ContentCreateUpdateView视图的第一部分。这个视图允许我们去创建和更新不同模块的内容。这个视图定义了以下方法:

添加以下get()post()方法给ContentCreateUpdateView

def get(self, request, module_id, model_name, id=None):
    form = self.get_form(self.model, instance=self.obj)
    return self.render_to_response({'form': form,
                                       'object': self.obj})
                                       
def post(self, request, module_id, model_name, id=None):
    form = self.get_form(self.model,
                            instance=self.obj,
                            data=request.POST,
                            files=request.FILES)
    if form.is_valid():
        obj = form.save(commit=False)
        obj.owner = request.user
        obj.save()
        if not id:
            # new content
            Content.objects.create(module=self.module,
        return redirect('module_content_list', self.module.id)
    return self.render_to_response({'form': form,
                                       'object': self.obj})

以上方法如下所示:

编辑courses应用的urls.py文件禀帖添加以下URL模式:

url(r'^module/(?P<module_id>\d+)/content/(?P<model_name>\w+)/create/$',
    views.ContentCreateUpdateView.as_view(),
    name='module_content_create'),
url(r'^module/(?P<module_id>\d+)/content/(?P<model_name>\w+)/(?P<id>\d+)/$',
    views.ContentCreateUpdateView.as_view(),
    name='module_content_update'),

以上新的URL模式如下:

courses/manage/模板目录下创建新的目录命名为content。创建模板courses/manage/content/form.html并且添加以下代码:

   {% extends "base.html" %}
   
   {% block title %}
     {% if object %}
       Edit content "{{ object.title }}"
     {% else %}
       Add a new content
     {% endif %}
   {% endblock %}
   
   {% block content %}
     <h1>
       {% if object %}
         Edit content "{{ object.title }}"
       {% else %}
         Add a new content
       {% endif %}
     </h1>
     <div class="module">
       <h2>Course info</h2>
       <form action="" method="post" enctype="multipart/form-data">
         {{ form.as_p }}
         {% csrf_token %}
         <p><input type="submit" value="Save content"></p>
       </form>
     </div>
   {% endblock %}

这个模板是给ContentCreateUpdateView视图使用的。在这个模板中,我们会检查是否有一个object变量在上下文环境中。如果object存在上下文环境中,我们知道我们正在更新一个已经存在的对象。如果没有,我们在创建一个新的对象。

我们在<form>HTML元素中包含enctype="multipart/form-data",因为这个表单包含一个文件上传用来给FieldImage内容模型使用。

运行开发服务器。给存在的课程创建一个模块并且在你的浏览器中打开 http://127.0.0.1:8000/course/module/6/content/image/create/ 。如果有必要,在ULR中修改模块id。你将会看到以下表单用来创建新的Image对象:

django-10-10

先不要提交表单。如果你想要尝试,它将会是失败的,因为我们还没有定义module_content_list的URL。我们一会儿就要去创建它。

我们还需要一个视图去删除内容。编辑courses应用的views.py文件,添加以下代码:

class ContentDeleteView(View):
    def post(self, request, id):
        content = get_object_or_404(Content,
                            id=id,
                            module__course__owner=request.user)
        module = content.module
        content.item.delete()
        content.delete()
        return redirect('module_content_list', module.id)

ContentDeleteView通过给予的id检索content对象,它删除关联的TextVideoImage以及File对象,并且在最后,它会删除Content对象并且重定向用户到module_content_list URL去排列其他模块的内容。

编辑courses应用的urls.py文件并且添加以下URL模式:

url(r'^content/(?P<id>\d+)/delete/$',
    views.ContentDeleteView.as_view(),
    name='module_content_delete'),

现在,教师们可以方便的创建,更新以及删除内容。

管理模块和内容

我们已经构建了用来创建,编辑以及删除课程模块和内容的视图。现在,我们需要一个视图去给一个课程显示所有的模块并且给一个指定的模块排列所有的内容。

编辑courses应用的views.py文件并且添加以下代码:

class ModuleContentListView(TemplateResponseMixin, View):
    template_name = 'courses/manage/module/content_list.html'
    
    def get(self, request, module_id):
        module = get_object_or_404(Module,
                                      id=module_id,
                                      course__owner=request.user)
                                      
        return self.render_to_response({'module': module})

以上就是ModuleContentListView视图。这个视图通过给予的id拿到Module对象该对象是属于当前的用户并且通过给予的模块渲染一个模板。

编辑courses应用的urls.py文件,添加以下URL模式:

url(r'^module/(?P<module_id>\d+)/$',
    views.ModuleContentListView.as_view(),
    name='module_content_list'),

templates/courses/manage/module/目录下创建新的模板命名为content_list.html,添加以下代码:

{% extends "base.html" %}
{% block title %}
     Module {{ module.order|add:1 }}: {{ module.title }}
{% endblock %}
{% block content %}
{% with course=module.course %}
  <h1>Course "{{ course.title }}"</h1>
  <div class="contents">
    <h3>Modules</h3>
    <ul id="modules">
      {% for m in course.modules.all %}
        <li data-id="{{ m.id }}" {% if m == module %}
        class="selected"{% endif %}>
          <a href="{% url "module_content_list" m.id %}">
            <span>
              Module <span class="order">{{ m.order|add:1 }}</span>
            </span>
            <br>
            {{ m.title }}
          </a> 
        </li>
      {% empty %}
        <li>No modules yet.</li>
      {% endfor %}
    </ul>
    <p><a href="{% url "course_module_update" course.id %}">Edit modules</a>
    </p>
  </div>
  <div class="module">
    <h2>Module {{ module.order|add:1 }}: {{ module.title }}</h2>
    <h3>Module contents:</h3>
    <div id="module-contents">
      {% for content in module.contents.all %}
        <div data-id="{{ content.id }}">
          {% with item=content.item %}
            <p>{{ item }}</p>
            <a href="#">Edit</a>
            <form action="{% url "module_content_delete" content.id %}" method="post">
              <input type="submit" value="Delete">
              {% csrf_token %}
            </form>
          {% endwith %}
        </div>
      {% empty %}
        <p>This module has no contents yet.</p>
      {% endfor %}
      </div>
       <hr>
       <h3>Add new content:</h3>
       <ul class="content-types">
         <li><a href="{% url "module_content_create" module.id "text" %}">Text</a></li>
         <li><a href="{% url "module_content_create" module.id "image" %}">Image</a></li>
         <li><a href="{% url "module_content_create" module.id "video" %}">Video</a></li>
         <li><a href="{% url "module_content_create" module.id "file" %}">File</a></li>
      </ul> 
    </div>
{% endwith %}
{% endblock %}   

这个模板展示一个课程所有的模块以及被选中的模块的内容。我们迭代课程模块并将它们展示在侧边栏。我们还迭代模块的内容并且通过content.item去获取关联的TextVideoImage以及File对象。我们还包含可以创建新的文本,视频,图片以及文件内容的链接。

我们想要知道每个item对象是哪种类型(文本,视频,图片或者文件)的对象。我们需要模型名用来构建URL去编辑对象。除此以外,我们还在模板中展示各式各样的不同的item,基于item的内容类型。我们可以通过访问对象的_meta属性来从模型的Meta类中获取一个对象的模型。尽管如此,Django不允许访问开头是下划线的变量或者属性在模板中为了编辑检索私有属性或者调用到私有方法。我们可以通过编写一个定制模板过滤器来解决这个问题。

courses应用目录下创建以下文件结构:

templatetags/
    __init__.py
    course.py

编辑course.py模块,添加以下代码:

from django import template

register = template.Library()

@register.filter
def model_name(obj):
    try:
        return obj._meta.model_name
    except AttributeError:
        return None

以上就是model_name模板过滤器。我们可以在模板中通过object|model_name应用它来给一个对象获取模型的名字。

编辑templates/courses/manage/module/content_list.html模板,在{% extends %}模板标签下添加以下内容:

{% load course %}

这样将会加载course模板标签。之后,将以下内容:

<p>{{ item }}</p>
<a href="#">Edit</a>

替换成:

<p>{{ item }} ({{ item|model_name }})</p>
<a href="{% url "module_content_update" module.id item|model_name item.id %}">Edit</a>

现在,我们在模板中展示item模型并且使用模型名曲构建编辑对象的链接。编辑courses/manage/course/list.html模板,添加一个链接给module_content_list URL,如下所示:

<a href="{% url "course_module_update" course.id %}">Edit modules</a>
{% if course.modules.count > 0 %}
 <a href="{% url "module_content_list" course.modules.first.id %}">Manage contents</a>
{% endif %}

这个新链接允许用户去访问课程的第一个模块的内容,如果有好多内容的话。

打开 http://127.0.0.1:8000/course/mine/ 然后点击一个包含最新模块的课程的Manage contents链接。你会看到如下页面:

django-10-11

当你点击左方侧边栏的一个模块上,它的内容会在主区域显示。这个模板还包含用来给展示的模块添加一个新的文本,视频,图片或者文件内容。给这个模块添加一堆不同的内容然后看下结果。这个内容将会出现在Module contents之后,如下所示:

django-10-12

重新整理模块和内容

我们需要提供一个简单的放来去重新排序课程模板和它们的内容。我们将要使用一个JavaScript drag-n-drop 控件去让我们的用户通过拖拽课程的模块来对课程模块进行重新排序。当用户完成拖拽一个模块,我们将会执行一个异步请求(AJAX)去存储新的模块顺序。

我们需要一个视图,该视图通过编译在JSON中的模块的id来检索新的对象。编辑courses应用的views.py文件,添加以下代码:

from braces.views import CsrfExemptMixin, JsonRequestResponseMixin

class ModuleOrderView(CsrfExemptMixin,
                         JsonRequestResponseMixin,
                         View):
    
    def post(self, request):
        for id, order in self.request_json.items():
            Module.objects.filter(id=id,
                course__owner=request.user).update(order=order)
        return self.render_json_response({'saved': 'OK'})

以上是ModuleOrderView。我们使用以下django-braces的mixins:

我们可以构建一个类似的视图去排序一个模块的内容。添加以下代码到views.py中:

class ContentOrderView(CsrfExemptMixin,
                          JsonRequestResponseMixin,
                          View):
    def post(self, request):
        for id, order in self.request_json.items():
            Content.objects.filter(id=id,
                          module__course__owner=request.user) \
                          .update(order=order)
        return self.render_json_response({'saved': 'OK'})

现在,编辑courses应用的urls.py文件,添加以下URL模式:

url(r'^module/order/$',
       views.ModuleOrderView.as_view(),
       name='module_order'),
url(r'^content/order/$',
       views.ContentOrderView.as_view(),
       name='content_order'),

最后,我们在模板中导入drag-n-drop功能。我们将要使用jQuery UI库来使用这个功能。jQuery UI基于jQuery构建并且它提供了一组界面交互,效果和小部件。我们将要使用它的sortable元素。首先,我们需要在基础模板中加载jQuery UI。打开courses应用下的templates/目录下的base.html文件,在加载jQuery的下方添加jQuery UI脚本,如下所示:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>

(译者注:要用以上地址,记得翻墙。。。。。或者自己直接下载)

我们在jQuery框架下加载jQuery UI。现在,编辑courses/manage/module/content_list.html模板添加以下代码在模板的底部:

{% block domready %}
   $('#modules').sortable({
       stop: function(event, ui) {
           modules_order = {};
           $('#modules').children().each(function(){
               // update the order field
               $(this).find('.order').text($(this).index() + 1);
               // associate the module's id with its order
               modules_order[$(this).data('id')] = $(this).index();
               });
               $.ajax({
                type: 'POST',
                url: '{% url "module_order" %}',
                contentType: 'application/json; charset=utf-8',
                dataType: 'json',
                data: JSON.stringify(modules_order)
                });
    }
});

$('#module-contents').sortable({
    stop: function(event, ui) {
        contents_order = {};
        $('#module-contents').children().each(function(){
            // associate the module's id with its order
            contents_order[$(this).data('id')] = $(this).index();
           });
        $.ajax({
               type: 'POST',
               url: '{% url "content_order" %}',
               contentType: 'application/json; charset=utf-8',
               dataType: 'json',
               data: JSON.stringify(contents_order),
        }); 
      }
   });
{% endblock %}  

这个JavaScripy代码在{% block domready %}区块中,因此它会被包含在我们之前定义在base.html模板中的jQuery的$(document).ready()事件中。这将保证我们的JavaScripy代码会在页面每次加载的时候都会被执行一次。我们给列在侧边栏的模块定义了一个sortable元素并且给模块内容列也定义了一个不同的。这两者有着相似的方式。在以上代码中,我们执行以下任务:

sortable元素排列内容非常类似与上者的方法。回到你的浏览器然后重载页面。现在你将可以点击并且拖动模块和内容,去重新排序它们如下所示:

django-10-13

很好!现在你可以重新排序课程模块和模块内容了。

总结

在这章中,你学习了如何创建一个多功能的内容管理系统。你使用了模型继承以及创建了一个定制模型字段。你还通过基于类的视图和mixins工作。你创建了formsets以及一个系统去管理不同类型的内容。

在下一章,你将会创建一个学生注册系统。你还将熏染不同类型的内容,并且你还会学习如何使用Django的缓存框架。

译者总结

四个字:本章真长。

上一篇下一篇

猜你喜欢

热点阅读