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。