第十章 構建一個線上學習平臺(下)
10 構建一個線上學習平臺
10.5 建立內容管理系統
現在我們已經建立了一個萬能的資料模型,接下來我們會建立一個內容管理系統(CMS)。CMS允許教師建立課程,並管理它們的內容。我們需要以下功能:
- 登入到CMS
- 教師建立的課程列表
- 建立,編輯和刪除課程
- 新增單元到課程,並對它們重新排序
- 新增不同型別的內容到每個單元中,並對它們重新排序
10.5.1 新增認證系統
我們將在平臺中使用Django的認證框架。教師和學生都是Django的User
模型的例項。因此,他們可以使用django.contrib.auth
的認證檢視登入網站。
編輯educa
專案的主urls.py
檔案,並引入Django認證框架的login
logout
檢視:
from django.conf.urls import include, url
from django.contrib import admin
from django.contrib.auth import views as auth_views
urlpatterns = [
url(r'^accounts/login/$', auth_views.login, name='login'),
url(r'^accounts/logout/$', auth_views.logout, name='logout'),
url(r'^admin/' , admin.site.urls),
]
10.5.2 建立認證模板
在courses
應用目錄中建立以下檔案結構:
templates/
base.html
registration/
login.html
logged_out.html
構建認證模板之前,我們需要為專案準備基礎模板。編輯base.html
模板,並新增以下內容:
{% load staticfiles %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>{% block title %}Educa{% endblock title %}</title>
<link href="{% static "css/base.css" %}" rel="stylesheet">
</head>
<body>
<div id="header">
<a href="/" class="logo">Educa</a>
<ul class="menu">
{% if request.user.is_authenticated %}
<li><a href="{% url "logout" %}">Sign out</a></li>
{% else %}
<li><a href="{% url "login" %}">Sign in</a></li>
{% endif %}
</ul>
</div>
<div id="content">
{% block content %}{% endblock content %}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script>
$(document).ready(function() {
{% block domready %}{% endblock domready %}
});
</script>
</body>
</html>
這是基礎模板,其它模板會從它擴充套件。在這個模板中,我們定義了以下塊:
title
:其它模組用來為每個頁面新增自定義標題的塊。content
:主要的內容塊。所有擴充套件基礎模板的模板必須在這個塊中新增內容。domready
:位於jQuery的$(document).ready()
函式內。允許我們在DOM完成載入時執行程式碼。
這個模板中使用的CSS樣式位於本章例項程式碼的courses
應用的static/
目錄中。你可以把它拷貝到專案的相同位置。
編輯registration/login.html
模板,並新增以下程式碼:
{% extends "base.html" %}{% block title %}Log-in{% endblock title %}{% block content %}
<h1>Log-in</h1>
<div class="module">
{% if form.errors %}
<p>Your username and password didn't match.Please try again.</p>
{% else %}
<p>Please, user the following form to log-in:</p>
{% endif %}
<div class="login-form">
<form action="{% url "login" %}" method="post">
{{ form.as_p }}{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<p><input type="submit" value="Log-in"></p>
</form>
</div>
</div>
{% endblock content %}
這是Django的login
檢視的標準登入模板。編輯registration/logged_out.html
模板,並新增以下程式碼:
{% extends "base.html" %}{% block title %}Logged out{% endblock title %}{% block content %}
<h1>Logged out</h1>
<div class="module">
<p>
You have been successfully logged out. You can
<a href="{% url "login" %}">log-in again</a>.
</p>
</div>
{% endblock content %}
使用者登出後會顯示這個模板。執行python manage.py runserver
命令啟動開發伺服器,然後在瀏覽器中開啟http://127.0.0.1:8000/accounts/login/
,你會看到以下登入頁面:
10.5.3 建立基於類的檢視
我們將構建用於建立,編輯和刪除課程的檢視。我們將使用基於類的檢視。編輯courses
應用的views.py
檔案,並新增以下程式碼:
from django.views.generic.list import ListView
from .models import Course
class ManageCourseListView(ListView):
model = Course
template_name = 'courses/manage/course/list.html'
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(owner=self.request.user)
這是ManageCourseListView
檢視。它從Django的通用ListView
繼承。我們覆寫了檢視的get_queryset()
方法,只檢索當前使用者建立的課程。要阻止使用者編輯,更新或者刪除不是他們建立的課程,我們還需要在建立,更新和刪除檢視中覆寫get_queryset()
方法。當你需要為數個基於類的檢視提供特定行為,推薦方式是使用minxins
。
10.5.4 為基於類的檢視使用mixins
Mixins是一個類的特殊的多重繼承。你可以用它們提供常見的離散功能,把它們新增到其它mixins中,允許你定義一個類的行為。有兩種主要場景下使用mixins:
- 你想為一個類提供多個可選的特性
- 你想在數個類中使用某個特性
你可以在這裡閱讀如何在基於類的檢視中使用mixins的文件。
Django自帶幾個mixins,為基於類的檢視提供額外的功能。你可以在這裡找到所有mixins。
我們將建立包括一個常見功能的mixins類,並把它用於課程的檢視。編輯courses
應用的views.py
檔案,如下修改:
from django.core.urlresolvers import reverse_lazy
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView
from django.views.generic.edit import UpdateView
from django.views.generic.edit import DeleteView
from .models import Course
class OwnerMixin:
def get_queryset(self):
qs = super().get_queryset()
return qs.filter(owner=self.request.user)
class OwnerEditMixin:
def form_valid(self, form):
form.instance.owner = self.request.user
return super().form_valid(form)
class OwnerCourseMixin(OwnerMixin):
model = Course
class OwnerCourseEditMixin(OwnerCourseMixin, OwnerEditMixin):
fields = ['subject', 'title', 'slug', 'overview']
success_url = reverse_lazy('manage_course_list')
template_name = 'courses/manage/course/form.html'
class ManageCourseListView(OwnerCourseMixin, ListView):
template_name = 'courses/manage/course/list.html'
class CourseCreateView(OwnerCourseEditMixin, CreateView):
pass
class CourseUpdateView(OwnerCourseEditMixin, UpdateView):
pass
class CourseDeleteView(OwnerCourseMixin, DeleteView):
template_name = 'courses/manage/course/delete.html'
success_url = reverse_lazy('manage_course_list')
在這段程式碼中,我們建立了OwnerMixin
和OwnerEditMixin
兩個mixins。我們與Django提供的ListView
,CreateView
,UpdateView
和DeleteView
檢視一起使用這些mixins。OwnerMixin
實現了以下方法:
get_queryset()
:檢視用這個方法獲得基本的QuerySet。我們的mixin會覆寫這個方法,通過owner
屬性過濾物件,來檢索屬於當前使用者的物件(request.user)。
OwnerEditMixin
實現以下方法:
form_valid()
:使用Django的ModelFormMixin
的檢視會使用這個方法,比如,帶表單或者模型表單的檢視(比如CreateView
和UpdateView
)。當提交的表單有效時,會執行form_valid()
。這個方法的預設行為是儲存例項(對於模型表單),並重定向使用者到success_url
。我們覆寫這個方法,在被儲存物件的owner
屬性中自動設定當前使用者。這樣,當儲存物件時,我們自動設定了物件的owner
。
我們的OwnerMixin
類可用於與包括owner
屬性的任何模型互動的檢視。
我們還定義了一個OwnerCourseMixin
,它從OwnerMixin
繼承,併為子檢視提供以下屬性:
model
:用於QuerySet的模型。可以被所有檢視使用。
我們用以下屬性定義了一個OwnerCourseEditMixin
:
fields
:模型的這個欄位構建了CreateView
和UpdateView
檢視的模型表單。success_url
:當表單提交成功後,CreateView
和UpdateView
用它重定向使用者。
最後,我們建立從OwnerCourseMixin
繼承的檢視:
ManageCourseListView
:列出使用者建立的課程。它從OwnerCourseMixin
和ListView
繼承。CourseCreateView
:用模型表單建立一個新的Course
物件。它用在OwnerCourseEditMixin
中定義的欄位來構建模型表單,它還從CreateView
繼承。CourseUpdateView
:允許編輯一個已存在的Course
物件。它從OwnerCourseEditMixin
和UpdateView
繼承。CourseDeleteView
:從OwnerCourseMixin
和通用的DeleteView
繼承。定義了success_url
,用於刪除物件後重定向使用者。
10.5.5 使用組和許可權
我們已經建立了管理課程的基礎檢視。當前,任何使用者都可以訪問這些檢視。我們想限制這些檢視,只有教師有許可權建立和管理課程。Django的認證框架包括一個許可權系統,允許你給使用者和組分配許可權。我們將為教師使用者建立一個組,並分配建立,更新和刪除課程的許可權。
使用python manage.py runserver
命令啟動開發伺服器,並在瀏覽器中開啟http://127.0.0.1:8000/admin/auth/group/add/
,然後建立一個新的Group
物件。新增組名為Instructors
,並選擇courses
應用的所有許可權,除了Subject
模型的許可權,如下圖所示:
正如你所看到,每個模型有三個不同的許可權:Can add
,Can change
和Can delete
。為這個組選擇許可權後,點選Save
按鈕。
Django自動為模型建立許可權,但你也可以建立自定義許可權。你可以在這裡閱讀更多關於新增自定義許可權的資訊。
開啟http://127.0.0.1:8000/admin/auth/user/add/
,然後新增一個新使用者。編輯使用者,並把它新增Instructors
組,如下圖所示:
使用者從它所屬的組中繼承許可權,但你也可以使用管理站點為單個使用者新增獨立許可權。is_superuser
設定為True
的使用者自動獲得所有許可權。
10.5.5.1 限制訪問基於類的檢視
我們將限制訪問檢視,只有擁有適當許可權的使用者才可以新增,修改或刪除Course
物件。認證框架包括一個permission_required
裝飾器來限制訪問檢視。Django 1.9將會包括基於類檢視的許可權mixins。但是Django 1.8不包括它們。因此,我們將使用第三方模組django-braces
提供的許可權mixins。
譯者注:現在Django的最新版本是1.11.X。
Django-braces是一個第三方模組,其中包括一組通用的Django mixins。這些mixins為基於類的檢視提供了額外的特性。你可以在這裡檢視django-braces提供的所有mixins。
使用pip
命令安裝django-braces:
pip install django-braces
我們將使用django-braces的兩個mixins來限制訪問檢視:
LoginRequiredMixin
:重複login_required
裝飾器的功能。PermissionRequiredMixin
:允許有特定許可權的使用者訪問檢視。記住,超級使用者自動獲得所有許可權。
編輯courses
應用的views.py
檔案,新增以下匯入:
from braces.views import LoginRequiredMixin
from braces.views import PermissionRequiredMixin
讓OwnerCourseMixin
從LoginRequiredMixin
繼承:
class OwnerCourseMixin(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
屬性中之指定的許可權。現在只有合適許可權的使用者可以訪問我們的檢視。
讓我們為這些檢視建立URL。在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/', admin.site.urls),
url(r'^course/', include('courses.urls')),
]
我們需要為這些檢視建立模板。在courses
應用的templates/
目錄中建立以下目錄和檔案:
courses/
manage/
course/
list.html
form.html
delete.html
編輯courses/manage/course/list.html
模板,並新增以下程式碼:
{% extends "base.html" %}{% block title %}My courses{% endblock title %}{% 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 content %}
這是ManageCourseListView
檢視的模板。在這個模板中,我們列出了當前使用者建立的課程。我們包括了編輯或刪除每個課程的連結,和一個建立新課程的連結。
使用python manage.py runserver
命令啟動開發伺服器。在瀏覽器中開啟http://127.0.0.1:8000/accounts/login/?next=/course/mine/
,並用屬於Instructors
組的使用者登入。登入後,你會重定向到http://127.0.0.1:8000/course/mine/
,如下所示:
這個頁面會顯示當前使用者建立的所有課程。
讓我們建立模板,顯示建立和更新課程檢視的表單。編輯courses/manage/course/form.html
模板,並新增以下程式碼:
{% extends "base.html" %}{% block title %}{% if object %}
Edit course "{{ object.title }}"
{% else %}
Create a new course
{% endif %}{% endblock title %}{% 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 content %}
form.html
模板用於CourseCreateView
和CourseUpdateView
檢視。在這個模板中,我們檢查上下文是否存在object
變數。如果上下文中存在object
,我們已經正在更新一個已存在課程,並在頁面標題使用它。否則,我們建立一個新的Course
物件。
在瀏覽器中開啟http://127.0.0.1:8000/course/mine/
,然後點選Create new course
。你會看到以下頁面:
填寫表單,然後點選Save course
按鈕。課程會被儲存,並且你會被重定向到課程列表頁面,如下圖所示:
然後點選你剛建立的課程的Edit
連結。你會再次看到表單,但這次你在編輯已存在的Course
物件,而不是建立一個新的。
最後,編輯courses/manage/course/delete.html
模板,並新增以下程式碼:
{% extends "base.html" %}{% block title %}Delete course{% endblock title %}{% 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 content %}
這是CourseDeleteView
檢視的模板。這個檢視從Django提供的DeleteView
檢視繼承,它希望使用者確認是否刪除一個物件。
開啟你的瀏覽器,並點選課程的Delete
連結。你會看到以下確認頁面:
點選CONFIRM
按鈕。課程會被刪除,你會再次被重定向到課程列表頁面。
現在教師可以建立,編輯和刪除課程。下一步,我們將給教師提供一個內容管理系統,為課程新增單元和內容。我們從管理課程單元開始。
10.5.6 使用表單集
Django自帶一個抽象層,可以在同一個頁面使用多個表單。這些表單組稱為表單集(formsets)。表單集管理多個確定的Form
或ModelForm
例項。所有表單會一次性提交,表單集會負責處理一些事情,比如顯示的初始表單數量,限制最大的提交表單數量,以及驗證所有表單。
表單集包括一個is_valide()
方法,可以一次驗證所有表單。你還可以為表單提供初始資料,並指定顯示多少額外的空表單。
10.5.6.1 管理課程單元
因為一個課程分為多個單元,所以這裡可以使用表單集。在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
表單集。我們用Django提供的inlineformset_factory()
函式構建它。內聯表單集(inline formsets)是表單集之上的一個小抽象,可以簡化關聯物件的使用。這個函式允許我們動態構建一個模型表單集,把Module
物件關聯到一個Course
物件。
我們使用以下引數構建表單集:
fields
:在表單集的每個表單中包括的欄位。extra
:允許我們在表單集中設定兩個額外的空表單。can_delete
:如果設定為True
,Django會為每個表單包括一個布林值欄位,該欄位渲染為一個複選框。它允許你標記物件為刪除。
編輯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().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
檢視處理表單集來新增,更新和刪除指定課程的單元。這個檢視從以下mixins和檢視繼承:
TemplateResponseMixin
:這個mixin負責渲染模板,並返回一個HTTP響應。它需要一個template_name
屬性,指定被渲染的模板,並提供render_to_response()
方法,傳入上下文引數,並渲染模板。View
:Django提供的基礎的基於類的檢視。
在這個檢視中,我們實現了以下方法:
get_formset()
:我們定義這個方法,避免構建表單集的重複程式碼。我們用可選的data
為給定的Course
物件建立ModuleFormSet
物件。dispatch()
:這個方法由View
類提供。它接收一個HTTP請求作為引數,並嘗試委託到與使用的HTTP方法匹配的小寫方法:GET請求委託到get()
方法,POST請求委託到post()
方法。在這個方法中,我們用get_object_or_404()
函式獲得屬於當前使用者,並且ID等於id
引數的Course
物件。因為GET和POST請求都需要檢索課程,所以我們在dispatch()
方法中包括這段程式碼。我們把它儲存在檢視的course
屬性,讓其它方法也可以訪問。get()
:GET請求時執行的方法。我們構建一個空的ModuleFormSet
表單集,並使用TemplateResponseMixin
提供的render_to_response()
方法,把當前Course
物件和表單集渲染到模板中。post()
:POST請求時執行的方法。在這個方法中,我們執行以下操作:
- 我們用提交的資料構建一個
ModuleFormSet
例項。 - 我們執行表單集的
is_valid()
方法,驗證表單集的所有表單。 - 如果表單集有效,則呼叫
save()
方法儲存它。此時,新增,更新或者標記刪除的單元等任何修改都會應用到資料庫中。然後我們重定向使用者到manage_course_list
URL。如果表單集無效,則渲染顯示錯誤的模板。
- 我們用提交的資料構建一個
編輯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 title %}{% 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 content %}
在這個模板中,我們建立了一個<form>
元素,其中包括我們的表單集。我們還用{{ formset.management_form }}
變數為表單集包括了管理表單。管理表單儲存隱藏的欄位,用於控制表單的初始數量,總數量,最小數量和最大數量。正如你所看到的,建立表單集很簡單。
編輯courses/manage/course/list.html
模板,在課程編輯和刪除連結下面,為course_module_update
URL新增以下連結:
<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
連結,你會看到如圖所示的表單集:
表單集中包括課程中每個Module
物件的表單。在這些表單之後,顯示了兩個額外的空表單,這是因為我們為ModuleFormSet
設定了extra=2
。當你儲存表單集時,Django會包括另外兩個額外欄位來新增新單元。
10.5.7 新增內容到課程單元
現在我們需要一種新增內容到課程單元的方式。我們有四種不同型別的內容:文字,視訊,圖片和檔案。我們可以考慮建立四個不同的檢視,來為每種模型建立內容。但是我們會用更通用的方法:建立一個可以處理建立或更新任何內容模型物件的檢視。
編輯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().dispatch(request, module_id, model_name, id)
這是ContentCreateUpdateView
的第一部分。它允許我們建立和更新不同模型的內容。這個檢視定義了以下方法:
get_model()
:在這裡,我們檢查給定的模型名稱是否為四種內容模型之一:文字,視訊,圖片或檔案。然後我們用Django的apps.get_model()
獲得給定模型名的實際類。如果給定的模型名不是四種之一,則返回None
。get_form()
:我們用表單框架的modelform_factory()
函式動態構建表單。因為我們要為Text
,Video
,Image
和File
模型構建表單,所以我們使用exclude
引數指定要從表單中排出的欄位,而讓剩下的所有欄位自動包括在表單中。這樣我們不用根據模型來包括欄位。dispatch()
:它接收以下URL引數,並用類屬性儲存相應的單元,模型和內容物件:
module_id
:內容會關聯的單元的ID。model_name
:內容建立或更新的模型名。id
:被更新的物件的ID。建立新物件時為None。
在ContentCreateUpdateView
類中新增以下get()
和post()
方法:
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,
item=obj
)
return redirect('module_content_list', self.module.id)
return self.render_to_response({
'form': form,
'object': self.obj
})
這些方法分別是:
get()
:收到GET請求時執行。我們為被更新的Text
,Video
,Image
或者File
例項構建模型表單。否則我們不會傳遞例項來建立新物件,因為如果沒有提供id
,則self.obj
為None。post()
:收到POST請求時執行。我們傳遞提交的所有資料和檔案來構建模型表單。然後驗證它。如果表單有效,我們建立一個新物件,並在儲存到資料庫之前把request.user
作為它的所有者。我們檢查id
引數。如果沒有提供id
,我們知道使用者正在建立新物件,而不是更新已存在的物件。如果這是一個新物件,我們為給定的單元建立一個Content
物件,並把它關聯到新的內容。
編輯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模式分別是:
module_content_create
:用於建立文字,視訊,圖片或者檔案物件,並把它們新增到一個單元。它包括module_id
和model_name
引數。第一個引數允許我們把新內容物件連結到給定的單元。第二個引數指定了構建表單的內容模型。module_content_update
:用於更新已存在的文字,檢視,圖片或者檔案物件。它包括module_id
和model_name
引數,以及被更新的內容的id
引數。
在courses/manage/