文章目录
- 环境配置与项目介绍
- Django升级2.0+ 注意的点
- app管理
- 数据库设计
- 注册model
- 注册app目录
- 5 xadmin搭建后台管理系统
- 5-3 users app 的model注册
- 5-5 xadmin全局配置
- app.py 定义app后台名称
- 第6章 用户注册功能实现
- 6.1 登录表单验证
- 6-6 session和cookie自动登录机制
- 用户注册
- 发送邮件
- 激活用户
- 第7章 课程机构功能实现
- 7-3 课程机构列表页数据展示
- Django 定义本地上传图片的路径
- 渲染模板(使用后端配置)
- 分页 pure_pagination
- 7-7 Django modelform提交我要学习咨询
- 7-9 机构详情展示
- BUG
环境配置与项目介绍
- 配置虚拟环境
Django升级2.0+ 注意的点
app管理
数据库设计
注册model
- django如果每个app注册后,需要admin显示,则在app/admin.py中注册,xadmin则为adminx.py
# course/adminx.pyimport xadminfrom .models import Course, Lesson, Video, CourseResource,BannerCourse
from organization.models import CourseOrgclass LessonInline(object):model = Lessonextra = 0class CourseResourceInline(object):model = CourseResourceextra = 0# Course的admin管理器
class CourseAdmin(object):'''课程'''list_display = [ 'name','desc','detail','degree','learn_times','students','get_zj_nums','go_to'] #显示的字段search_fields = ['name', 'desc', 'detail', 'degree', 'students'] #搜索list_filter = [ 'name','desc','detail','degree','learn_times','students'] #过滤model_icon = 'fa fa-book' #图标ordering = ['-click_nums'] #排序readonly_fields = ['click_nums'] #只读字段exclude = ['fav_nums'] #不显示的字段# list_editable = ['degree','desc']# refresh_times = [3,5] #自动刷新(里面是秒数范围)inlines = [LessonInline,CourseResourceInline] #增加章节和课程资源style_fields = {"detail": "ueditor"}def queryset(self):# 重载queryset方法,来过滤出我们想要的数据的qs = super(CourseAdmin, self).queryset()# 只显示is_banner=True的课程qs = qs.filter(is_banner=False)return qsdef save_models(self):# 在保存课程的时候统计课程机构的课程数# obj实际是一个course对象obj = self.new_obj# 如果这里不保存,新增课程,统计的课程数会少一个obj.save()# 确定课程的课程机构存在。if obj.course_org is not None:#找到添加的课程的课程机构course_org = obj.course_org#课程机构的课程数量等于添加课程后的数量course_org.course_nums = Course.objects.filter(course_org=course_org).count()course_org.save()class BannerCourseAdmin(object):'''轮播课程'''list_display = [ 'name','desc','detail','degree','learn_times','students']search_fields = ['name', 'desc', 'detail', 'degree', 'students']list_filter = [ 'name','desc','detail','degree','learn_times','students']model_icon = 'fa fa-book'ordering = ['-click_nums']readonly_fields = ['click_nums']exclude = ['fav_nums']inlines = [LessonInline,CourseResourceInline]def queryset(self):#重载queryset方法,来过滤出我们想要的数据的qs = super(BannerCourseAdmin, self).queryset()#只显示is_banner=True的课程qs = qs.filter(is_banner=True)return qsclass LessonAdmin(object):'''章节'''list_display = ['course', 'name', 'add_time']search_fields = ['course', 'name']#这里course__name是根据课程名称过滤list_filter = ['course__name', 'name', 'add_time']class VideoAdmin(object):'''视频'''list_display = ['lesson', 'name', 'add_time']search_fields = ['lesson', 'name']list_filter = ['lesson', 'name', 'add_time']class CourseResourceAdmin(object):'''课程资源'''list_display = ['course', 'name', 'download', 'add_time']search_fields = ['course', 'name', 'download']list_filter = ['course__name', 'name', 'download', 'add_time']# 将管理器与model进行注册关联
xadmin.site.register(Course, CourseAdmin)
xadmin.site.register(BannerCourse, BannerCourseAdmin)
xadmin.site.register(Lesson, LessonAdmin)
xadmin.site.register(Video, VideoAdmin)
xadmin.site.register(CourseResource, CourseResourceAdmin)
注册app目录
5 xadmin搭建后台管理系统
5-3 users app 的model注册
-
一般在admin中对model进行注册与显示的逻辑更改
-
显示字段 list_display = (‘action_time’, ‘user’, ‘ip_addr’, ‘str’, ‘link’)
-
筛选 list_filter = [‘user’, ‘action_time’]
-
搜索 search_fields = [‘ip_addr’, ‘message’]
-
图标 model_icon = ‘fa fa-cog’
5-5 xadmin全局配置
# users/adminx.pyimport xadminfrom .models import EmailVerifyRecord,Banner
from xadmin import views# 创建xadmin的最基本管理器配置,并与view绑定
class BaseSetting(object):# 开启主题功能enable_themes = Trueuse_bootswatch = True# 全局修改,固定写法
class GlobalSettings(object):# 修改titlesite_title = 'imooc后台管理界面'# 修改footersite_footer = '科比的公司'# 收起菜单 将每个数据库的表收起menu_style = 'accordion'#xadmin中这里是继承object,不再是继承admin
class EmailVerifyRecordAdmin(object):# 显示的列list_display = ['code', 'email', 'send_type', 'send_time']# 搜索的字段search_fields = ['code', 'email', 'send_type']# 过滤list_filter = ['code', 'email', 'send_type', 'send_time']class BannerAdmin(object):list_display = ['title', 'image', 'url','index', 'add_time']search_fields = ['title', 'image', 'url','index']list_filter = ['title', 'image', 'url','index', 'add_time']xadmin.site.register(EmailVerifyRecord,EmailVerifyRecordAdmin)
xadmin.site.register(Banner,BannerAdmin)# 将基本配置管理与view绑定
xadmin.site.register(views.BaseAdminView,BaseSetting)# 将title和footer信息进行注册
xadmin.site.register(views.CommAdminView,GlobalSettings)
app.py 定义app后台名称
将app.py 的配置 注册到init.py中
第6章 用户注册功能实现
6.1 登录表单验证
from django import forms
from captcha.fields import CaptchaField
class LoginForm(forms.Form):'''登录验证表单'''username = forms.CharField(required=True)password = forms.CharField(required=True,min_length=5)
class LoginView(View):'''用户登录'''def get(self,request):return render(request, 'login.html')def post(self,request):# 实例化login_form = LoginForm(request.POST)if login_form.is_valid():# 获取用户提交的用户名和密码user_name = request.POST.get('username', None)pass_word = request.POST.get('password', None)# 成功返回user对象,失败Noneuser = authenticate(username=user_name, password=pass_word)# 如果不是null说明验证成功if user is not None:if user.is_active:# 只有注册激活才能登录login(request, user)return HttpResponseRedirect(reverse('index'))else:return render(request, 'login.html', {'msg': '用户名或密码错误', 'login_form': login_form})# 只有当用户名或密码不存在时,才返回错误信息到前端else:return render(request, 'login.html', {'msg': '用户名或密码错误','login_form':login_form})# form.is_valid()已经判断不合法了,所以这里不需要再返回错误信息到前端了else:return render(request,'login.html',{'login_form':login_form})
6-6 session和cookie自动登录机制
设置redis Djangosession
这里采用默认的mysql保存
用户注册
from django.contrib.auth.hashers import make_passwordclass RegisterView(View):'''用户注册'''def get(self,request):register_form = RegisterForm()return render(request,'register.html',{'register_form':register_form})def post(self,request):register_form = RegisterForm(request.POST)if register_form.is_valid():user_name = request.POST.get('email', None)# 如果用户已存在,则提示错误信息if UserProfile.objects.filter(email = user_name):return render(request, 'register.html', {'register_form':register_form,'msg': '用户已存在'})pass_word = request.POST.get('password', None)# 实例化一个user_profile对象user_profile = UserProfile()user_profile.username = user_nameuser_profile.email = user_nameuser_profile.is_active = False# 对保存到数据库的密码加密user_profile.password = make_password(pass_word)user_profile.save()send_register_eamil(user_name,'register')return render(request,'login.html')else:return render(request,'register.html',{'register_form':register_form})
发送邮件
# apps/utils/email_send.pyfrom random import Random
from django.core.mail import send_mailfrom users.models import EmailVerifyRecord
from RcOnline.settings import EMAIL_FROM# 生成随机字符串
def random_str(random_length=8):str = ''# 生成字符串的可选字符串chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'length = len(chars) - 1random = Random()for i in range(random_length):str += chars[random.randint(0, length)]return str# 发送注册邮件
def send_register_eamil(email, send_type="register"):# 发送之前先保存到数据库,到时候查询链接是否存在# 实例化一个EmailVerifyRecord对象email_record = EmailVerifyRecord()# 生成随机的code放入链接if send_type == 'update_email':code = random_str(4)else:code = random_str(16)email_record.code = codeemail_record.email = emailemail_record.send_type = send_typeemail_record.save()# 定义邮件内容:email_title = ""email_body = ""if send_type == "register":email_title = "NBA注册激活链接"email_body = "请点击下面的链接激活你的账号: http://127.0.0.1:8000/active/{0}".format(code)# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者listsend_status = send_mail(email_title, email_body, EMAIL_FROM, [email])# 如果发送成功if send_status:passelif send_type == "forget":email_title = "NBA找回密码链接"email_body = "请点击下面的链接找回你的密码: http://127.0.0.1:8000/reset/{0}".format(code)# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者listsend_status = send_mail(email_title, email_body, EMAIL_FROM, [email])# 如果发送成功if send_status:passelif send_type == "update_email":email_title = "NBA邮箱修改验证码"email_body = "你的邮箱验证码为{0}".format(code)# 使用Django内置函数完成邮件发送。四个参数:主题,邮件内容,从哪里发,接受者listsend_status = send_mail(email_title, email_body, EMAIL_FROM, [email])# 如果发送成功if send_status:pass
settings
EMAIL_HOST = "smtp.qq.com" # SMTP服务器主机
EMAIL_PORT = 25 # 端口
EMAIL_HOST_USER = "" # 邮箱地址
EMAIL_HOST_PASSWORD = "" # 授权码
EMAIL_USE_TLS = False
EMAIL_FROM = "" # 邮箱地址
激活用户
# 激活用户
class ActiveUserView(View):def get(self, request, active_code):# 查询邮箱验证记录是否存在all_record = EmailVerifyRecord.objects.filter(code = active_code)if all_record:for record in all_record:# 获取到对应的邮箱email = record.email# 查找到邮箱对应的useruser = UserProfile.objects.get(email=email)user.is_active = Trueuser.save()# 验证码不对的时候跳转到激活失败页面else:return render(request,'active_fail.html')# 激活成功跳转到登录页面return render(request, "login.html", )
第7章 课程机构功能实现
7-3 课程机构列表页数据展示
Django 定义本地上传图片的路径
-
在settings.py定义
# 上传图片 MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,'media')
-
在root目录下设置media文件夹
-
在models.py中定义文件路径
class CourseOrg(models.Model): ORG_CHOICES = (("pxjg", u"培训机构"),("gx", u"高校"),("gr", u"个人"), ) name = models.CharField('机构名称',max_length=50) desc = models.TextField('机构描述') category = models.CharField(max_length=20, choices=ORG_CHOICES, verbose_name=u"机构类别", default="pxjg") click_nums = models.IntegerField('点击数',default=0) tag = models.CharField('机构标签',max_length=10,default='全国知名') fav_nums = models.IntegerField('收藏数',default=0) students = models.IntegerField("学习人数",default=0) course_nums = models.IntegerField("课程数",default=0) image = models.ImageField('logo',upload_to='org/%Y/%m',max_length=100) address = models.CharField('机构地址',max_length=150,) city = models.ForeignKey(CityDict,verbose_name='所在城市',on_delete=models.CASCADE) add_time = models.DateTimeField(default=datetime.now)
- 目录结构,(其实应该将文件上传到云 or 改成时间戳的名称存储)
- 目录结构,(其实应该将文件上传到云 or 改成时间戳的名称存储)
渲染模板(使用后端配置)
-
1.settings.py
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',],},}, ]
-
2.设置图片路径,使用{{ MEDIA_URL }}{{ course_org.image }}
{% for course_org in all_orgs.object_list %}<dl class="des difdes"><dt><a href="{% url 'org:org_home' course_org.id %}"><img width="200" height="120" class="scrollLoading"data-url="{{ MEDIA_URL }}{{ course_org.image }}"/></a></dt><dd><div class="clearfix"><a href="org-detail-homepage.html"><h1>{{ course_org.name }}</h1><div class="pic fl"><img src="{% static 'images/authentication.png' %}"/><img src="{% static 'images/gold.png' %}"/></div></a></div><ul class="cont"><li class="first"><p class="pic9">课程数:<span>1</span></p><p class="c7">学习人数:<span>1000</span></p></li><li class="c8" style="padding-left:18px;">北京市海淀区中关村北大街</li><li class="pic10" style="padding-left:18px;">经典课程:<a href="/diary/19/">c语言基础入门</a><a href="/diary/16/">数据库基础</a></li></ul></dd><div class="buy start_groupbuy jsShowPerfect2" data-id="22"><br/>联系<br/>服务</div></dl> {% endfor %}
分页 pure_pagination
pure_pagination 对Django的分页paginate进行封装, 在settings.py的INSTALLED_APPS设置pure_pagination
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','course','operation','organization','users','xadmin','crispy_forms','captcha','pure_pagination','DjangoUeditor',
]
from pure_pagination import Paginator, EmptyPage, PageNotAnIntegerclass OrgView(View):'''课程机构'''def get(self, request):# 所有课程机构all_orgs = CourseOrg.objects.all()# 所有城市all_citys = CityDict.objects.all()# 机构搜索功能search_keywords = request.GET.get('keywords', '')if search_keywords:# 在name字段进行操作,做like语句的操作。i代表不区分大小写# or操作使用Qall_orgs = all_orgs.filter(Q(name__icontains=search_keywords) | Q(desc__icontains=search_keywords))# 城市筛选city_id = request.GET.get('city','')if city_id:all_orgs = all_orgs.filter(city_id=int(city_id))# 类别筛选category = request.GET.get('ct','')if category:all_orgs = all_orgs.filter(category=category)# 对课程机构进行分页# 尝试获取前台get请求传递过来的page参数# 如果是不合法的配置参数默认返回第一页try:page = request.GET.get('page', 1)except PageNotAnInteger:page = 1# 这里指从allorg中取五个出来,每页显示2个,可以在settings中设置分页显示数量p = Paginator(all_orgs, 2, request=request)orgs = p.page(page)return render(request, "org-list.html", {"all_orgs": orgs,"all_citys": all_citys,"org_nums": org_nums,'city_id':city_id,"category": category,'hot_orgs':hot_orgs,'sort':sort,})
<div class="pageturn"><ul class="pagelist">{% if all_courses.has_previous %}<li class="long"><a href="?{{ all_courses.previous_page_number.querystring }}">上一页</a></li>{% endif %}{% for page in all_courses.pages %}{% if page %}{% ifequal page all_courses.number %}<li class="active"><a href="?{{ page.querystring }}">{{ page }}</a></li>{% else %}<li><a href="?{{ page.querystring }}" class="page">{{ page }}</a></li>{% endifequal %}{% else %}<li class="none"><a href="">...</a></li>{% endif %}{% endfor %}{% if all_courses.has_next %}<li class="long"><a href="?{{ all_courses.next_page_number.querystring }}">下一页</a></li>{% endif %}</ul></div>
7-7 Django modelform提交我要学习咨询
# organization/forms.pyimport re
from django import forms
from operation.models import UserAskclass UserAskForm(forms.ModelForm):class Meta:model = UserAskfields = ['name', 'mobile', 'course_name']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 mobileelse:raise forms.ValidationError(u"手机号码非法", code="mobile_invalid")
class AddUserAskView(View):"""用户添加咨询"""def post(self, request):userask_form = UserAskForm(request.POST)if userask_form.is_valid():user_ask = userask_form.save(commit=True)# 如果保存成功,返回json字符串,后面content type是告诉浏览器返回的数据类型return HttpResponse('{"status":"success"}', content_type='application/json')else:# 如果保存失败,返回json字符串,并将form的报错信息通过msg传递到前端return HttpResponse('{"status":"fail", "msg":"添加出错"}', content_type='application/json')
7-9 机构详情展示
Django orm反向查找 course_org.course_set.all()[:4],利用课程机构查找课程
class OrgHomeView(View):'''机构首页'''def get(self,request,org_id):current_page = 'home'# 根据id找到课程机构course_org = CourseOrg.objects.get(id=int(org_id))course_org.click_nums += 1course_org.save()# 判断收藏状态has_fav = Falseif request.user.is_authenticated:if UserFavorite.objects.filter(user=request.user, fav_id=course_org.id, fav_type=2):has_fav = True# 反向查询到课程机构的所有课程和老师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,'has_fav':has_fav,})
BUG
集成DjangoUeditor时,出现错误,错误提示为:
from widgets import UEditorWidget,AdminUEditorWidget
ImportError: No module named ‘widgets’
经查发现,DjangoUeditor是基于Python 2.7的,对Python3的支持有问题。导致widgets.py文件出错,不能import。解决方法为修改widgets.py或者采用网上修改好的版本
参考
https://blog.csdn.net/zhch1979/article/details/104684122/
https://blog.csdn.net/weixin_43063753/article/details/87466738