settings.py
# -*- coding: utf-8 -*- #需要添加,識別中文編碼
import os
import sys #需要添加,用於apps包的歸集
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 獲取文件的目錄
sys.path.insert(0, os.path.join(BASE_DIR, 'apps')) #需要添加,用於將app放入apps文件夾下
# a. 新建python package文件夾
# b. 在pycharm中將apps mark爲Source Root,然後將所有的app移到apps文件夾下
# c. 最好是先將app放到apps文件夾之後,再migrate,如果migrate之後再移動文件夾,記得不要選擇search for references
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '46*2(7b65mpz3jj4#jymyz7##^sntg3!3h=$uv@_*rhfou%-hd'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True #正式上線,進入生產環境應該設爲False
ALLOWED_HOSTS = [] #正式上線應該設置爲?
# Application definition
AUTHENTICATION_BACKENDS = ( # 重載變量,錄。
# 自定義的類,重載了方法authentication(),用於自定義用戶通過用戶名,還是郵箱或者手機號登錄,django默認用用戶名登
'users.views.CustomBackend',
)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users', # 將非django自帶app列入,下同
'courses',
'orgs_teachs',
'operation',
'emailverify',
]
AUTH_USER_MODEL = 'users.UserProfile' # 需要添加
# a. 重載django自帶的user users爲app的名稱,UserProfile爲重載的AbstractUser子類
# b. 在users app中,定義 class UserProfile(AbstractUser):
# c. 需要在adminx.py文件中,xadmin.site.unregister(User) # 取消django自帶,然後再xadmin.site.register(UserProfile, UserProfileAdmin) # 重新註冊自定義的用戶
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mxonline2.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates')] #如爲空,需要添加,即添加模板的目錄
,
'APP_DIRS': True, #默認爲True,即系統會遍歷app裏的templates文件夾
'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', #上傳文件訪問模板,需要添加
],
},
},
]
WSGI_APPLICATION = 'mxonline2.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = { #此處爲原sqlite 修改爲mysql的格式
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxonline2',
'USER': 'root',
'PASSWORD': 'root',
'HOST': '127.0.0.1',
}
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'zh-hans' # 語言設置,設爲中文,此處由 LANGUAGE_CODE = 'en-us' 修改而來
TIME_ZONE = 'Asia/Shanghai' # 時區設置,此處由TIME_ZONE = 'UTC'修改而來
USE_I18N = True
USE_L10N = True
USE_TZ = False #此處應設置爲False,默認爲True,即接照時區設置爲UTC的時間; False爲取本地時間
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') #解決錯誤:You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path。只在collecstatic時有用,可以不設。
STATICFILES_DIRS = [ #此處STATICFILES_DIRS需要添加,寫相對路徑,而不是絕對路徑,在html文件開始添加 {% load staticfiles %},將靜態文件的路徑替換爲如{% static 'css/style.css' %}指明模板文件的目錄
os.path.join(BASE_DIR, 'static'),
]
MEDIA_URL = '/media/' # MEDIA_URL是指從瀏覽器訪問時的地址前綴
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 上傳文件保存的根目錄
# 配置郵件發送者
EMAIL_HOST = 'smtp.sina.com'
EMAIL_PORT = 25
EMAIL_HOST_USER = '[email protected]' # 登錄用戶
EMAIL_HOST_PASSWORD = 'litaifa001'
EMAIL_FROM = '[email protected]' # 指明發件人,應與郵件登錄用戶保持一致
EMAIL_USE_TLS = False
models.py
ORM 對象關係映射--Object Relational Mapping
Django 的對象關係映射器 (ORM) 位於框架的中心,介於數據模型(在 django.db.models.Model 類之上構建的 Python 類)和基礎關係數據庫對象之間。
簡單來說,Django的ORM機制就是把底層的關係數據庫和Python的面向對象特質聯繫起來。模型類映射數據表,對模型的操作直接反映到底層的數據表,即類代表了表,對象代表了其中的每一行,而對象的屬性則代表了列。所以定義數據模型之後,你將能夠通過映射到基礎數據庫中的對象的 Python 對象,來創建、檢索、更新以及刪除數據庫數據。
Django的ORM機制除了 支持PostgreSQL、MySQL 和 SQLite 之外, 還正式支持 Oracle 數據庫。
幾種特殊Field
- models.ForeignKey
- models.ManyToManyField
- models.OneToOneField一對一關係
模型繼承關係在model類中定義
Meta類中定義:
abstract = True #表示該模型爲抽象模型,即只用來被子模型類派生而不生成實際的模型(不生成數據表)
abstract = Proxy #表示該模型爲虛繼承,不生成數據表,但可以在admin站點中加載顯示數據查詢
- 每個Model都有一個objects屬性,而這個objects屬性具有以下方法:Model.objects.all() # 獲取所有對象的QuerySet Model.objects.filter() # 獲取滿足條件的對象的QuerySet model.objects.filter(user_id__in = user_ids_list) # 指的是所有user_id在user_ids_list列表中的實例 Model.objects.filter(name__icontains=keywords) # name字段中包含keywords,i不區分大小寫 Model.objects.exclude() # 獲取不滿足條件的對象的QuerySet Model.objects.get() # 獲取單個符合條件的對象的QuerySet,如果有多個或沒有符合條件的會拋出異常
查詢結果排序,並提取前5個
all_sorted_first = Person.objects.all().order_by('-first')[:5]
用Q()讓條件靈活組合,與 & | ~ 配合,形成組合條件
Person.objects.filter( Q(last="Doe") | (Q(last="Smith") & Q(first="John") & ~Q(middle__startswith="W")) )
用extra()提供其它的功能
## select提供簡單數據 # SELECT age, (age > 18) as is_adult FROM myapp_person; Person.objects.all().extra(select={'is_adult': "age > 18"}) ## where提供查詢條件 # SELECT * FROM myapp_person WHERE first||last ILIKE 'jeffrey%'; Person.objects.all().extra(where=["first||last ILIKE 'jeffrey%'"]) ## table連接其它表 # SELECT * FROM myapp_book, myapp_person WHERE last = author_last Book.objects.all().extra(table=['myapp_person'], where=['last = author_last']) ## params添參數 # !! 錯誤的方式 !! first_name = 'Joe' # 如果first_name中有SQL特定字符就會出現漏洞 Person.objects.all().extra(where=["first = '%s'" % first_name]) # 正確方式 Person.objects.all().extra(where=["first = '%s'"], params=[first_name])
adminx.py
xadmin 文檔說明
https://xadmin.readthedocs.io/en/latest/index.html安裝xadmin
方法一:通過pip安裝
方法二:原文件安裝- 登錄:github.com ,搜索xadmin #原碼託管站點
- 選擇sshwsfc/xadmin,進行下載
- 解壓後,將xadmin文件夾複製到項目中
- 需要根據requirement.txt 安裝依賴包 pip install -r requirements.txt
- 按照文檔將xadmin等app添加到setting INSTALLED_APPS 中
xadmin自定義
重新設置xadmin站點樣式。在users/xadmin.py中添加:
from xadmin import views class BaseSetting(object): # 基本設置類 enable_themes=True # 使主題功能可用 use_bootswatch=True # 加載可用主題 class GlobalSettings(object): # 全局設置類 site_title=u'慕學後臺管理系統' # 重設站點的標題 site_footer=u'慕學在線網' # 重設站點底部頁腳 menu_style='accordion' # 摺疊左邊菜單 xadmin.site.register(views.BaseAdminView,BaseSetting) # 註冊BaseSetting xadmin.site.register(views.CommAdminView,GlobalSettings) # 註冊GlobalSettings
將app顯示名顯示爲中文
a. 在app的apps.py文件中添加,如operation/apps.pyclass UsersConfig(AppConfig): name = 'users' verbose_name=u'用戶操作' # 添加內容,給app定義別名
b. 在app的init.py文件中添加
default_app_config="operation.apps.OperationConfig" # 添加默認設置 ,operation爲apps.py所在的app名稱
設置頁面顯示及功能設置
如:在courses/adminx.py文件中,class CourseAdmin(object): list_display=['name','desc'] # 在model首頁條目中顯示的字段 search_fields=['name','desc'] # 通過搜索框搜索的數據列的名字, 進行模糊查找 list_filter=['name','desc'] # 過濾器的字段列表 free_query_filter=True # 默認爲True,即用戶可以通過url鏈接進行自定義查找 ordering=['-clicknum',] # 定義默認,按照clicknum字段降序排列 readonly_fields=['name','desc'] # 定義name、desc默認爲只讀字段,不可以編輯 exclude=['favnum',] # 指定在編輯狀態下不顯示的字段列表,其中字段不與readonly_fields中字段衝突 refresh_times = (3, 5) # 顯示一個下拉列表, 用戶可以選擇3秒或5秒刷新一次頁面 list_editable = ['price', 'status', ...] # 列表可編輯功能,便於快速修改數據 報錯,需要測試 list_export = ('xls', xml', 'json') # 列出可導出的文件格式 show_detail_fields = ['group', 'father', ...] # 顯示字段的的詳細頁面 class OrgTeachAdmin(object): relfield_style='fk-ajax' # 當OrgTeach作爲其它model的外鍵,在編輯頁面中顯示爲搜索,替換原來的下拉選項
xadmin其它插件
data_charts = { # 定義數據圖表 "user_count": {'title': u"User Report", "x-field": "date", "y-field": ("user_count", "view_count"), "order": ('date',)}, "avg_count": {'title': u"Avg Report", "x-field": "date", "y-field": ('avg_count',), "order": ('date',)} } show_bookmarks # 默認爲True,顯示書籤 list_bookmarks = [{ # 定義默認書籤 'title': "Female", # 書籤的名稱, 顯示在書籤菜單中 'query': {'gender': True}, # 過濾參數, 是標準的 queryset 過濾 'order': ('-age'), # 排序參數 'cols': ('first_name', 'age', 'phones'), # 顯示的列 'search': 'Tom' # 搜索參數, 指定搜索的內容 }, {...}
重載user
a. models.py文件中定義自己的userfrom django.contrib.auth.models import AbstractUser class UserProfile(AbstractUser): nick_name = models.CharField(verbose_name=u'暱稱', max_length=10, default='') #.....
b. adminx.py中定義admin類,並將model類與admin類進行註冊
class UserProfileAdmin(UserAdmin): # 如果需要調整user 的頁面顯示及其它,需要重新定義UserAdmin的子類 # list_display = ('username', 'email') ...... xadmin.site.unregister(User) # 由於user之前已經默認註冊,需要取消註冊,並重新註冊 xadmin.site.register(UserProfile, UserProfileAdmin) #重新註冊自定義的用戶
c. 如果設置郵箱或用戶名都可以登錄,需要重載authenticate()方法
from django.contrib.auth.backends import ModelBackend from django.db.models import Q class CustomBackend(ModelBackend): # ModelBackend有authenticate方法 def authenticate(self, username=None, password=None, **kwargs): # 重載該函數 try: # get方法獲得一個對象,如果沒符合條件的對象,或有多條符合條件的對象都會報錯 # | 表示或的關係,如果(Q(username=username),Q(email=username)) 則表示與,要求兩個條件同時成立 user = UserProfile.objects.get(Q(username=username) | Q(email=username)) if user.check_password(password): # 父類函數,用於判斷密碼是否正確 return user else: return None except Exception as e: return None
d. 在settings.py文件中重載變量
AUTHENTICATION_BACKENDS=( # 重載變量 'users.views.CustomBackend', # 自定義的類,重載了方法authentication() ) # 重載user user_profile 爲app的名稱,UserProfile爲重載的AbstractUser子類 AUTH_USER_MODEL = 'user_profile.UserProfile'
在xadmin站點中添加菜單icon
a. 添加類屬性,如:class UserMessageAdmin(object): #..... model_icon = 'fa fa-cog' # 設置圖標字體
b. 在網站下載最新圖標:http://fontawesome.io/
c. 解壓下載文件,將fonts、css文件夾覆蓋xadmin下的文件夾,路徑:
d. 完成上述操作後,重啓程序,並按ctr+f5強制刷新
在頁面編輯添加外鍵的detail,如:
在/courses/adminx.py中定義類,class ChapterInline(object): # 增加類 model=Chapter # 對應的model ,course 應作爲chapter的外鍵 extra=0 # 默認添加的Chapter條目數 class ResourceInline(object): model= Resource extra=0 在/courses/adminx.py 中, class CourseAdmin(object): # 添加屬性 inlines,course爲chapter的外鍵 ..... #添加課程時,可以直接添加章節, Chapter和 CourseResource 均有外鍵指向Course inlines =[ChapterInline,ResourceInline]
在站點中,通過兩個頁面管理同一個數據表
在courses\model.py文件中添加
class BannerCourse(Course): # 需要繼承Course類 class Meta: verbose_name=u'輪播課程' verbose_name_plural=verbose_name proxy=True # 必須設置爲True,這樣後臺數據庫不會生成數據數據
在courses\adminx.py文件中添加:
class CourseAdmin(object): # 修改CourseAdmin,添加queryset函數 #...... def queryset(self): qs = super(CourseAdmin, self).queryset() # 調用父類的queryset()方法 qs = qs.filter(isbanner=False) # 只顯示字段isbanner爲False的數據 return qs class BannerCourseAdmin(object): # 添加queryset函數 ...... def queryset(self): qs = super(BannerCourseAdmin, self).queryset() qs = qs.filter(isbanner=True) # 只顯示字段isbanner爲True的數據 return qs xadmin.site.register(BannerCourse,BannerCourseAdmin) # 所有的admin如果需要在後臺管理頁面中顯示,需要register
其它常見功能
如果models的函數顯示在頁面,如果使用別名,需要在函數下面定義全局變量,如:
def get_zj_nums(self): #...... get_zj_nums.short_description=u'章節數‘ # 定義函數別名
models函數中定義html,如:
def go_to(self): from django.utils.safestring import mark_safe # 在頁面中顯示跳轉,而不是html代碼 return mark_safe("<a href='http://www.163.com'>跳轉</>") go_to.short_description=u'章節數' # 定義函數別名
在保存數據表時,自動統計數據,並保存。重載save_models(問題:是否應當重載course.save()),如:course\admin.py 文件下:
class CourseAdmin(object): # 在保存課程的時侯統計課程機構的課程數,此處course只通過後臺添加 ..... def save_models(self): # 方法可用,但需要解決刪除時重載函數。否則會造成數據刪除,而相應的統計數據未改變 obj = self.new_obj #獲取當前新增加或改變的 obj.save() # 先將新的數據保存 if obj.organization is not None: org = obj.organization # 獲得organization類 org.coursenum=Course.objects.filter(organization=org).count()( org.save()
urls.py
# -*- coding: utf-8 -*-
from django.conf.urls import url, include
from django.views.generic import TemplateView
import xadmin
from users.views import LoginView,EmailActiveView,RegisterView,ForgetPasswordView,ResetPasswordView
xadmin.autodiscover()
# version模塊自動註冊需要版本控制的 Model
# from xadmin.plugins import xversion
# xversion.register_models()
urlpatterns = [
url(r'^xadmin/', include(xadmin.site.urls)),
url(r'^captcha/', include('captcha.urls')), # 驗證碼url入口,每次點擊都會重生成一個隨機數
url(r'^register/$', RegisterView.as_view(), name='register'),
# 郵件激活鏈接,含註冊激活、重置密碼
url(r'^emailverify/(?P<active_type>\w+)//$', EmailActiveView.as_view(), name='email_verify'), # 作爲get()的參數傳入
]
templates
模板
顧名思意,就是一個通用的文檔格式,等待填充動態的數據,形成完整的文檔。在Django裏,模板通常用於制定HTML文檔,但是它還可以用於任何文本格式。模板過濾器
模板框架可以通過叫過濾器(filter)的機制來對context變量進一步處理。用管道符”|”,與Linux的管道符有類似的意義。:後面爲參數,參數用單引號或雙引號包起,
常用的管道符過濾有:a. str|slice:’9’ # 取str的前9個字符;
b. numb|divisibleby:’5’ # 判斷numb是否被5整除,能返回True,否則返回False
c. numb|add:5 # 返回numb+5
d. city.id|stringformat:”i” # 將數字轉換爲字符串
e. text|truncatechars:5 #它限定字符顯示長度,如果 text=‘ABCDEFGH’,顯示結果爲”AB…”
f. string|lower # 將字符串string轉換爲小寫控制標籤
除了{{ 變量名 }}之外,還有控制邏輯的標籤,它們都是{% xxxx %}格式的。
{% for item in list_or_tupe %} … {% endfor %}
{% if %} … {% endif %}
{% ifequal %} … {% endifequal %}
{% block block_name %} … {% endblock %}
{% extends file_name %}
{% include file_name %}for語句
for語句中提供一個局部變量{{forloop}},它可以提供許多有用的信息。如:
{{forloop.first}},boolean,表示是否是第一個元素
{{forloop.last}},boolean,表示是否是最後一個元素
{{forloop.counter}},int,表示循環計數:1,2,3…
{{forloop.counter0}},int,表示循環計數:0,1,2…block與extends
{% block block_name %}…{%endblock%}中間的部分叫block。
{% extends “file_name” %}表示當前這個模塊繼承於file_name這個模板。
block的特性是,如果有其它的模板extends該模板,如有同名的block,那麼就會用新
的block替代舊的block。這個有些類似C++裏的類繼承,子類可以重寫父類的虛函數。
*注:
1、html的繼承,還可B繼承A,C繼承B,如果B有新增加的block,需要寫在原來A中已經定義的block之內
2、子類的變量能傳遞到父類*include包含
{%include “file_name”%} 這很像C/C++裏的#inlcude,也像shell中的source,就是將指定文件的內容替換該語句。
include是將file_name的文件內容複製到當前位置,如果 file_name中含有{{ msg }}include後,msg將顯示msg的值
注:extend與include指令能接受字符串也能接受變量。這樣就可以讓模板動態地由上下文決定要包含或繼承於哪個模塊了。
views.py
views主要負責處理url加載時,向django傳遞數據或者顯示網頁
- LoginView(View)
LoginView(View)
from django.views.generic import View
from django.contrib.auth import authenticate,login #import 判斷是否登錄的函數authenticate,返回用戶名; login函數加載登錄信息
class LoginView(View):
def post(self, request):
username = request.POST.get('username', '') # get()中第一個參數username要與Form表單中input的name保持一致,第二個參數爲默認值
password = request.POST.get('password', '')
user = authenticate(username=username, password=password) # authenticate()已重載,成功返回user實例對象,失敗返回None
if user is not None: # 如果登錄成功
login(request,user) # login 用戶,login後,用戶處理登錄狀態
return render(request, 'index.html', {}) # 此處直接返回index.html,自動加載登錄信息,但相關數據並不加載,需要重定向 return HttpResponseRedirect(reverse('index'))
- LogoutView(View)
from django.http import HttpResponseRedirect
class LogoutView(View):
def get(self,request):
logout(request)
from django.core.urlresolvers import reverse
# 重定向後,顯index頁面,網址也變爲http://127.0.0.1:8000/,還有一個重要的好處是可以加載數據到模板中
return HttpResponseRedirect(reverse('index'))
# return render(request,'index.html') # 雖然顯示index頁面,但網址仍然爲http://127.0.0.1:8000/logout/
定義登錄驗證類,當view繼承該類後,如果用戶沒有登錄,則跳轉到登錄頁面
- 定義LoginRequiredMixin類
from django.contrib.auth.decorators import login_required from django.utils.decorators import method_decorator class LoginRequiredMixin(object): @method_decorator(login_required(login_url='/login/')) # 與登錄的路徑保持一致 def dispatch(self, request, *args, **kwargs): return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
- 繼承LoginRequiredMixin類
class UserCenterInfoView(LoginRequiredMixin, View): # url調用UserCenterInfoView類時,必須是登錄用戶,否則跳轉到登錄界面 def get(self, request): return render(request, 'usercenter-info.html', { })
判斷用戶是否登錄
a. 在views.py中if request.user.is_authenticated(): # 疑問:如果用戶未登錄,request.user 是None,是否不需要這樣判斷
b. 在html模板中
{% if request.user.is_authenticated %}
forms.py
表單是在web中,用戶與服務器交互的重要途徑,對於用戶提交的數據,服務器需要進行校驗,而Forms是初審,是把用戶提交的數據信息進行初步審覈後,再提交服務器,用以減輕服務器的壓力。
form在Django中扮演的角色有:
*驗證用戶提交的數據
*顯示form
驗證用戶提交的數據
- 在forms.py中定義form,如:
from django import forms class LoginForm(forms.Form): username = forms.CharField(required=True) # username必須與html中input的name保持一致 password = forms.CharField(required=True,min_length=8)
- 在views.py中使用LoginForm驗證
class LoginView(View): ..... def post(self, request): # 定義LoginForm的實例,並將POST作爲參數傳入,LoginForm根據定義,校驗字段 loginform= LoginForm(request.POST) if loginform.is_valid(): # 返回校驗是否通過,通過返回True,否則返回False。如果成功,則會有loginform.cleaned_data,如果失敗會有loginform.errors。這兩則只會存在一個,不會同時存在。 ..... else: return render(request, 'login.html', { 'loginform': loginform, # loginform內含錯誤信息 })
- 在html文件中顯示相關錯誤信息,如,login.html:
<div class="form-group marb20 {% if loginform.errors.username %}errorput{% endif %}"> <label>用 戶 名</label> # 錯誤,顯示紅框 <input name="username" id="account_l" type="text" placeholder="手機號/郵箱" /> </div> ...... <div class="error btns login-form-tips" id="jsLoginTips"> {% for key,error in loginform.errors.items %} # 遍歷錯誤信息條目 {{ key }} : {{ error }} # 輸出鍵、值,也可以根據需要進行調整 {% endfor %} {{ msg }} # 如果無msg返回值,html文件會忽略 </div>
顯示Form
在模板中顯示錶單
a. forms.py 中LoginForm同上
b. mytest.html文件中:{{ loginform }}
c. views.py文件中:class MyTestView(View): def get(self,request): loginform=LoginForm(label_suffix="=") # label_suffix 效果如下圖示標黃處 return render(request,'mytest.html',{ 'loginform':loginform.as_table(), # as_table()爲默認方法 })
顯示效果:
d. 其它
Form除了as_table()方法,還有as_ul()與as_p()方法,默認是as_table()方法。
Form是在 django/forms/forms.py 定義的。
打開forms.py文件,可以瞭解到,Form繼承於BaseForm。
BaseForm的定義大致如下:class BaseForm(object): def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None, initial=None, label_suffix=None): ... def as_table(self): ... def as_ul(self): ... def as_p(self): ... def __str__(self): return self.as_table() class Form(BaseForm): ... #### auto_id爲,控件的自動id格式。爲''或None表示不需要id。 label_suffix爲lable名稱的後綴,默認爲":",可以改。比如:label_suffix="=" prefix爲name的前綴,如果設置了prefix="aaa",那麼initial爲初始值字典。
forms中Field
Fields定義在django/forms/fields.py文件裏。
打開fields.py文件,可以看到除CharField外還有更多的Field供選擇:
Field
|–CharField
| |–RegexField
| |–EmailField
| |–URLField
| |–IPAddressField
| |–GenericIPAddressField
|--SlugField
–DecimalField
|--IntegerField
| |--FloatField
|
|–BaseTomporalField
| |–DateField
| |–TimeField
|--DateTimeField
–ImageField
|--FileField #file選擇文件
|
|–BooleanField #checkbox
|--NullBooleanField #select:Unknow,Yes,No
–MultipleChoiceField
|--ChoiceField #select
| |--TypedChoiceField
| |--FilePathField
|
|--TypedMultipleChoiceField
–MultiValueField
|--ComboField
`–SplitDateTimeField
# 每種Field有個默認的Widget。
class Field(object):
widget = TextInput
…
class EmailField(CharField):
widget = EmailInput
…
class FileField(Field):
widget = ClearableFileInput
…
Widget告訴Field生成哪種web控件。
我們也可以爲Field指定Widget。
比如登陸表單:class LoginForm(forms.Form): #email = forms.EmailField() email = forms.CharField(widget=widgets.EmailInput()) password = forms.CharField(widget=widgets.PasswordInput())
password這個域不能用明文顯示,所以更改了widget。
所有的Widget都定義在django/forms/widgets.py中。
有如下控件:
‘Media’, ‘MediaDefiningClass’, ‘Widget’, ‘TextInput’,
‘EmailInput’, ‘URLInput’, ‘NumberInput’, ‘PasswordInput’,
‘HiddenInput’, ‘MultipleHiddenInput’, ‘ClearableFileInput’,
‘FileInput’, ‘DateInput’, ‘DateTimeInput’, ‘TimeInput’, ‘Textarea’,
‘CheckboxInput’, ‘SplitDateTimeWidget’,
‘Select’, ‘NullBooleanSelect’, ‘SelectMultiple’, ‘RadioSelect’,
‘CheckboxSelectMultiple’, ‘MultiWidget’,
name = forms.CharField(widget=forms.TextInput(attrs={‘class’: ‘special’})) # 還可以通過widget 添加css屬性基於模型的表單,也可以用於驗證數據。
根據模型的定義來自動定義表單。如下:
from django import forms from models import Book class BookModelForm(froms.ModelForm): class Meta: model = Book
而Book的定義在models.py裏:
class Book(models.Model): isbn = models.CharField(max_length=50) title = models.CharField(max_length=200) author = models.ForeignKey('Author')
這樣以來,BookModelForm也有了與Book對應的isbn,title,author。
保存ModelForm
ModelForm與一般的Form的重要區別是,ModelForm具有save()功能。能將表單裏的數據
加入到數據庫,並返回一個Model對象。
爲了演示方便,我就不採用模板了。同樣是引用上面BookModelForm與Book:def add_book_view(request): book_form = forms.BookModelForm(request.GET) try: book_model = book_form.save() content = 'Add ' + book_model.title + ' success!' except: content = '' content += book_form.as_p() content += '' return HttpResponse(content)
並將add_book_view視圖的url指定爲r’^add-book/’。
第一次訪問/add-book/時,由於GET中沒有參數,所以在book_form.save()就會拋出異
常。在except中返回個表單給用戶。用戶填好後提交。這次再處理時,GET裏就有數據
了,所以book_form.save()正常,最後輸出success消息。
有時,我們在save()時僅僅是想驗證一下用戶的輸入,並不打算提交到數據爲。
這裏,只要save(commit=False)即可。
如此,在save(commit=False)時返回了book模型的對象。我們可以繼而對其進一步修改
,再保存到數據庫。#book_model = book_form.save() book_model = book_form.save(commit=False) #返回model對象 book_model.title = 'Balabala' book_model.save()
第一行只是驗證一下用戶的輸出是不是符合要求。然後對模型對象進行修改,最後才保
存到數據庫去。就這樣,在中間改了title,然後再保存到了數據庫。ModelForm顯示個別域
ModelForm默認情況下,與Model是一致的。但是很多時候,並不是模型中所有的域都要
讓用戶填的。我們可以選擇性地選擇或排除個別域。
這裏就要用到Meta的exclude或fields。exclude表示排除什麼域,而fields表示需要顯
示哪些域。二者不能同時使用。class BookModelForm(forms.ModelForm): class Meta: model = Book exclude = ('author') #表示不顯示author域 # ------------------------------------------------- class BookModelForm(forms.ModelForm): class Meta: model = Book fields = ('title', 'isbn') #表示只顯示title與isbn域
重寫ModelForm中的域
class BookModelForm(forms.ModelForm): isbn = forms.CharField(max_length=13) class Meta: model = Book
將Book中指定的CharField(max_length=50),改成了13。
- 新增ModelForm中的域
class BookModelForm(forms.ModelForm): review = forms.CharField() class Meta: model = Book
這樣以來,BookModelForm不僅只有Book中的域,還有review。