上篇文章讲解了系统的登陆、注册、查看和编辑个人信息、修改密码功能,本篇章继续讲解课程信息模块的相关功能以及代码实现。本篇章主要使用 Django 中的通用视图来简化系统的开发,该项目只有 project 一个 APP,当时在写项目的时候,发现两个APP 中的 models 并不能直接相互调用,因此将所有表都写入到了 models.py 文件中。
模型 models.py
# 上传图片需要安装 pillow
pip install pillow
# 安装 CKEditor
pip install django-ckeditor
# 在项目文件夹下新建 static文件夹, 下载 ckeditor 所需的 js 和 css 文件
python manage.py collectstatic
# 配置 homework/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'project',
'ckeditor',
'ckeditor_uploader',
]
#ckeditor
SITE_ID = 1
# 富文本编辑器
CKEDITOR_UPLOAD_PATH = 'homework_uploads/'
CKEDITOR_JQUERY_URL ='https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js'
CKEDITOR_IMAGE_BACKEND = 'pillow'
CKEDITOR_ALLOW_NONIMAGE_FILES = False
CKEDITOR_BROWSE_SHOW_DIRS = True
CKEDITOR_RESTRICT_BY_USER = True
CKEDITOR_RESTRICT_BY_DATE = True
# 显示代码
# 只需要在CKEDITOR_CONFIGS中加入codesnipppet的plugin即可。
# 'extraPlugins': 'codesnippet',
# 同时找到static -> ckeditor -> ckeditor -> config.js把codesnippet注册一下。
# CKEDITOR.editorConfig = function( config ) {
# // Define changes to default configuration here. For example:
# // config.language = 'fr';
# // config.uiColor = '#AADC6E';
# config.extraPlugins: "codesnippet";
# };
CKEDITOR_CONFIGS = {
'default': {
'toolbar': (['Source', '-', 'Preview', '-', ],
['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Print', 'SpellChecker', ],
['Undo', 'Redo', '-', 'Find', 'Replace', '-', 'SelectAll', 'RemoveFormat', '-',
"CodeSnippet", 'Subscript', 'Superscript'],
['NumberedList', 'BulletedList', '-', 'Blockquote'],
['Link', 'Unlink', ],
['Image', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', ],
['Format', 'Font', 'FontSize', 'TextColor', 'BGColor', ],
['Bold', 'Italic', 'Underline', 'Strike', ],
['JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
),
'extraPlugins': 'codesnippet',#显示代码
'width': 'auto',
}
}
# 模型中使用 ckeditor
from ckeditor_uploader.fields import RichTextUploadingField
TextField 改成 RichTextUploadingField
# 表单中使用 ckeditor
# 表单的输入 widget 需要改为 CKEditorUploadingWidget
from ckeditor_uploader.widgets import CKEditorUploadingWidget
# 模板中使用 {{ form.media }} 调入 ckeditor 静态文件
# project/models.py
from django.urls import reverse
from unidecode import unidecode
from django.template.defaultfilters import slugify
from datetime import datetime
import uuid
import os
from ckeditor_uploader.fields import RichTextUploadingField
'''
自定义上传文件的存储路径
'''
def user_directory_path(instance, filename):
ext = filename.split('.')[-1]
filename = '{}.{}'.format(uuid.uuid4().hex[:10], ext)
sub_folder = 'file'
if ext.lower() in ["jpg", "png", "gif"]:
sub_folder = "avatar"
if ext.lower() in ["pdf", "docx","xlsx"]:
sub_folder = "document"
# instance 将表中的某一属性相关联
return os.path.join(str(instance.course.teacher.id), sub_folder, filename)
class Course(models.Model):
OPENED_CHOICES = (
(0,'公开'),
(1,'不公开')
)
cname = models.CharField(verbose_name='课程名称', max_length=50,null=False)
classes = models.CharField(verbose_name='班级',default='', max_length=50)
description = models.TextField(verbose_name='课程描述',)
opened = models.SmallIntegerField('公开状态',choices=OPENED_CHOICES,default=0)
teacher = models.ForeignKey(Teacher,related_name="course",on_delete=models.CASCADE)
student = models.ManyToManyField(Student,blank=True)
def __str__(self):
return self.cname
def get_absolute_url(self):
return reverse('project:course_detail', args=[str(self.pk)])
# @property把homework_count伪装成属性
@property
def homework_count(self):
return Homework.objects.filter(homework_id=self.id).count()
class Meta:
verbose_name='课程'
verbose_name_plural = verbose_name
class WorkAbstractModel(models.Model):
body = RichTextUploadingField('正文')
created = models.DateTimeField('创建时间', auto_now_add=True)
modified = models.DateTimeField('修改时间', auto_now=True)
file = models.FileField('文件',upload_to=user_directory_path,blank=True)
class Meta:
abstract = True
class Homework(WorkAbstractModel):
STATUS_CHOICES = (
('d', '草稿'),
('p', '发表'),
)
GROUP_CHOICES = (
(0,'个人'),
(1,'小组')
)
title = models.CharField('标题', max_length=200)# unique=True
slug = models.SlugField('摘要', max_length=60, blank=True)
published = models.DateTimeField('发布时间', null=True)
status = models.CharField('作业状态', max_length=1, choices=STATUS_CHOICES, default='p')
group = models.SmallIntegerField('组队状态',choices=GROUP_CHOICES,default=0)
views = models.PositiveIntegerField('浏览量', default=0)
course = models.ForeignKey(Course,related_name="homework",on_delete=models.CASCADE)
def __str__(self):
return self.title
# 快速获取文件格式
def get_format(self):
return self.file.url.split('.')[-1].upper()
# 利用unidecode对中文解码,利用slugify方法根据标题手动生成slug
def save(self, *args, **kwargs):
if not self.id or not self.slug:
self.slug = slugify(unidecode(self.title))
super().save(*args, **kwargs)
# Django的 CreateView 和 UpdateView 在完成对象的创建或编辑后会自动跳转到这个绝对url
def get_absolute_url(self):
return reverse('project:homework_detail', args=[str(self.course.id),str(self.pk)])
def clean(self):
if self.status == 'd' and self.published is not None:
self.published = None
# raise ValidationError('草稿没有发布日期. 发布日期已清空。')
if self.status == 'p' and self.published is None:
self.published = datetime.now()
def viewed(self):
self.views += 1
self.save(update_fields=['views'])
def publish(self):
self.status = 'p'
self.published = datetime.now()
self.save(update_fields=['status', 'published'])
class Meta:
ordering = ['-modified']
verbose_name = "作业"
verbose_name_plural = verbose_name
class Handin(WorkAbstractModel):
author = models.ForeignKey(Student,related_name="handin",on_delete=models.CASCADE)
course = models.ForeignKey(Course,related_name="handin",on_delete=models.CASCADE)
homework = models.ForeignKey(Homework,related_name="handin",on_delete=models.CASCADE)
score = models.IntegerField('分数',null=True)
def __str__(self):
return self.author.name
def get_format(self):
return self.file.url.split('.')[-1].upper()
def get_absolute_url(self):
return reverse('project:handin_detail', args=[str(self.homework.course.id),str(self.homework.pk),str(self.pk)])
class Meta:
verbose_name = "作答"
verbose_name_plural = verbose_name
class Comment(models.Model):
homework = models.ForeignKey(Homework,related_name="comment",on_delete=models.CASCADE)
text = models.TextField('评论内容')
created = models.DateTimeField('评论时间',auto_now_add=True)
username = models.CharField('用户名称',max_length=50)
def __str__(self):
return self.text[:20]
class Meta:
verbose_name = "评论"
verbose_name_plural = verbose_name
class Group(models.Model):
EDIT_CHOICES = (
(0,'不可编辑'),
(1,'可编辑')
)
leader = models.ForeignKey(Teacher,related_name="leader",on_delete=models.CASCADE)
course = models.ForeignKey(Course,related_name="group",on_delete=models.CASCADE)
member = models.ManyToManyField(Student,blank=True)
edit = models.SmallIntegerField(choices=EDIT_CHOICES,default=1,verbose_name='编辑状态')
def __str__(self):
return self.leader.name
def get_absolute_url(self):
return reverse('project:group_list', args=[str(self.course.id)])
class Meta:
verbose_name = "组队"
verbose_name_plural = verbose_name
记得同步一下数据库:
python manage.py makemigrations
python manage.py migrate
表单 forms.py
# project/forms.py
from django.forms import ModelForm
from .models import Course,Homework,Handin,Comment,Group
from ckeditor_uploader.widgets import CKEditorUploadingWidget
class CourseForm(forms.ModelForm):
class Meta:
model = Course
# 选择指定字段的所有数据 field
fields = ['cname','classes','description','opened']
# boostrap表单需要 form-control 这个样式
widgets = {
'cname': forms.TextInput(attrs={'class': 'form-control'}),
'classes': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control','rows':'3'}),
'opened': forms.Select(attrs={'class': 'form-control'}),
}
class HomeworkForm(ModelForm):
class Meta:
model = Homework
# 剔除指定字段的所有数据 exclude
exclude = ['author', 'views', 'slug','published','course']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'body': CKEditorUploadingWidget(attrs={'class': 'form-control'}),
'status': forms.Select(attrs={'class': 'form-control'}),
'group': forms.Select(attrs={'class': 'form-control'}),
'file': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
labels = {
'title': '作业标题',
'body': '作业内容',
'status': '作业状态',
'group': '组队状态',
'file': '上传文件',
}
def clean_file(self):
file = self.cleaned_data['file']
if file:
ext = file.name.split('.')[-1].lower()
if ext not in ["","jpg","png","pdf", "xlsx","docx","zip","doc"]:
raise forms.ValidationError("Only zip jpg, png, pdf, doc, docx, and xlsx files are allowed.")
return file
class HandinForm(ModelForm):
class Meta:
model = Handin
exclude = ['author','homework','course','score']
widgets = {
'body': CKEditorUploadingWidget(attrs={'class': 'form-control'}),
# 'file': forms.FileInput(attrs={'class': 'form-control'}),
'file': forms.ClearableFileInput(attrs={'class': 'form-control'}),
}
def clean_file(self):
file = self.cleaned_data['file']
if file:
ext = file.name.split('.')[-1].lower()
if ext not in ["","jpg","png", "pdf", "xlsx","docx","zip","doc"]:
raise forms.ValidationError("Only zip jpg, png, pdf, doc, docx, and xlsx files are allowed")
return file
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['text']
widgets = {
'text': forms.Textarea(attrs={'class': 'form-control','rows':'3'}),
}
labels = {
'text':'评论内容',
}
class GroupForm(forms.ModelForm):
class Meta:
model = Group
fields = ['member']
widgets = {
'member':forms.CheckboxSelectMultiple(attrs={'class': 'multi-checkbox'}),
}
labels = {
'member': '学生列表',
}
路由 urls.py
项目完整路由代码如下:
# project/urls.py
from django.urls import re_path,path
from . import views
app_name = 'project'
urlpatterns = [
# 登陆、注册 以及 信息、密码修改
path('',views.index,name='index'),
re_path(r'^register/$',views.register,name='register'),
re_path(r'^login/$', views.login, name='login'),
re_path(r'^user/(?P\d+)/profile/$', views.profile, name='profile'),
re_path(r'^user/(?P\d+)/profile/update/$', views.profile_update, name='profile_update'),
re_path(r'^user/(?P\d+)/pwdchange/$', views.pwd_change, name='pwd_change'),
re_path(r'^logout/$', views.logout, name='logout'),
# 教师创建课程 增 删 查 改
path('course/', views.CourseList.as_view(), name='course_list'),
re_path(r'^user/(?P\d+)/course/$', views.CourseListSelf.as_view(), name='course_list_self'),
re_path(r'^course/create/$',views.CourseCreate.as_view(), name='course_create'),
re_path(r'^course/(?P\d+)/$',views.CourseDetail.as_view(), name='course_detail'),
re_path(r'^course/(?P\d+)/update/$',views.CourseUpdate.as_view(), name='course_update'),
re_path(r'^course/(?P\d+)/delete$',views.CourseDelete.as_view(), name='course_delete'),
re_path(r'^course/(?P\d+)/select$', views.course_select, name='course_select'),
re_path(r'^course/(?P\d+)/cancel$', views.course_cancel, name='course_cancel'),
# 教师发布作业 增 删 查 改
re_path(r'^course/(?P\d+)/list$', views.HomeworkList.as_view(), name='homework_list'),
re_path(r'^course/(?P\d+)/homework/create/$',views.HomeworkCreate.as_view(), name='homework_create'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/$',views.HomeworkDetail.as_view(), name='homework_detail'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/update/$',views.HomeworkUpdate.as_view(), name='homework_update'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/delete$',views.HomeworkDelete.as_view(), name='homework_delete'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/publish/$',views.homework_publish, name='homework_publish'),
re_path(r'^course/(?P\d+)/homework/draft/$', views.HomeworkListDraft.as_view(), name='homework_list_publishing'),
re_path(r'^course/(?P\d+)/homework/publish/$', views.HomeworkListPublished.as_view(), name='homework_list_published'),
re_path(r'^search/$', views.homework_search, name='homework_search'),
# 课程作业的评论功能
re_path(r'^comment/(?P[0-9]+)/$', views.homework_comment, name='homework_comment'),
# 学生作业统计
re_path(r'^course/(?P\d+)/homework/(?P\d+)/count$', views.HomeworkHandin.as_view(), name='homework_handin_count'),
# 学生提交作业 增 删 查 改
re_path(r'^course/(?P\d+)/homework/(?P\d+)/list$', views.HandinList.as_view(), name='handin_list'),
re_path(r'^course/(?P\d+)/handin/list$', views.HandinListDone.as_view(), name='handin_list_done'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/handin/create/$',views.HandinCreate.as_view(), name='handin_create'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/handin/(?P\d+)/$',views.HandinDetail.as_view(), name='handin_detail'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/handin/(?P\d+)/update/$',views.HandinUpdate.as_view(), name='handin_update'),
re_path(r'^course/(?P\d+)/homework/(?P\d+)/handin/(?P\d+)/delete/$',views.HandinDelete.as_view(), name='handin_delete'),
# 课程分组
re_path(r'^course/(?P\d+)/student$', views.course_student, name='course_student'),
re_path(r'^course/(?P\d+)/group$', views.GroupList.as_view(), name='group_list'),
re_path(r'^course/(?P\d+)/group/(?P\d+)/delete/$', views.GroupDelete.as_view(), name='group_delete'),
re_path(r'^course/(?P\d+)/course/group/create$', views.course_group_create, name='course_group_create'),
]
视图 views.py
展示对象列表(比如所有用户,所有文章)- ListView
展示某个对象的详细信息(比如用户资料,比如文章详情) - DetailView
通过表单创建某个对象(比如创建用户,新建文章)- CreateView
通过表单更新某个对象信息(比如修改密码,修改文字内容)- UpdateView
用户填写表单后转到某个完成页面 - FormView
删除某个对象 - DeleteView
可通过重写 queryset, template_name 和 context_object_name 来完成ListView的自定义
class CourseList(ListView):
model = Course
# 两段代码等价
def CourseList(request):
queryset = Course.objects.all()
return render(request, 'project/course_list.html', {"object_list": queryset})
ListView用来展示一个对象的列表。
# DetailView 视图不能使用 @login_required 这个装饰器
class CourseDetail(DetailView):
model = Course
DetailView 用来展示一个具体对象的详细信息。
class CourseCreate(CreateView):
model = Course
form_class = CourseForm
template_name = 'project/course_form.html'
CreateView 一般通过某个表单创建某个对象,通常完成后会转到对象列表。可通过重写 template_name 和 form_class 来完成 CreateView 的自定义。
class CourseUpdate(UpdateView):
model = Course
form_class = CourseForm
UpdateView 一般通过某个表单更新现有对象的信息,更新完成后会转到对象详细信息页面。
class CourseDelete(DeleteView):
model = Course
# get = DeleteView.post
success_url = reverse_lazy('project:course_list')
DeleteView一般用来删除某个具体对象。
本项目完整视图代码如下:
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import auth
from django.contrib.auth.models import User
from django.views.generic import DetailView, ListView
from django.views.generic.edit import CreateView, UpdateView, DeleteView,FormView
from .models import Teacher,Student,Role,Course,Homework,Handin,Comment,Group,Role
from .forms import RegistrationForm, LoginForm, ProfileForm, PwdChangeForm,CourseForm,HomeworkForm,HandinForm,CommentForm,GroupForm
from django.http import HttpResponseRedirect,Http404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from django.core.paginator import Paginator
from django.urls import reverse, reverse_lazy
class CourseList(ListView):
paginate_by = 5
template_name = 'project/course_list.html'
def get_queryset(self):
return Course.objects.filter(opened=0).order_by("id")
@method_decorator(login_required, name='dispatch')
class CourseListSelf(ListView):
paginate_by = 5
template_name = 'project/course_list_self.html'
def get_queryset(self):
# 判断用户角色返回相应的 queryset
if self.request.user.role.role == 1:
return Course.objects.filter(teacher=self.request.user.role.teacher).order_by("id")
else:
return Course.objects.filter(student=self.request.user.role.student).order_by("id")
class CourseDetail(DetailView):
model = Course
template_name = 'project/course_detail.html'
@method_decorator(login_required, name='dispatch')
class CourseCreate(CreateView):
model = Course
form_class = CourseForm
template_name = 'project/course_form.html'
# 将创建对象的用户与 model 里的 user 结合
def form_valid(self,form):
form.instance.teacher = self.request.user.role.teacher
return super().form_valid(form)
@method_decorator(login_required, name='dispatch')
class CourseUpdate(UpdateView):
model = Course
form_class = CourseForm
template_name = 'project/course_form.html'
# 用户只能修改自己的课程,反之返回 Http404 错误
def get_object(self,queryset=None):
obj = super().get_object(queryset=queryset)
if obj.teacher != self.request.user.role.teacher:
raise Http404()
return obj
# 用户提交表单后可以做一些事情
# def form_valid(self, form):
# form.do_sth()
# return super().form_valid(form)
@method_decorator(login_required, name='dispatch')
class CourseDelete(DeleteView):
model = Course
# 通过这行代码,每次删除时候就不用弹出确认界面
# get = DeleteView.post
success_url = reverse_lazy('project:course_list')
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.teacher != self.request.user.role.teacher:
raise Http404()
return obj
@method_decorator(login_required, name='dispatch')
class HomeworkList(ListView):
def get_queryset(self):
return Homework.objects.filter(course__id=self.kwargs['pk']).filter(status='p').order_by('-published')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
homework = [x for x in self.get_queryset()]
judge = [0 for i in range(len(homework))]
handin = Handin.objects.filter(course__id=self.kwargs['pk']).filter(author=self.request.user.role.student)
for handin in handin:
if handin.homework in homework:
judge[homework.index(handin.homework)] = 1
# zip 函数将两个列表合并,返回一个 tuple
info = list(zip(homework,judge))
if Group.objects.filter(course=course,member=self.request.user.role.student):
group = Group.objects.get(course=course,member=self.request.user.role.student).member.all()
else:
group = ''
context.update({
'course':course,
'judge':judge,
'info':info,
'group':group,
})
return context
@method_decorator(login_required, name='dispatch')
class HomeworkListPublished(ListView):
template_name = 'project/homework_list_published.html'
paginate_by = 5
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.course.teacher != self.request.user.role.teacher:
raise Http404()
return obj
def get_queryset(self):
return Homework.objects.filter(course__id=self.kwargs['pk']).filter(status='p').order_by('-published')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
context.update({
'course':course,
})
return context
@method_decorator(login_required, name='dispatch')
class HomeworkListDraft(ListView):
paginate_by = 5
template_name = 'project/homework_list_publishing.html'
# 用户只能看到自己的文章草稿。当用户查看别人的文章草稿时,返回http 404错误
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.course.teacher != self.request.user.role.teacher:
raise Http404()
return obj
def get_queryset(self):
return Homework.objects.filter(course__id=self.kwargs['pk']).filter(status='d').order_by('-published')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
context.update({
'course':course,
})
return context
class HomeworkDetail(DetailView):
model = Homework
template_name = 'project/homework_detail.html'
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
obj.viewed()
return obj
# 覆写 get_context_data 的目的是因为除了将 homework 传递给模板外(DetailView 已经帮我们完成),
# 还要把评论表单、homework 下的评论列表传递给模板。
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pkr'])
form = CommentForm()
comment_list = self.object.comment.filter(homework=self.object)
context.update({
'course':course,
'form': form,
'comment_list': comment_list
})
return context
@method_decorator(login_required, name='dispatch')
class HomeworkCreate(CreateView):
model = Homework
form_class = HomeworkForm
template_name = 'project/homework_form.html'
def form_valid(self, form):
form.instance.course = Course.objects.get(id=self.kwargs['pk'])
return super().form_valid(form)
@method_decorator(login_required, name='dispatch')
class HomeworkUpdate(UpdateView):
model = Homework
form_class = HomeworkForm
template_name = 'project/homework_form.html'
@method_decorator(login_required, name='dispatch')
class HomeworkDelete(DeleteView):
model = Homework
get = DeleteView.post
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.course.teacher != self.request.user.role.teacher:
raise Http404()
return obj
def get_success_url(self):
return reverse_lazy('project:homework_list_published',args=[str(self.kwargs['pkr'])])
# 不需要确认模板直接删除
# def get(self,request,*args,**kwargs):
# return self.post(request,*args,**kwargs)
@login_required()
def homework_publish(request, pk,pkr):
homework = get_object_or_404(Homework, pk=pk)
homework.publish()
return redirect(reverse('project:homework_detail', args=[str(pkr),str(pk)]))
@login_required()
def homework_search(request):
pass
@login_required
def homework_comment(request, pk):
homework = get_object_or_404(Homework, pk=pk)
comment_list = homework.comment.filter(homework=homework)
if request.method == 'POST':
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
"""
commit=False 的作用是仅仅利用表单的数据生成 Comment 模型类的实例,但还不保存评论数据到数据库。
将评论和被评论的文章关联起来。
"""
comment.homework = homework
if request.user.role.role == 1:
comment.username = request.user.role.teacher.name
else:
comment.username = request.user.role.student.name
comment.save()
"""
重定向到 homework 的详情页,实际上当 redirect 函数接收一个模型的实例时,它会调用这个模型实例的 get_absolute_url 方法,
然后重定向到 get_absolute_url 方法返回的 URL。
"""
return redirect(homework)
else:
"""
检查到数据不合法,重新渲染详情页,并且渲染表单的错误。
因此传三个模板变量给 detail.html,
一个是作业(Homework),一个是评论列表,一个是表单 form
注意这里没有用到homework.comment_set.all() 方法:homework.comment_set.all() 反向查询全部评论。
而用到了related_name的反向查询
"""
context = {'homework': homework,
'form': form,
'comment_list': comment_list
}
return render(request, 'homework/homework_detail.html', context=context)
"""
不是 homework 请求,说明用户没有提交数据,重定向到文章详情页。
"""
return redirect(homework)
def course_select(request,pk):
course = get_object_or_404(Course,pk=pk)
user = get_object_or_404(User,pk=request.user.id)
course.student.add(user.role.student)
return HttpResponseRedirect(reverse('project:course_list_self', args=[user.id]))
def course_cancel(request,pk):
course = get_object_or_404(Course,pk=pk)
user = get_object_or_404(User,pk=request.user.id)
course.student.remove(user.role.student)
return HttpResponseRedirect(reverse('project:course_list_self', args=[user.id]))
@method_decorator(login_required, name='dispatch')
class HomeworkHandin(ListView):
model = Handin
template_name = 'project/homework_handin_count.html'
def get_queryset(self):
return Handin.objects.filter(course__id=self.kwargs['pkr']).filter(homework__id=self.kwargs['pk'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
homework = Homework.objects.get(id=self.kwargs['pk'])
context.update({
'homework':homework,
})
return context
@method_decorator(login_required, name='dispatch')
class HandinList(ListView):
paginate_by = 5
template_name = 'project/handin_list.html'
def get_queryset(self):
return Homework.objects.get(id=self.kwargs['pk']).handin.all()
@method_decorator(login_required, name='dispatch')
class HandinListDone(ListView):
template_name = 'project/handin_list_done.html'
paginate_by = 5
def get_queryset(self):
return Handin.objects.filter(course__id=self.kwargs['pk']).filter(author=self.request.user.role.student).order_by('-id')
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
if Group.objects.filter(course=course,member=self.request.user.role.student):
group = Group.objects.get(course=course,member=self.request.user.role.student).member.all()
else:
group = ''
context.update({
'course':course,
'group':group,
})
return context
@method_decorator(login_required, name='dispatch')
class HandinCreate(CreateView):
model = Handin
form_class = HandinForm
template_name = 'project/handin_form.html'
def form_valid(self, form):
homework = Homework.objects.get(id=self.kwargs['pk'])
course = Course.objects.get(id=self.kwargs['pkr'])
if homework.group == 0:
form.instance.course = course
form.instance.homework = homework
form.instance.author = self.request.user.role.student
return super().form_valid(form)
else:
# 小组作业,一人提交则全部提交
form.instance.course = course
form.instance.homework = homework
form.instance.author = self.request.user.role.student
if Group.objects.filter(course=course,member=self.request.user.role.student):
group = Group.objects.get(course=course,member=self.request.user.role.student).member.all()
for each in group:
if each != self.request.user.role.student:
Handin.objects.get_or_create(course=course,homework=homework,author=each)
return super().form_valid(form)
else:
form.instance.course = course
form.instance.homework = homework
form.instance.author = self.request.user.role.student
return super().form_valid(form)
class HandinDetail(DetailView):
model = Handin
template_name = "project/handin_detail.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
homework = Homework.objects.get(id=self.kwargs['pkr'])
context.update({
'homework':homework,
})
return context
@method_decorator(login_required, name='dispatch')
class HandinUpdate(UpdateView):
model = Handin
form_class = HandinForm
template_name = 'project/handin_form.html'
@method_decorator(login_required, name='dispatch')
class HandinDelete(DeleteView):
model = Handin
def get_success_url(self):
return reverse_lazy('project:homework_list',args=[str(self.kwargs['pka'])])
def get_object(self, queryset=None):
obj = super().get_object(queryset=queryset)
if obj.author != self.request.user.role.student:
raise Http404()
return obj
def get(self,request,*args,**kwargs):
return self.post(request,*args,**kwargs)
@method_decorator(login_required, name='dispatch')
class GroupList(ListView):
paginate_by = 5
template_name = 'project/group.html'
def get_queryset(self):
return Group.objects.filter(course__id=self.kwargs['pk'])
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
group = [x for x in self.get_queryset()]
group_list = []
for each in group:
group_list.append(each.member.all())
info = list(zip(group,group_list))
context.update({
'course':course,
'info':info,
})
return context
@method_decorator(login_required, name='dispatch')
class GroupCreate(CreateView):
model = Group
form_class = GroupForm
template_name = 'project/group_form.html'
def form_valid(self, form):
form.instance.course = Course.objects.get(id=self.kwargs['pk'])
form.instance.leader = self.request.user.role.teacher
return super().form_valid(form)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
course = Course.objects.get(id=self.kwargs['pk'])
context.update({
'course':course,
})
return context
def course_group_create(request,pk):
course = Course.objects.get(pk=pk)
if request.method == 'GET':
course_students = course.student.all()
groups = Group.objects.filter(course=course)
members = []
for group in groups:
for member in group.member.all():
members.append(member)
students = [x for x in course_students if x not in members]
print(students)
return render(request,'project/group_course_form.html',{'students':students,'course':course})
elif request.method == 'POST':
students = request.POST.getlist('students')
new_group = Group.objects.create(leader=course.teacher,course=course)
for student in students:
new_group.member.add(Student.objects.get(id=student))
group = [x for x in Group.objects.filter(course=course)]
group_list = []
for each in group:
group_list.append(each.member.all())
info = list(zip(group,group_list))
context = {
'course':course,
'info':info,
}
return render(request,'project/group.html',context=context)
@method_decorator(login_required, name='dispatch')
class GroupDelete(DeleteView):
model = Group
get = DeleteView.post
def get_success_url(self):
return reverse_lazy('project:group_list',args=[str(self.kwargs['pkr'])])
def course_student(request,pk):
course = Course.objects.get(pk=pk)
student = course.student.all()
return render(request,'project/course_student.html',context={'student':student,'course':course})
模板 Templates
表单 form 不要忘记添加 enctype="multipart/form-data" 否则文件或图片无法上传成功。
其他模板代码基本上都是一样的,可直接去我的资源中下载。
效果图如下: