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>驗 證 碼</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">×</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>用 戶 名</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>