八、授课机构功能
1、模板继承
如果几个页面的大体结构相同,可以使用继承的方式来实现母版的重用性,也就是子版继承母版的内容,既可以使用模板的内容,也可以重写需要改变的地地方。
首先完成授课机构的页面,通过页面显示发现,
先把org-list.html页面拷贝到templates下,在该目录下新建base.html页面,然后将org-list.html内容剪切到base.html下,然后修改静态文件的路径,在base.html页面下找到需要block的地方,以供子版继承重写:
org-list.html继承base.html,自定义org-list.html页面中的内容:
base.html页面还需要修改的地方是顶部登录注册的显示问题,这个问题和index.html页面的一样,只需要将index页面的拷贝过来即可。
2、后端机构列表接口
2.1 机构列表接口
在organization/views.oy文件下编写机构列表的接口:
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 return render(request, 'org-list.html')
配置url:
1 from organization.views import OrgView 2 3 urlpatterns = [ 4 path('org_list/', OrgView.as_view(), name='org_list') # 机构列表 5 ]
然后修改index.html导航栏跳转到机构列表的url:
现在访问机构列表页就可以看到页面了。
2.2 后台添加数据
后台添加城市的相关数据:
后台添加机构的相关数据,不过需要先在organization/models.py机构中添加一个字段category,用来区分机构的类别:
1 class CourseOrg(models.Model): 2 """课程机构""" 3 CATEGORY_CHOICES = ( 4 ('pxjg', '培训机构'), 5 ('gx', '高校'), 6 ('gr', '个人') 7 ) 8 name = models.CharField('机构名称', max_length=50) 9 category = models.CharField('机构类别', max_length=20, choices=CATEGORY_CHOICES, default='pxjg') 10 desc = models.TextField('机构描述') 11 click_nums = models.IntegerField('点击数', default=0) 12 fav_nums = models.IntegerField('收藏数', default=0) 13 image = models.ImageField('封面图', upload_to='org/%Y/%m', max_length=100) 14 address = models.CharField('地址', max_length=150) 15 city = models.ForeignKey(CityDict, verbose_name='所在城市', on_delete=models.CASCADE) 16 add_time = models.DateTimeField('添加时间', default=datetime.now) 17 18 class Meta: 19 verbose_name = '课程机构' 20 verbose_name_plural = verbose_name
修改之后需要迁移数据库。
在后台添加机构信息的时候需要上传机构的图片,在项目根目录下新建一个media,用来存放上传的图片,然后在settings.py文件中设置上传文件的路径:
1 # 上传文件的路径 2 MEDIA_URL = '/media/' 3 MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
然后现在添加机构的数据:
2.3 完善机构列表的接口
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 org_nums = all_orgs.count() 7 8 # 取出所有的城市 9 all_citys = CityDict.objects.all() 10 11 return render(request, 'org-list.html', { 12 'all_orgs': all_orgs, 13 'all_citys': all_citys, 14 'org_nums': org_nums 15 })
然后修改前端org-list.html显示的内容:
要显示后台的图片,需要在settings.py中的TEMPLATES添加图片处理器django.template.context_processors.media,如下:
1 TEMPLATES = [ 2 { 3 'BACKEND': 'django.template.backends.django.DjangoTemplates', 4 'DIRS': [os.path.join(BASE_DIR, 'templates')] 5 , 6 'APP_DIRS': True, 7 'OPTIONS': { 8 'context_processors': [ 9 'django.template.context_processors.debug', 10 'django.template.context_processors.request', 11 'django.contrib.auth.context_processors.auth', 12 'django.contrib.messages.context_processors.messages', 13 # 图片处理器,为了在课程列表中前面加上MEDIA_URL 14 'django.template.context_processors.media', 15 ], 16 }, 17 }, 18 ]
然后在urls中配置处理图片的url,固定写法:
1 from django.views.static import serve 2 from MxOnline.settings import MEDIA_ROOT 3 4 urlpatterns = [ 5 re_path(r'^media/(?P<path>.*)', serve, {"document_root": MEDIA_ROOT}), # 处理图片显示 6 ]
现在刷新列表页即可看到如下效果:
3、分页功能
机构如果超出每一页的数量,就需要使用分页,在这里使用第三方库django-pure-pagination来实现分页。
3.1 安装
在虚拟环境中直接pip install django-pure-pagination
3.2 注册
将pure_pagination注册进INSTALLED_APPS中:
1 INSTALLED_APPS = [ 2 'pure_pagination', 3 ]
3.3 在机构列表接口加入分页逻辑
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 org_nums = all_orgs.count() 7 8 # 取出所有的城市 9 all_citys = CityDict.objects.all() 10 11 # 分页 12 try: 13 page = request.GET.get('page', 1) 14 except PageNotAnInteger: 15 page = 1 16 p = Paginator(all_orgs, 5, request=request) 17 orgs = p.page(page) 18 19 return render(request, 'org-list.html', { 20 'all_orgs': orgs, 21 'all_citys': all_citys, 22 'org_nums': org_nums 23 })
然后修改org-list.html中的all_orgs:
在org-list.html中修改前端分页显示的代码:
刷新列表页后,每页显示5条机构:
4、筛选功能
4.1 城市筛选
在机构列表接口中完善筛选数据的逻辑:
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 org_nums = all_orgs.count() 7 8 # 取出所有的城市 9 all_citys = CityDict.objects.all() 10 11 # 筛选(从request中获取城市的id) 12 city_id = request.GET.get('city', '') 13 if city_id: 14 all_orgs = all_orgs.filter(city_id=int(city_id)) 15 16 # 分页 17 try: 18 page = request.GET.get('page', 1) 19 except PageNotAnInteger: 20 page = 1 21 p = Paginator(all_orgs, 5, request=request) 22 orgs = p.page(page) 23 24 return render(request, 'org-list.html', { 25 'all_orgs': orgs, 26 'all_citys': all_citys, 27 'org_nums': org_nums, 28 'city_id': city_id 29 })
前端显示的修改如下:
4.2 类别筛选
继续在机构列表接口完善类别筛选功能:
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 7 # 取出所有的城市 8 all_citys = CityDict.objects.all() 9 10 # 城市筛选(从request中获取城市的id) 11 city_id = request.GET.get('city', '') 12 if city_id: 13 all_orgs = all_orgs.filter(city_id=int(city_id)) 14 15 # 类别筛选(从request中获取机构类别ct) 16 category = request.GET.get('ct', '') 17 if category: 18 all_orgs = all_orgs.filter(category=category) 19 20 # 筛选完再统计数量 21 org_nums = all_orgs.count() 22 23 # 分页 24 try: 25 page = request.GET.get('page', 1) 26 except PageNotAnInteger: 27 page = 1 28 p = Paginator(all_orgs, 5, request=request) 29 orgs = p.page(page) 30 31 return render(request, 'org-list.html', { 32 'all_orgs': orgs, 33 'all_citys': all_citys, 34 'org_nums': org_nums, 35 'city_id': city_id, 36 'category': category 37 })
前端的显示修改内容如下:
修改完成之后,筛选功能完成,可以根据类别和城市筛选机构:
4.3 机构排名筛选
机构的排名按照点击量进行排名,在机构列表接口中添加排名筛选逻辑:
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 7 # 取出所有的城市 8 all_citys = CityDict.objects.all() 9 10 # 排名筛选(根据点击量排名) 11 hot_orgs = all_orgs.order_by('-click_nums')[:3] 12 13 # 城市筛选(从request中获取城市的id) 14 city_id = request.GET.get('city', '') 15 if city_id: 16 all_orgs = all_orgs.filter(city_id=int(city_id)) 17 18 # 类别筛选(从request中获取机构类别ct) 19 category = request.GET.get('ct', '') 20 if category: 21 all_orgs = all_orgs.filter(category=category) 22 23 # 筛选完再统计数量 24 org_nums = all_orgs.count() 25 26 # 分页 27 try: 28 page = request.GET.get('page', 1) 29 except PageNotAnInteger: 30 page = 1 31 p = Paginator(all_orgs, 5, request=request) 32 orgs = p.page(page) 33 34 return render(request, 'org-list.html', { 35 'all_orgs': orgs, 36 'all_citys': all_citys, 37 'org_nums': org_nums, 38 'city_id': city_id, 39 'category': category, 40 'hot_orgs': hot_orgs 41 })
然后修改前端显示代码:
4.4 学习人数和课程排名筛选
首先在organization/models.py中的CourseOrg中加入students和course_nums两个字段:
1 class CourseOrg(models.Model): 2 """课程机构""" 3 CATEGORY_CHOICES = ( 4 ('pxjg', '培训机构'), 5 ('gx', '高校'), 6 ('gr', '个人') 7 ) 8 name = models.CharField('机构名称', max_length=50) 9 category = models.CharField('机构类别', max_length=20, choices=CATEGORY_CHOICES, default='pxjg') 10 desc = models.TextField('机构描述') 11 students = models.IntegerField('学习人数', default=0) 12 course_nums = models.IntegerField('课程数', default=0) 13 click_nums = models.IntegerField('点击数', default=0) 14 fav_nums = models.IntegerField('收藏数', default=0) 15 image = models.ImageField('封面图', upload_to='org/%Y/%m', max_length=100) 16 address = models.CharField('地址', max_length=150) 17 city = models.ForeignKey(CityDict, verbose_name='所在城市', on_delete=models.CASCADE) 18 add_time = models.DateTimeField('添加时间', default=datetime.now) 19 20 class Meta: 21 verbose_name = '课程机构' 22 verbose_name_plural = verbose_name 23 24 def __str__(self): 25 return self.name
然后迁移数据库。
然后在机构列表接口中完善学习人数和课程数排名的逻辑:
1 class OrgView(View): 2 """机构列表""" 3 def get(self, request): 4 # 取出所有的机构 5 all_orgs = CourseOrg.objects.all() 6 7 # 取出所有的城市 8 all_citys = CityDict.objects.all() 9 10 # 排名筛选(根据点击量排名) 11 hot_orgs = all_orgs.order_by('-click_nums')[:3] 12 13 # 学习人数和课程数排名筛选 14 sort = request.GET.get('sort', '') 15 if sort: 16 if sort == 'students': 17 all_orgs = all_orgs.order_by('-students') 18 elif sort == 'courses': 19 all_orgs = all_orgs.order_by('-course_nums') 20 21 # 城市筛选(从request中获取城市的id) 22 city_id = request.GET.get('city', '') 23 if city_id: 24 all_orgs = all_orgs.filter(city_id=int(city_id)) 25 26 # 类别筛选(从request中获取机构类别ct) 27 category = request.GET.get('ct', '') 28 if category: 29 all_orgs = all_orgs.filter(category=category) 30 31 # 筛选完再统计数量 32 org_nums = all_orgs.count() 33 34 # 分页 35 try: 36 page = request.GET.get('page', 1) 37 except PageNotAnInteger: 38 page = 1 39 p = Paginator(all_orgs, 5, request=request) 40 orgs = p.page(page) 41 42 return render(request, 'org-list.html', { 43 'all_orgs': orgs, 44 'all_citys': all_citys, 45 'org_nums': org_nums, 46 'city_id': city_id, 47 'category': category, 48 'hot_orgs': hot_orgs, 49 'sort': sort 50 })
然后修改前端代码:
5、我要学习咨询功能
5.1 我要学习咨询接口
我要学习咨询的表单这次使用ModelForm来实现,首先在organization下新建form.py文件:
1 from django import forms 2 from operation.models import UserAsk 3 4 5 class UserAskForm(forms.ModelForm): 6 """我要学习咨询表单验证""" 7 class Meta: 8 model = UserAsk 9 fields = ['name','mobile','course_name']
编写我要学习咨询的后台接口:
1 class UserAskView(View): 2 """我要学习咨询""" 3 def post(self, request): 4 userask_form = UserAskForm(request.POST) 5 if userask_form.is_valid(): 6 # 通过ModelForm可以直接将表单中的内容保存 7 user_ask = userask_form.save(commit=True) 8 # 通过ajax提交,给前端返回json数据 9 return HttpResponse('{"status": "success"}', content_type='application/json') 10 else: 11 return HttpResponse('{"status": "fail", "msg": "添加出错"}', content_type='application/json')
现在使用路由分发的方式来配置url,首先删除urls.py中的org_list这个路由,然后添加一级路由:
1 urlpatterns = [ 2 # path('org_list/', OrgView.as_view(), name='org_list'), # 机构列表 3 path('org/', include('organization.urls', namespace='org')), # 机构列表 4 ]
然后在organization下新建urls.py文件,在这个文件配置机构列表页的相关url,之前删除了机构列表的url,现在将机构列表和我要学习咨询的url都添加到这个文件下:
1 from django.urls import path, re_path 2 3 from .views import OrgView, UserAskView 4 5 app_name = 'organization' 6 7 urlpatterns = [ 8 path('list/',OrgView.as_view(),name='org_list'), # 机构列表 9 path('user_ask/', UserAskView.as_view(), name='user_ask'), # 我要学习咨询 10 ]
之前在首页跳转机构列表的url需要修改过来:
然后在form中添加验证手机号码合法性的逻辑:
1 import re 2 3 from django import forms 4 from operation.models import UserAsk 5 6 7 class UserAskForm(forms.ModelForm): 8 """我要学习咨询表单验证""" 9 class Meta: 10 model = UserAsk 11 fields = ['name','mobile','course_name'] 12 13 def clean_mobile(self): 14 """验证手机号合法性""" 15 mobile = self.cleaned_data['mobile'] 16 REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|176\d{8}$" 17 p = re.compile(REGEX_MOBILE) 18 if p.match(mobile): 19 return mobile 20 else: 21 raise forms.ValidationError('手机号非法', code='mobile_invalid')
我要学习咨询表单是将数据ajax提交到后台的,不刷新页面,所以在前端要编写script代码: