完成授课机构的功能
模板继承
在templates
目录下,新建base.html
,剪切org-list.html
内容到里面
编写org-list.html
内容
继承base.html
,将里面的面包屑和内容拷贝到org-list.html
中
配置路由
# 课程机构首页url
path('org_list/', OrgView.as_view(), name="org_list"),
这里需要修改一下models
# organization/models.py
class CourseOrg(models.Model):
ORG_CHOICES = (
("pxjg", u"培训机构"),
("gx", u"高校"),
("gr", u"个人"),
)
# 添加字段
category = models.CharField(max_length=20, choices=ORG_CHOICES, verbose_name="机构类别", default="pxjg")
修改了models之后做数据库的变动:
makemigrations organization
migrate organization
在项目目录下面新建一个目录media
,用来存放上传的图片
setting中要配置我们把文件存放在哪个根目录之下
# 设置我们上传文件的路径
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
在后台自行添加教育机构和城市
添加完数据后进行逻辑代码的编写
class OrgView(View):
'''课程机构'''
def get(self,request):
# 取出所有课程机构
all_orgs = CourseOrg.objects.all()
org_onums = all_orgs.count()
# 取出所有城市
all_citys = CityDict.objects.all()
return render(request, "org-list.html", {
"all_orgs": all_orgs,
"all_citys": all_citys,
'org_onums':org_onums,
})
数据填充
打开org-list.html
进行数据的填充
这里只保留一个dl标签即可,其他的数据自行填充
这里说一下MEDIA_URL
数据库中存储的图片路径时没有/media/
前缀的
如果想要使用MEDIA_RUL
需要在设置中添加media处理器
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')]
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
#添加图片处理器,为了在课程列表中前面加上MEDIA_URL
'django.template.context_processors.media',
],
},
},
]
然后还需要配url
from django.views.static import serve
from MxOnline.settings import MEDIA_ROOT
# 处理图片显示的url,使用Django自带serve,传入参数告诉它去哪个路径找,我们有配置好的路径MEDIAROOT
re_path(r'^media/(?P.*)', serve, {"document_root": MEDIA_ROOT })
列表分页功能
这里不使用Django内置的由分页模块,而是使用插件django-pure-pagination
pip install django-pure-pagination
进行settings的配置
INSTALLED_APPS = (
...
'pure_pagination',
)
PAGINATION_SETTINGS = {
'PAGE_RANGE_DISPLAYED': 10,
'MARGIN_PAGES_DISPLAYED': 2,
'SHOW_FIRST_PAGE_WHEN_INVALID': True,
}
这里参考官方的例子进行配置
from pure_pagination import Paginator, EmptyPage, PageNotAnInteger
class OrgView(View):
def get(self,request):
# 查找到所有的课程机构
all_orgs = CourseOrg.objects.all()
# 总共有多少家机构使用count进行统计
org_nums = all_orgs.count()
# 取出所有的城市
all_city = CityDict.objects.all()
# 对课程机构进行分页
# 尝试获取前台get请求传递过来的page参数
# 如果是不合法的配置参数默认返回第一页
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
# 这里指从allorg中取五个出来,每页显示5个
p = Paginator(all_orgs, 5, request=request)
orgs = p.page(page)
return render(request, "org-list.html", {
"all_orgs":orgs,
"all_city": all_city,
"org_nums": org_nums,
})
在html文件中进行分页
这里for循环需要使用object_list
{% for org in all_orgs.object_list %}
{% if all_orgs.has_previous %}
- 上一页
{% endif %}
{% for page in all_orgs.pages %}
{% if page %}
{% ifequal page all_orgs.number %}
- {{ page }}
{% else %}
- {{ page }}
{% endifequal %}
{% else %}
- ...
{% endif %}
{% endfor %}
{% if all_orgs.has_next %}
- 下一页
{% endif %}
分类筛选功能
点击某个城市时,该城市处于选中状态,下面显示的数据是当前城市的
class OrgView(View):
def get(self,request):
# 查找到所有的课程机构
all_orgs = CourseOrg.objects.all()
# 取出所有的城市
all_city = CityDict.objects.all()
city_id = request.GET.get('city', '')
if city_id:
all_orgs = all_orgs.filter(city_id=int(city_id))
# 总共有多少家机构使用count进行统计
org_nums = all_orgs.count()
# 对课程机构进行分页
# 尝试获取前台get请求传递过来的page参数
# 如果是不合法的配置参数默认返回第一页
try:
page = request.GET.get('page', 1)
except PageNotAnInteger:
page = 1
# 这里指从allorg中取五个出来,每页显示5个
p = Paginator(all_orgs, 5, request=request)
orgs = p.page(page)
return render(request, "org-list.html", {
"all_orgs":orgs,
"all_city": all_city,
"org_nums": org_nums,
'city_id': city_id,
})
后台逻辑中给前端传递了一个city_id用来城市的标记
全部
{% for city in all_city %}
{{ city.name }}
{% endfor %}
因为city.id
是一个int类型,要转换成字符串,再作比较。
{% ifequal city_id '' %}
如果为空,说明没有city选中,则“全部”是“active”
同理,添加类别筛选
# 类别筛选
category = request.GET.get('ct','')
if category:
all_orgs = all_orgs.filter(category=category)
机构类别
所在地区
更多
全部
{% for city in all_city %}
{{ city.name }}
{% endfor %}
进行城市与分类的联动:
- 当选择全部类别的时候,就只通过当前城市id。
- 当选择全部城市的时候,就只通过当前目录id。
- 当两者都选的时候使用&连接。
课程机构排名
# 热门机构,如果不加负号会是有小到大。
hot_orgs = all_orgs.order_by("-click_nums")[:3]
授课机构排名
{% for curent_org in hot_orgs %}
- {{ foorloop.counter }}
-
{{ curent_org.name }}
{{ curent_org.address }}
{% endfor %}
循环时内置变量forloop.counter取当前循环到第几次
学习人数和课程的筛选
在models中添加学习人数和课程数两个字段
# 当学生点击学习课程,找到所属机构,学习人数加1
students = models.IntegerField(default=0, verbose_name=u"学习人数")
# 当发布课程就加1
course_nums = models.IntegerField(default=0, verbose_name=u"课程数")
makemigrations
migrate
# 进行排序
sort = request.GET.get('sort', "")
if sort:
if sort == "students":
all_orgs = all_orgs.order_by("-students")
elif sort == "courses":
all_orgs = all_orgs.order_by("-course_nums")
modelform 提交我要学习咨询
在organazition目录下创建forms.py
文件
# 普通版本的form
# class UserAskForm(forms.Form):
# name = forms.CharField(required=True, min_length=2, max_length=20)
# phone = forms.CharField(required=True, max_length=11, min_length=11)
# course_name = forms.CharField(required=True, min_length=5, max_length=50)
# 进阶版本的modelform:它可以向model一样save
from django import forms
from operation.models import UserAsk
class AnotherUserForm(forms.ModelForm):
# 继承之余还可以新增字段
# 是由哪个model转换的
class Meta:
model = UserAsk
# 我需要验证的字段
fields = ['name','mobile','course_name']
使用include进行路由分发
在organization
目录下新建urls.py
from django.urls import path
from organization.views import OrgView, AddUserAskView
app_name = "organization"
urlpatterns = [
# 课程机构首页url
path('list/', OrgView.as_view(), name="org_list"),
]
根目录下的urls.py
删掉org_list,新增include
# 课程机构app的url配置
path("org/", include('organization.urls',namespace="org")),
修改base.html中“课程机构的链接”
授课机构
后台逻辑代码
# 用户添加我要学习
class AddUserAskView(View):
# 处理表单提交当然post
def post(self,request):
userask_form = UserAskForm(request.POST)
# 判断该form是否有效
if userask_form.is_valid():
# 这里是modelform和form的区别
# 它有model的属性
# 当commit为true进行真正保存
user_ask = userask_form.save(commit=True)
# 这样就不需要把一个一个字段取出来然后存到model的对象中之后save
# 如果保存成功,返回json字符串,后面content type是告诉浏览器的,
return HttpResponse('{"status": "success"}', content_type='application/json')
else:
# 如果保存失败,返回json字符串,并将form的报错信息通过msg传递到前端
return HttpResponse('{"status": "fail", "msg":{0}}'.format(userask_form.errors), content_type='application/json')
路由
path('add_ask/', AddUserAskView.as_view(), name="add_ask"),
前端页面采用Ajax方式请求
form表单添加crsf_token
在ModelForm中自定义一个手机号验证的方法
def clean_mobile(self):
"""
验证手机号码是否合法
"""
mobile = self.cleaned_data['mobile']
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|176\d{8}$"
p = re.compile(REGEX_MOBILE)
if p.match(mobile):
return mobile
else:
raise forms.ValidationError(u"手机号码非法", code="mobile_invalid")
机构详情
- 机构首页
- 机构课程
- 机构介绍
- 机构讲师
课程中应该有一个外键指向它是哪个机构的
# courses/models.py
course_org = models.ForeignKey(CourseOrg,on_delete=models.CASCADE, verbose_name="所属机构",null=True,blank=True)
makemigration
migrate
登录xadmin添加基础的必要数据。添加课程与讲师。
新建一个模板,命名为“org_base.html”,复制org-detail-homepage.html的内容到里面,
添加block,修改静态文件路径
把org_base中的三个“right”剪切到home里面
路由配置
re_path('home/(?P\d+)/', OrgHomeView.as_view(), name="org_home"),
视图函数
class OrgHomeView(View):
'''机构首页'''
def get(self,request,org_id):
# 根据id找到课程机构
course_org = CourseOrg.objects.get(id=int(org_id))
# 反向查询到课程机构的所有课程和老师
all_courses = course_org.course_set.all()[:4]
all_teacher = course_org.teacher_set.all()[:2]
return render(request,'org-detail-homepage.html',{
'course_org':course_org,
'all_courses':all_courses,
'all_teacher':all_teacher,
})
显示课程
修改org-base.html
为讲师增加头像字段
image = models.ImageField(
default= '',
upload_to="teacher/%Y/%m",
verbose_name=u"头像",
max_length=100)
makemgration
migrate
显示机构教师
机构教师
查看更多
{% for teacher in all_teacher %}
昵称:{{ teacher.name }}
java开发教程
发表于:2015-10-12
课程介绍
{% endfor %}
显示机构详情
机构介绍
查看更多 >
{{ course_org.desc }}
机构课程
配置路由
re_path('course/(?P\d+)/', OrgCourseView.as_view(), name="org_course"),
视图函数
class OrgCourseView(View):
"""
机构课程列表页
"""
def get(self, request, org_id):
# 根据id取到课程机构
course_org = CourseOrg.objects.get(id= int(org_id))
# 通过课程机构找到课程。内建的变量,找到指向这个字段的外键引用
all_courses = course_org.course_set.all()
return render(request, 'org-detail-course.html',{
'all_courses':all_courses,
'course_org': course_org,
})
修改org-base.html中left的链接
显示机构课程,修改org-detail-course.html
{% block right_form %}
机构课程
{% endblock %}
左侧active修改
因为现在没有值能判断当前是哪个页面。所以在orghomeview中传值回来current page
修改views,传个current_page参数到前端,可以知道当前是哪个被激活状态
class OrgHomeView(View):
'''机构首页'''
def get(self,request,org_id):
current_page = 'home'
# 根据id找到课程机构
course_org = CourseOrg.objects.get(id=int(org_id))
# 反向查询到课程机构的所有课程和老师
all_courses = course_org.course_set.all()[:4]
all_teacher = course_org.teacher_set.all()[:2]
return render(request,'org-detail-homepage.html',{
'course_org':course_org,
'all_courses':all_courses,
'all_teacher':all_teacher,
'current_page': current_page,
})
class OrgCourseView(View):
"""
机构课程列表页
"""
def get(self, request, org_id):
current_page = 'course'
# 根据id取到课程机构
course_org = CourseOrg.objects.get(id= int(org_id))
# 通过课程机构找到课程。内建的变量,找到指向这个字段的外键引用
all_courses = course_org.course_set.all()
return render(request, 'org-detail-course.html',{
'all_courses':all_courses,
'course_org': course_org,
'current_page': current_page,
})
机构介绍
re_path('desc/(?P\d+)/', OrgDescView.as_view(), name="org_desc"),
class OrgDescView(View):
'''机构介绍页'''
def get(self, request, org_id):
current_page = 'desc'
# 根据id取到课程机构
course_org = CourseOrg.objects.get(id= int(org_id))
return render(request, 'org-detail-desc.html',{
'course_org': course_org,
'current_page':current_page,
})
{% block right_form %}
机构介绍
{{ course_org.desc }}
{% endblock %}
机构讲师
re_path('teacher/(?P\d+)/', OrgTeacherView.as_view(), name="org_teacher"),
class OrgTeacherView(View):
"""
机构教师页
"""
def get(self, request, org_id):
current_page = 'teacher'
course_org = CourseOrg.objects.get(id= int(org_id))
all_teacher = course_org.teacher_set.all()
return render(request, 'org-detail-teachers.html',{
'all_teacher':all_teacher,
'course_org': course_org,
'current_page':current_page,
})
{% block right_form %}
机构讲师
{% endblock %}
重载我们的pagepath和title,使数据动态显示
{% block title %}机构教师{% endblock %}
{% block page_path %}
机构教师
{% endblock %}
课程机构收藏功能
path('add_fav/', AddFavView.as_view(), name="add_fav"),
class AddFavView(View):
"""
用户收藏与取消收藏功能
"""
def post(self, request):
# 表明你收藏的不管是课程,讲师,还是机构。他们的id
# 默认值取0是因为空串转int报错
id = request.POST.get('fav_id', 0)
# 取到你收藏的类别,从前台提交的ajax请求中取
type = request.POST.get('fav_type', 0)
# 收藏与已收藏取消收藏
# 判断用户是否登录:即使没登录会有一个匿名的user
if not request.user.is_authenticated:
# 未登录时返回json提示未登录,跳转到登录页面是在ajax中做的
return HttpResponse('{"status":"fail", "msg":"用户未登录"}', content_type='application/json')
exist_records = UserFavorite.objects.filter(user=request.user, fav_id=int(id), fav_type=int(type))
if exist_records:
# 如果记录已经存在, 则表示用户取消收藏
exist_records.delete()
return HttpResponse('{"status":"success", "msg":"收藏"}', content_type='application/json')
else:
user_fav = UserFavorite()
# 过滤掉未取到fav_id type的默认情况
if int(type) >0 and int(id) >0:
user_fav.fav_id = int(id)
user_fav.fav_type = int(type)
user_fav.user = request.user
user_fav.save()
return HttpResponse('{"status":"success", "msg":"已收藏"}', content_type='application/json')
else:
return HttpResponse('{"status":"fail", "msg":"收藏出错"}', content_type='application/json')
Ajax放在org_base.html
里面