django 线上教育平台开发记录

1、环境搭建

2、新建项目

  1)、首先通过 django-admin 新建一个项目,(例如项目名为mxonline)

    

django-admin startproject mxonline

  运行后会出现一个 mxonline的文件夹,这就是整个项目的文件夹,文件夹下的文件结构为:

  2)、进入到 mxonline下的settings.py 文件配置数据库,我自己是这样配置的

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': "mxonline",
        'USER':'root',
        'PASSWORD':'147896321',
        'HOST':'localhost',
        'PORT':'3306',
    }
}

   然后通过 migrate命令在数据库中会生成初始的数据表

python manage.py makemigrations

python manage.py migrate

  注意:如果makemigrations出错,一般都是数据库配置错误,请仔细检查

3、第一个django页面

  运行django

python manage.py runserver

  然后在浏览器打开 http://127.0.0.1:8000/,就可以看到我么第一个django页面了

4、app的model设计

  1)新建一个叫users的app,新建完成后,会出现一个和mxonline同级的users文件夹,这就是我们的users 的app

python manage.py startapp users

  2)设计user的model(users/model.py)

  

# -*- coding: utf-8 -*-
from django.db import models from django.contrib.auth.models import AbstractUser from django.utils.encoding import python_2_unicode_compatible python_2_unicode_compatible class UserProfile(AbstractUser): nickname = models.CharField(max_length=16, verbose_name=u'昵称', default="") birday = models.DateField(verbose_name=u'生日', null=True, blank=True) gender = models.CharField(choices=((‘male’,u''),('female',u'')), default="female") address = models.CharField(max_length=64, default="") mobile = models.CharField(max_length=11, null=True, blank=True) image = mdoels.ImageField(upload_to="image/%Y/%m", default = u'image/default.png', max_length=64) class = Meta: verbose_name = "用户信息" verbose_name_plural = verbose_name def __str__(self): return self.username
#other models
...

  3)注册 app,并设置 AUTH_USER_MODEL

  在settings.py中的 INSTALLED_APP中添加 users

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
]

AUTH_USER_MODEL = "users.UserProfile"

  4)因为models中用到了 ImageField,需要安装pillow

    进入虚拟环境,安装pillow

pip  install pillow

  5)同步user表到数据库

python manage.py makemigrations

python manage.py migrate

5、如何将多个app放到一个文件夹apps(apps所在位置为项目根目录)下:

  在settings.py文件中,insert apps路径即可

  首先  

import sys

  然后在 BASE_DIR 后面添加apps

sys.path.insert(0, os.path.join(BASE_DIR, 'apps'))

 6、创建后台admin的用户

python manage.py createsuperuser admin

7、settings.py相关设置

LANGUAGE_CODE = 'zh-hans'    #修改django后台为中文

TIME_ZONE = 'Asia/Shanghai'    #修改时区

USE_TZ = False  #本地时间,True为国际时间

8、xadmin(django最好使用1.9.8的版本,pip install django=1.9.8,当然自动安装很简单)

  1)settings.py修改

pip install xadmin

  修改settings.py中的配置

  将 xadmin 和 crispy_forms(注意这里的-改为_) 加入到INSTALLED_APPS中

  2)项目urls.py的修改

# from django.contrib import admin
import xadmin

urlpatterns = [
    # url(r'^admin/', admin.site.urls),
    url(r'^xadmin/', xadmin.site.urls),
]

9、手动安装 xadmin

  1)、下载xadmin源码,xadmin的源码是保存在github上的,在github首页进行搜索,然后下载到本地

  

  2)、新建extra_apps(第三方app)文件夹(与apps文件夹同级),并在该文件夹下新建__init__.py文件(标识为一个可被调用的包)  

    然后打开xadmin的源码,将里面的xadmin目录复制到extra_apps文件夹下

  3)、最后在settings.py文件中将extra_apps添加到项目路径

sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps'))

  注意:本人在手动安装后运行项目时,出现了下面的错误

from future.utils import iteritems
ImportError: No module named future.utils

  通过网上查询,在github上下载了future相关代码

  然后复制到项目对应虚拟环境的目录下

  我自己对应的目录为:C:\Users\Administrator\Envs\mxonline\Lib\site-packages\

  再次运行项目,又出现ImportError: No module named six 的错误,然后可以通过pip install six的方式解决

  最后, 同步xadmin相关数据表到数据库

python manage.py makemigrations
python manage.py migrate

  项目终于成功启动!!!

10、django邮箱验证登录的实现方式

  views.py文件中,在登录函数上面重写CustomBackend类:

from django.contrib.auth.backends import ModelBackend
from django.db.models import Q

from .models import UserProfile

class CustomBackend(ModelBackend):
    def authenticate(self, username=None, password=None, **kwargs):
        try:
            user = UserProfile.objects.get(Q(username=username)|Q(email=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None

  然后在settings.py中添加:

AUTHENTICATION_BACKENDS = (
    'users.views.CustomBackend',
)

 11、Django验证码 django-simple-captcha 的使用

  1)、安装和配置

  通过pip install 快捷安装

pip install  django-simple-captcha

  将captcha添加到settings.py文件中的 INSTALLED_APPS中

  同步响应的配置到数据库中(同步后数据库会新建一个名为captcha_captchastore的数据表)

python manage.py migrate

  最后在项目的urls.py文件中添加:

urlpatterns += patterns('',
    url(r'^captcha/', include('captcha.urls')),
)

  2)、添加到表单并在视图函数或试图类验证

  添加到表单

class RegisterForm(forms.Form):
    password = forms.CharField(required=True, min_length=6)
    email = forms.EmailField()
    captcha = captcha = CaptchaField(error_messages={"invalid":u"验证码错误"})

  模板中添加以显示出验证码

<div class="form-group marb8 captcha1 {% if register_form.errors.captcha %}errorput{% endif %}">
    <label>&nbsp;&nbsp;</label>
    {{ register_form.captcha }}
</div>

  验证:

class 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():
     ...

 12、邮箱验证

  1)、首先配置settings.py

EMAIL_HOST = 'smtp.163.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = "abc@163.com"
EMAIL_HOST_PASSWORD = '123456'
EMAIL_USE_TLS = False
EMAIL_FROM = "abc@163.com"

  2)、在app的同级目录中新建一个utils的文件夹(记得新建一个__init__.py文件,将文件夹标记为可引用的包),在utils下新建email_send.py文件用于通过邮箱发送验证信息

email_send.py

# -*- coding: utf-8 -*-

from random import Random

from django.core.mail import send_mail

from users.models import EmailVerfyRecord
from mxonline.settings import EMAIL_FROM


def random_str(randomlength=8):
    str = ''
    chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
    length = len(chars) - 1
    random = Random()
    for i in range(randomlength):
        str += chars[random.randint(0, length)]
    return str

def send_email_verification_code(email, send_type="register"):
    email_record = EmailVerfyRecord()
    #生成一个16位数字的验证码
    code = random_str(16)
    email_record.code = code
    email_record.email = email
    email_record.send_type = send_type
    email_record.save()

    if send_type == 'register':
        email_title = "暮雪在线网注册激活链接"
        email_body = '请点击链接激活账号:http://127.0.0.1:8000/active/{0}'.format(code)

        send_status = send_mail(email_title, email_body, EMAIL_FROM, [email])
        if send_status:
            pass

  3)、在注册的处理函数中调用email_send.py中的 send_email_verification_code()函数,以发送验证信息到新用户的注册邮箱

class RegisterView(View):
    def get(self, request):
    ...
    def post(self, request):
        register_form = RegisterForm(request.POST)
        if register_form.is_valid():
            username = request.POST.get("email", "")
            ...

        user = UserProfile()
        user.username = username
        user.email = username
        user.password = make_password(password)
        user.is_active = False  #注册的时候先将该字段设置为false,通过邮箱验证后再设置为True
        user.save()

            send_email_verification_code(username, 'register')
            return render(request, 'login.html')
        else:
            return render(request, 'register.html', {'register_form':register_form})

  4)、可以看到我们发送给新用户的访问链接中是通过active/ 加上一个16位的随机码来访问的,所以需要在urls.py 和 views.py中添加相应的激活处理

urls.py

urlpatterns += url(r'^active/(?P<active_code>[a-zA-Z0-9]{16})/$', ActiveUserView.as_view(), name='active_user')

views.py

class ActiveUserView(View):
    def get(self, request, active_code):
        all_record = EmailVerfyRecord.objects.filter(code=active_code)
        for record in all_record:
            email = record.email
            user = UserProfile.objects.get(email=email)
            user.is_active = True
            user.save()
        return render(request, 'login.html')

 13、模板继承:

  注意:模板继承中的 static 标签,即使父模板中load了一次,子模板中还是要load

  子模板中记得要重新加载static标签        {% load staticfiles %}

14、关于上传图片 media相关的设置

  例如某个model 配置了 image这个字段,用于设置用户头像,是这么定义的:

image = models.ImageField(upload_to='org/%Y%m', verbose_name=u'logo')

  upload_to指明了上传的路径,然后还需要在settings.py文件中,TEMPLATES添加media的配置,并设置MEDIA_URL 和 MEDIA_ROOT

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',
                'django.core.context_processors.media',
            ],
        },
    },
]
...
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

  最后在项目的urls.py文件中做如下设置

  

from organization.views import OrgView
from mxonline.settings import MEDIA_ROOT

urlpatterns += url(r'^media/(?P<path>.*)$', serve, {"document_root":MEDIA_ROOT})

  完成!

 15、jQuery提示parsererror错误的解决办法

   原因:从服务端返回的json不合法,比如这样的形式,{'name':'xxxx'}。必须是双引号,{"name":"xxxx"}

 这是后端返回给页面的部分代码:json文本部分使用了单引号! 

class AddUserAskView(View):
    def post(relf, request):
        userask_form = UserAskForm(request.POST)
        if userask_form.is_valid():
            user_ask = userask_form.save(commit=True)   #UserAskForm为一个ModelForm类,相较于Form类,它可以直接提交表单数据到数据库
            return HttpResponse(“{‘status’:'success'}”, content_type='application/json')
        else:
            return ...

  ajax代码如下

<script>
    $(function(){
        $('#jsStayBtn').on('click', function(){
            $.ajax({
                cache: false,
                type: "POST",
                url:"{% url 'org:add_ask' %}",
                data:$('#jsStayForm').serialize(),
                async: true,
                success: function(data) {
                    if(data.status == 'success'){
                        $('#jsStayForm')[0].reset();
                        alert("提交成功")
                    }else if(data.status == 'fail'){
                        $('#jsCompanyTips').html(data.msg)
                    }
                },
                error: function(){
                    alert(arguments[1]);
                }
            });
        });
    })

</script>

  结果success函数一直不执行,最后添加了error函数后,提示parse error的异常

  最后发现问题,HttpResponse() 中的json文本,一定要用双引号

return HttpResponse('{"status":"success"}', content_type='application/json')

16、404页面配置

  1、设置settings.py 文件中 DEBUG 为 false

DEBUG = False

  2、将DEBUG模式为True的时候,django可以根据设置的STATICFILES_DIRS 取到的静态文件,当设置为false后,这种访问静态文件的方式将会失效(网站部署的时候,静态文件都是通过第三方【apache、nginx等】来代理的。),为了能正常访问静态文件,可以通过配置static的访问配置url和访问函数。

  1)首先在settings.py文件中 ,配置STATIC_ROOT

STATIC_ROOT = os.path.join(BASE_DIR, 'static')

  2)在项目文件的urls.py文件中,添加static的urls配置

from mxonline.settings import  STATIC_ROOT

url(r'^static/(?P<path>.*)$', serve, {"document_root":STATIC_ROOT}),

  3)在views.py文件中添加404页面的处理函数

#全局404处理函数
def page_not_found(request):
    response = render_to_response('404.html',{})
    response.status_code = 404
    return response

  4)最后在配置url的  urls.py文件中添加404页面的全局配置

handler404 = 'users.views.page_not_found'

17、xadmin 使用的图标来自第三方库 awesome

18、xadmin 中使用ueditor(DjangoEditor源码使用文档)

  1、安装

pip install DjangoUeditor

  2、在settings.py文件中,添加DjangoUeditor 到INSTALL_APPS里面

  3、urls.py文件中配置url

  

url(r'^ueditor/',include('DjangoUeditor.urls' )),

  4、models.py 中使用,并在xadmin注册的将其添加到 style_fields

  models.py

from DjangoUeditor.models import UEditorField
class Blog(models.Model):
    Name=models.CharField(,max_length=100,blank=True)
    detail=UEditorField(u'内容    ',width=600, height=300, toolbars="full", imagePath="", filePath="", upload_settings={"imageMaxSize":1204000},
             settings={},command=None,event_handler=myEventHander(),blank=True)

   adminx.py

class CourseAdmin(object):
  ...#指定在编辑状态不显示的字段
    exclude = ['fav_nums']
    #添加课程时,可以直接添加章节, Lesson 和 CourseResource 均有外键指向Course
    inlines = [LessonInline, CourseResourceInline]

    style_fields = {'detail':'ueditor'} 

  5、xadmin下的plugins文件夹中添加ueditor.py文件,然后在__init__.py中的 PLUGINS里面添加 'ueditor'

  ueditor.py文件内容为:

import xadmin
from django.db.models import TextField
from xadmin.views import BaseAdminPlugin, ModelFormAdminView, DetailAdminView
from DjangoUeditor.models import UEditorField
from DjangoUeditor.widgets import UEditorWidget
from django.conf import settings


class XadminUEditorWidget(UEditorWidget):
    def __init__(self,**kwargs):
        self.ueditor_options=kwargs
        self.Media.js = None
        super(XadminUEditorWidget,self).__init__(kwargs)


class UeditorPlugin(BaseAdminPlugin):
    def get_field_style(self, attrs, db_field, style, **kwargs):
        if style == 'ueditor':
            if isinstance(db_field, UEditorField):
                widget = db_field.formfield().widget
                param = {}
                param.update(widget.ueditor_settings)
                param.update(widget.attrs)
                return {'widget': XadminUEditorWidget(**param)}
            if isinstance(db_field, TextField):
                return {'widget': XadminUEditorWidget}
        return attrs
    def block_extrahead(self, context, nodes):
        js = '<script type="text/javascript" src="%s"></script>' % (settings.STATIC_URL + "media/ueditor/ueditor.config.js")
        js += '<script type="text/javascript" src="%s"></script>' % (settings.STATIC_URL + "media/ueditor/ueditor.all.min.js")
        nodes.append(js)


xadmin.site.register_plugin(UeditorPlugin, DetailAdminView)
xadmin.site.register_plugin(UeditorPlugin, ModelFormAdminView)

  __init__.py

PLUGINS = (
     ...
    'sortablelist',
    'ueditor',
)

  6、最后需要在展示富文本的地方关掉自动转义

{% autoescape off %}
{{ course.detail }}
{% endautoescape %}

19、xadmin插件开发之 excel导入

  1、xadmin/plugins中新建excel.py文件

  excel.py

# -*- coding: utf-8 -*-
import xadmin
from xadmin.views import BaseAdminPlugin, ListAdminView
from django.template import loader

#excel导入功能
class ListImportExcelPlugin(BaseAdminPlugin):
    import_excel = False

    def init_request(self, *args, **kwargs):
        return bool(self.import_excel)  #返回True才会加载插件

    def block_top_toolbar(self, context, nodes):    #显示自己的html
        nodes.append(loader.render_to_string('xadmin/excel/model_list.top_toolbar.import.html', context_instance=context))

xadmin.site.register_plugin(ListImportExcelPlugin, ListAdminView)

  2、配置对应的html模板文件

  在xadmin/templates  下新建excel文件夹,然后新建一个html文件,命名为

  model_list.top_toolbar.import.html,

{% load i18n %}
<div class="btn-group export">
  <a class="dropdown-toggle btn btn-default btn-sm" data-toggle="dropdown" href="#">
    <i class="icon-share"></i> 导入 <span class="caret"></span>
  </a>
  <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
      <li><a data-toggle="modal" data-target="#export-modal-import-excel"><i class="icon-circle-arrow-down"></i> 导入 Excel</a></li>
  </ul>
    <script>
        function fileChange(target){
//检测上传文件的类型
            var imgName = document.all.submit_upload.value;
            var ext,idx;
            if (imgName == ''){
                document.all.submit_upload_b.disabled=true;
                alert("请选择需要上传的 xls 文件!");
                return;
            } else {
                idx = imgName.lastIndexOf(".");
                if (idx != -1){
                    ext = imgName.substr(idx+1).toUpperCase();
                    ext = ext.toLowerCase( );
{#                    alert("ext="+ext);#}
                    if (ext != 'xls' && ext != 'xlsx'){
                        document.all.submit_upload_b.disabled=true;
                        alert("只能上传 .xls 类型的文件!");
                        return;
                    }
                } else {
                    document.all.submit_upload_b.disabled=true;
                    alert("只能上传 .xls 类型的文件!");
                    return;
                }
            }
        }
    </script>
    <div id="export-modal-import-excel" class="modal fade">
      <div class="modal-dialog">
        <div class="modal-content">
          <form method="post" action="" enctype="multipart/form-data">
              {% csrf_token %}
          <div class="modal-header">
            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
            <h4 class="modal-title">导入 Excel</h4>
          </div>
          <div class="modal-body">
               <input type="file" onchange="fileChange(this)" name="excel" id="submit_upload">

          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">{% trans "Close" %}</button>
            <button class="btn btn-success" type="submit" id="submit_upload_b"><i class="icon-share"></i> 导入</button>
          </div>
          </form>
        </div><!-- /.modal-content -->
      </div><!-- /.modal-dalog -->
    </div><!-- /.modal -->

</div>

  3、在plugins/__init__.py文件中添加excel(处理excel导入的文件名称)

  

PLUGINS = (
    ...
    'ueditor',
    'excel',
)

  4、在adminx.py文件中对应model注册的时候,设置 import_excel 并重写post()方法以将上传的excel文件解析,最后保存到数据库对应的表中

  这是我的xadmin.py文件中添加了了导入功能的Course的处理

  xlrd第三方库可以通过 pip install xlrd来安装

import xlrd
from .models import Course
... 

class CourseAdmin(object):
    #Course 中的方法 get_lesson_num 也可以放在展示列表中
    list_display = ['name','desc','detail','degree','learn_time','students','fav_nums','image','click_nums','get_lesson_num','go_to','add_time']
    search_fields = ['name','desc','detail','degree', 'students','fav_nums','image','click_nums']
    list_filter = ['name','desc','detail','degree','learn_time','students','fav_nums','image','click_nums','add_time']
    #根据点击排序
    ordering = ['-click_nums']
    #指定只读(不可修改)的字段
    readonly_fields = ['click_nums']
    #可直接在列表页编辑的字段
    list_editable = ['degree', 'desc']
    #指定在编辑状态不显示的字段
    exclude = ['fav_nums']
    #添加课程时,可以直接添加章节, Lesson 和 CourseResource 均有外键指向Course
    inlines = [LessonInline, CourseResourceInline]

    style_fields = {'detail':'ueditor'}

    #import_excel位True,excel导入,会覆盖插件中(plugins/excel.py)import_excel的默认值
    import_excel = True
    #将导入的excel文件内容存入数据库的course表中
    def post(self, request, *args, **kwargs):
        if 'excel' in request.FILES:
            wb = xlrd.open_workbook(filename=None, file_contents=request.FILES['excel'].read())
            table = wb.sheets()[0]
            row = table.nrows
            sql_list = []
            org_id_list = []
            for i in xrange(1,row):
                col = table.row_values(i)
                sql = Course(
                    degree=col[0],
                    learn_time=col[1],
                    detail=col[2],
                    desc=col[3],
                    students=col[4],
                    fav_nums=col[5],
                    name=col[6],
                    image=col[7],
                    click_nums=col[8],
                    course_org_id=col[10],
                    category=col[11],
                    tag=col[12],
                    teacher_id=col[13],
                    learn_what=col[14],
                    need_know=col[15],
                    notice=col[16],
                    is_banner=col[17],
                )
                sql_list.append(sql)
                org_id_list.append(col[10])
            Course.objects.bulk_create(sql_list)

            #更新excel文件中机构包含的课程数
            for id in org_id_list:
                org = CourseOrg.objects.get(id=int(id))
                org_course_nums = org.course_set.all().count()
                org.course_nums = org_course_nums
                org.save()

        return super(CourseAdmin, self).post(request, args, kwargs)

    #页面定时刷新,在页面选择
    # refresh_times = [3,5]

    #页面展示的数据过滤(只展示符合条件的数据)
    def queryset(self):
        course_ = super(CourseAdmin, self).queryset()
        course_ = course_.filter(is_banner=False)
        return course_

    #保存课程时统计课程机构的课程数
    def save_models(self):
        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()

...

 20、csrf_token 在html 和 js 中写法不一样

  1、js中 当做模板变量  应该用 {{ csrf_token }}

<script type="text/javascript">
function add_fav(current_elem, fav_id, fav_type){
    $.ajax({
        cache: false,
        type: "POST",
        url:"{% url 'org:add_fav' %}",
        data:{'fav_id':fav_id, 'fav_type':fav_type},
        async: true,
        beforeSend:function(xhr, settings){
            xhr.setRequestHeader("X-CSRFToken", "{{ csrf_token }}");
        },
        success: function(data) {
      ...
        },
    });
}

$('#jsLeftBtn').on('click', function(){
    add_fav($(this), {{ course.id }}, 1);
});

$('#jsRightBtn').on('click', function(){
    add_fav($(this), {{ course.course_org.id }}, 2);
});


</script>

  2、html中当做模板标签,应该用 {% csrf_token %}

<form action="/login/" method="post" autocomplete="off">
    <div class="form-group marb20 {% if login_form.errors.username %}errorput{% endif %}">
        <label>用&nbsp;户&nbsp;名</label>
        <input name="username" id="account_l" type="text" placeholder="手机号/邮箱" />
    </div>
  ...
     <input class="btn btn-green" id="jsLoginBtn" type="submit" value="立即登录 > " />
<input type='hidden' name='csrfmiddlewaretoken' value='5I2SlleZJOMUX9QbwYLUIAOshdrdpRcy' />
{% csrf_token %}
</form>
原文地址:https://www.cnblogs.com/justbreaking/p/7044483.html