慕學在線--1、django主要文件及模板基礎

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

    1. models.ForeignKey
    2. models.ManyToManyField
    3. 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

  1. xadmin 文檔說明
    https://xadmin.readthedocs.io/en/latest/index.html

  2. 安裝xadmin
    方法一:通過pip安裝
    方法二:原文件安裝

    1. 登錄:github.com ,搜索xadmin #原碼託管站點
    2. 選擇sshwsfc/xadmin,進行下載
    3. 解壓後,將xadmin文件夾複製到項目中
    4. 需要根據requirement.txt 安裝依賴包 pip install -r requirements.txt
    5. 按照文檔將xadmin等app添加到setting INSTALLED_APPS 中
  3. xadmin自定義

    1. 重新設置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
    2. 將app顯示名顯示爲中文
      a. 在app的apps.py文件中添加,如operation/apps.py

      class UsersConfig(AppConfig):
          name = 'users'
          verbose_name=u'用戶操作'       #  添加內容,給app定義別名

      b. 在app的init.py文件中添加

      default_app_config="operation.apps.OperationConfig"     #  添加默認設置 ,operation爲apps.py所在的app名稱
    3. 設置頁面顯示及功能設置
      如:在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的外鍵,在編輯頁面中顯示爲搜索,替換原來的下拉選項  
    4. 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'    # 搜索參數, 指定搜索的內容
              }, {...}
    5. 重載user
      a. models.py文件中定義自己的user

      from 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'
    6. 在xadmin站點中添加菜單icon
      a. 添加類屬性,如:

      class UserMessageAdmin(object):
      
      #.....
      
              model_icon = 'fa fa-cog'   # 設置圖標字體

      b. 在網站下載最新圖標:http://fontawesome.io/
      c. 解壓下載文件,將fonts、css文件夾覆蓋xadmin下的文件夾,路徑:
      1.png-2.5kB

      d. 完成上述操作後,重啓程序,並按ctr+f5強制刷新

    7. 在頁面編輯添加外鍵的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] 

      1.png-7kB

    8. 在站點中,通過兩個頁面管理同一個數據表

      • 在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
    9. 其它常見功能

      • 如果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

  1. 模板
    顧名思意,就是一個通用的文檔格式,等待填充動態的數據,形成完整的文檔。在Django裏,模板通常用於制定HTML文檔,但是它還可以用於任何文本格式。

  2. 模板過濾器
    模板框架可以通過叫過濾器(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轉換爲小寫

  3. 控制標籤

    • 除了{{ 變量名 }}之外,還有控制邏輯的標籤,它們都是{% 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

  1. 驗證用戶提交的數據

    • 在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>用&nbsp;戶&nbsp;名</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>
  2. 顯示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()爲默認方法
              })

      顯示效果:
      1.png-1.6kB

      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
      |--IntegerField
      | |--FloatField
      |
      –DecimalField
      |–BaseTomporalField
      | |–DateField
      | |–TimeField
      | --DateTimeField
      |--FileField #file選擇文件
      |
      –ImageField
      |–BooleanField #checkbox
      | --NullBooleanField #select:Unknow,Yes,No
      |--ChoiceField #select
      | |--TypedChoiceField
      | |--FilePathField
      |
      –MultipleChoiceField
      | --TypedMultipleChoiceField
      |--ComboField
      –MultiValueField
      `–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。

發佈了40 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章