慕学在线--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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章