Django【基础篇】
Python的WEB框架有Django、Tornado、Flask 等多种,Django相较与其他WEB框架其优势为:大而全,框架本身集成了ORM、模型绑定、模板引擎、缓存、Session等诸多功能。
基本配置
1、创建django程序
- 终端命令:django-admin startproject sitename
- IDE创建Django程序时,本质上都是自动执行上述命令
上述的sitename是自己定义的项目名称!
其他常用命令:
python manage.py runserver 0.0.0.0:port
python manage.py startapp appname
python manage.py syncdb
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
2、程序目录
settings.py 放配置文件
urls.py 存放路由系统(映射)
wsgi.py 让你做配置:wsgi有多重一种uwsgi和wsgi,你用那种wsgi来运行Django,一般不用改只有你用到的时候再改
manage.py 就是Django的启动管理程序
以上配置文件,如果是初学者当创建完project后都不要修改,因为涉及到很多配置文件需要修改
3、project和app的概念
project是一个大的工程,它可以包括很多个app就像手机里可以安装多个app应用一样。这些app之间是互不影响的。它们是对我们建立的project的分类。
''' Project --web (前台功能) --administrator (后台管理功能) 一个Project有多个app,其实他就是对你大的工程的一个分类 '''
4、在终端可以用以下语句创建app
python manage.py startapp app01
app里面的admin 是提供了后台管理的平台,test是用来测试的!
admin后台管理:
同步数据库
python manage.py syncdb #注意:Django 1.7.1及以上的版本需要用以下命令 python manage.py makemigrations python manage.py migrate
创建超级用户
python manage.py createsuperuser
输入你要设置的用户名和密码,然后启动Django,然后输入RUL/admin即可:http://127.0.0.1:8000/admin/
路由系统
1、每个路由规则对应一个view中的函数
在urls.py里添加RUL跳转
from django.conf.urls import url from django.contrib import admin #首先得导入App里面的views(Django是MTV框架 Views保存路由规则) from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^home/', views.home), ]
并在app里views.py里添加函数
from django.shortcuts import render #Django 在返回的时候需要一层封装,需要导入HttpResponse from django.shortcuts import HttpResponse # Create your views here. def home(request): #使用HttpRespons 封装返回信息 return HttpResponse('<h1>Hello Home</h1>')
django中的路由系统和其他语言的框架有所不同,在django中每一个请求的url都要有一条路由映射,这样才能将请求交给对一个的view中的函数去处理。其他大部分的Web框架则是对一类的url请求做一条路由映射,从而是路由系统变得简洁。
启动:两种方式一种直接在IDE运行另一种命令行启动:python manage.py runserver 127.0.0.1:6666
测试访问即可。(如果在里面加中文注释不要忘记加编码配置!)
返回html:
#/usr/bin/env python #-*- coding:utf-8 -*- from django.shortcuts import render #Django 在返回的时候需要一层封装,需要导入HttpResponse from django.shortcuts import HttpResponse # Create your views here. def home(request): #他内部做了几步操作 #找到home.html #读取home.html返回给用户 return render(request,'home.html')
模板
1、模板的执行
模版的创建过程,对于模版,其实就是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户。
上面已经可以正常的返回html了,我们要给他使用模板语言进行渲染怎么渲染呢?和上面的jinja2是一样的。
#/usr/bin/env python #-*- coding:utf-8 -*- from django.shortcuts import render #Django 在返回的时候需要一层封装,需要导入HttpResponse from django.shortcuts import HttpResponse # Create your views here. def home(request): #他内部做了几步操作 #找到home.html #读取home.html返回给用户 #定义一个字典然后传给render dic = {'name':'luotianshuai','age':'18','user_list':['shuai','ge','hen','shuai'],} return render(request,'home.html',dic)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Shuaige</title> </head> <body> <div style="background-color:red ;height: 100px"> {{ name }} {{ age }} </div> <div style="background-color:blue ;height: 100px"> <ul> {% for iterm in user_list %} <li> {{ iterm }} </li> {% endfor %} </ul> </div> <div style="background-color:green ;height: 100px"> {% if name == 'luotianshuai' %} <div style="background-color:red">Shuai</div> {% else %} <div style="background-color:blue">Ge</div> {% endif %} </div> </body> </html>
2、模板语言
模板中也有自己的语言,该语言可以实现数据展示
- {{ item }}
- {% for item in item_list %} <a>{{ item }}</a> {% endfor %}
forloop.counter
forloop.first
forloop.last - {% if ordered_warranty %} {% else %} {% endif %}
- 母板:{% block title %}{% endblock %}
子板:{% extends "base.html" %}
{% block title %}{% endblock %} - 帮助方法:
{{ item.event_start|date:"Y-m-d H:i:s"}}
{{ bio|truncatewords:"30" }}
{{ my_list|first|upper }}
{{ name|lower }}
上面的方法显然是不够用的,我们可以自定义
3、自定义模板语言
3.1、在app中创建templatetags模块
3.2、创建任意 .py 文件,如:xx.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #!/usr/bin/env python 2 #coding:utf-8 3 from django import template 4 from django.utils.safestring import mark_safe 5 from django.template.base import resolve_variable, Node, TemplateSyntaxError 6 7 register = template.Library() 8 9 @register.simple_tag 10 def my_simple_time(v1,v2,v3): 11 return v1 + v2 + v3 12 13 @register.simple_tag 14 def my_input(id,arg): 15 result = "<input type='text' id='%s' class='%s' />" %(id,arg,) 16 return mark_safe(result)
3.3、在使用自定义simple_tag的html文件中导入之前创建的 xx.py 文件名
1 {% load xx %}
3.4、使用simple_tag
1 {% my_simple_time 1 2 3%} 2 {% my_input 'id_username' 'hide'%
3.5、在settings中配置当前app,不然django无法找到自定义的simple_tag
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', )
4、母版
3.1、
母版的概念就是,当我们访问某网站的时候,上面的标签不动只有下面的内容是变动的
上面不动的标题就是用母版做出来的,然后点击进入其他页面的时候变动的是下面的内容,这个时候只需要子版去继承母版就可以了。
下面是母版的配置:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 <style> 7 .header{ 8 height: 48px; 9 background-color: red; 10 } 11 .body{ 12 background-color: #dddddd; 13 } 14 .body .menu{ 15 background-color: green; 16 float: left; 17 width: 20%; 18 } 19 .body .content{ 20 background-color: aquamarine; 21 float: left; 22 width:70%; 23 } 24 </style> 25 </head> 26 <body> 27 <div class="header"><h1>LOGO</h1></div> 28 <div class="body"> 29 <div class="menu">左侧菜单</div> 30 <div class="content"> 31 {#可变的子版内容,这个content和class content无关#} 32 {% block content %} {% endblock %} 33 </div> 34 </div> 35 </body> 36 </html>
子版的内容:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 {% extends 'master/master_templates.html' %} 2 3 {% block content %} 4 <h1>NAME LuoTianShuai</h1> 5 {% endblock %}
extends 集成那个母板 ,在加一个block content 来书写变化的内容。
3.2、增加URL路由和函数
def son(request): return render(request,'son_html.html')
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^home/', views.home), url(r'^son/', views.son), ]
建议不要使用多个母版
写多个模板的时候可以用导入标签的方式
4、创建一个include目录(名字可以随意定义),下面创建需要的html文件,里面写上要导入的内容
<h1>输入组合</h1> <input type="text"/> <input type="text"/> <input type="text"/> <input type="text"/> <input type="text"/>
然后在html中导入
{% extends 'master/master_templates.html' %} {% block content %} <h1>NAME LuoTianShuai</h1> {% include 'include/simple_input.html' %} {% endblock %}
公共的模块使用include的方法非常方便!
Django静态文件配置
把所有的静态都放在static目录下,比如:css、js、imgs、等
common.css里写相关的css文件
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/css/common.css"> {# 然后在模板里,我们也会写一个block,如果子版里有需要使用自己的css样式可以自己定义#} {% block css %} {% endblock %} </head> <body> <div class="header"><h1>LOGO</h1></div> <div class="body"> <div class="menu">左侧菜单</div> <div class="content"> {#可变的子版内容,这个content和class content无关#} {% block content %} {% endblock %} </div> </div> {# 公共的js写到母版中,如果某一个模板里使用自己的js,在写一个block即可#} {% block js %} {% endblock %} </body> </html>
注:在母版里引入了相应的css和js之后,子版里是默认继承的。如果某个子版想独立使用它自己的js,我们可以通过:{% block css %} {% endblock %} || {% block js %} {% endblock %}来定义!
2、配置引入static目录,在settings里,否则无法使用static目录下的静态文件,因为他找不到路径!需要告诉django
STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static'), )
Model
到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞:
- 创建数据库,设计表结构和字段
- 使用 MySQLdb 来连接数据库,并编写数据访问层代码
- 业务逻辑层去调用数据访问层执行数据库操作
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 import MySQLdb 2 3 def GetList(sql): 4 db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') 5 cursor = db.cursor() 6 cursor.execute(sql) 7 data = cursor.fetchall() 8 db.close() 9 return data 10 11 def GetSingle(sql): 12 db = MySQLdb.connect(user='root', db='wupeiqidb', passwd='1234', host='localhost') 13 cursor = db.cursor() 14 cursor.execute(sql) 15 data = cursor.fetchone() 16 db.close() 17 return data
django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)。
PHP:activerecord
Java:Hibernate
C#:Entity Framework
django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。
1、创建Model,之后可以根据Model来创建数据库表
1 from django.db import models 2 3 class userinfo(models.Model): 4 name = models.CharField(max_length=30) 5 email = models.EmailField() 6 memo = models.TextField()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 1、models.AutoField 自增列 = int(11) 2 如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。 3 2、models.CharField 字符串字段 4 必须 max_length 参数 5 3、models.BooleanField 布尔类型=tinyint(1) 6 不能为空,Blank=True 7 4、models.ComaSeparatedIntegerField 用逗号分割的数字=varchar 8 继承CharField,所以必须 max_lenght 参数 9 5、models.DateField 日期类型 date 10 对于参数,auto_now = True 则每次更新都会更新这个时间;auto_now_add 则只是第一次创建添加,之后的更新不再改变。 11 6、models.DateTimeField 日期类型 datetime 12 同DateField的参数 13 7、models.Decimal 十进制小数类型 = decimal 14 必须指定整数位max_digits和小数位decimal_places 15 8、models.EmailField 字符串类型(正则表达式邮箱) =varchar 16 对字符串进行正则表达式 17 9、models.FloatField 浮点类型 = double 18 10、models.IntegerField 整形 19 11、models.BigIntegerField 长整形 20 integer_field_ranges = { 21 'SmallIntegerField': (-32768, 32767), 22 'IntegerField': (-2147483648, 2147483647), 23 'BigIntegerField': (-9223372036854775808, 9223372036854775807), 24 'PositiveSmallIntegerField': (0, 32767), 25 'PositiveIntegerField': (0, 2147483647), 26 } 27 12、models.IPAddressField 字符串类型(ip4正则表达式) 28 13、models.GenericIPAddressField 字符串类型(ip4和ip6是可选的) 29 参数protocol可以是:both、ipv4、ipv6 30 验证时,会根据设置报错 31 14、models.NullBooleanField 允许为空的布尔类型 32 15、models.PositiveIntegerFiel 正Integer 33 16、models.PositiveSmallIntegerField 正smallInteger 34 17、models.SlugField 减号、下划线、字母、数字 35 18、models.SmallIntegerField 数字 36 数据库中的字段有:tinyint、smallint、int、bigint 37 19、models.TextField 字符串=longtext 38 20、models.TimeField 时间 HH:MM[:ss[.uuuuuu]] 39 21、models.URLField 字符串,地址正则表达式 40 22、models.BinaryField 二进制 41 23、models.ImageField 图片 42 24、models.FilePathField 文件 43 44 更多字段
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 1、models.AutoField 自增列 = int(11) 2 如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。 3 2、models.CharField 字符串字段 4 必须 max_length 参数 5 3、models.BooleanField 布尔类型=tinyint(1) 6 不能为空,Blank=True 7 4、models.ComaSeparatedIntegerField 用逗号分割的数字=varchar 8 继承CharField,所以必须 max_lenght 参数 9 5、models.DateField 日期类型 date 10 对于参数,auto_now = True 则每次更新都会更新这个时间;auto_now_add 则只是第一次创建添加,之后的更新不再改变。 11 6、models.DateTimeField 日期类型 datetime 12 同DateField的参数 13 7、models.Decimal 十进制小数类型 = decimal 14 必须指定整数位max_digits和小数位decimal_places 15 8、models.EmailField 字符串类型(正则表达式邮箱) =varchar 16 对字符串进行正则表达式 17 9、models.FloatField 浮点类型 = double 18 10、models.IntegerField 整形 19 11、models.BigIntegerField 长整形 20 integer_field_ranges = { 21 'SmallIntegerField': (-32768, 32767), 22 'IntegerField': (-2147483648, 2147483647), 23 'BigIntegerField': (-9223372036854775808, 9223372036854775807), 24 'PositiveSmallIntegerField': (0, 32767), 25 'PositiveIntegerField': (0, 2147483647), 26 } 27 12、models.IPAddressField 字符串类型(ip4正则表达式) 28 13、models.GenericIPAddressField 字符串类型(ip4和ip6是可选的) 29 参数protocol可以是:both、ipv4、ipv6 30 验证时,会根据设置报错 31 14、models.NullBooleanField 允许为空的布尔类型 32 15、models.PositiveIntegerFiel 正Integer 33 16、models.PositiveSmallIntegerField 正smallInteger 34 17、models.SlugField 减号、下划线、字母、数字 35 18、models.SmallIntegerField 数字 36 数据库中的字段有:tinyint、smallint、int、bigint 37 19、models.TextField 字符串=longtext 38 20、models.TimeField 时间 HH:MM[:ss[.uuuuuu]] 39 21、models.URLField 字符串,地址正则表达式 40 22、models.BinaryField 二进制 41 23、models.ImageField 图片 42 24、models.FilePathField 文件
2、注册APP(在settings里面注册,如果没有注册不能忘记要不然不能创建数据表)
3、执行命令
python manage.py makemigrations
python manage.py migrate
文件内容:
# -*- coding: utf-8 -*- # Generated by Django 1.9.2 on 2016-03-11 12:46 from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='UserInfo', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('email', models.CharField(max_length=16)), ('pwd', models.CharField(max_length=32)), ], ), ]
然后:python manage.py migrate 会读取这个数据库结构生成数据库表!
上一遍文章里已经有创建过超级用户,我们可以通过配置admin来配置后台管理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#/usr/bin/env python #-*- coding:utf-8 -*- from django.contrib import admin # Register your models here. #导入app01模块 from app01 import models #注册咱们创建的类,通过他来访问 admin.site.register(models.UserInfo)
2、增删改查
我们修改下代码,让他登录成功之后跳转到index页面,然后让index页面返回所有的用户信息
url:
url(r'^index/', views.index),
函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def login(request): #如果是GET请求 #如果是POST,检查用户输入 #print request.method 来查看用户是通过什么方式请求的 #还有个问题:当你POST的时候,会出现问题,现在临时解决方法是:在seetings里注释掉 ''' 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', ] ''' if request.method == 'POST': input_email = request.POST['email'] input_pwd = request.POST['pwd'] if input_email == 'luotianshuai@qq.com' and input_pwd == '123': #当登录成功后给它跳转,这里需要一个模块from django.shortcuts import redirect #成功后跳转到指定网址 return redirect('/index/') else: #如果没有成功,需要在页面告诉用户用户名和密码错误. return render(request,'login.html',{'status':'用户名或密码错误'}) #通过模板语言,来在login.html中添加一个status的替换告诉用户<span>{{ status }}</span> return render(request,'login.html')
index
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request): #数据库去数据 #数据和HTML渲染 #如果想使用数据库,需要先导入(需要在开头导入) from app01 import models #获取UserInfo表中的数据,下面一行语句是固定搭配 user_info_list = models.UserInfo.objects.all() #user_info 列表,列表的元素就是一行.每一行有两个字段:一个是email 一个pwd return render(request,'index.html',{'user_info_list':user_info_list},)
然后在html中循环通过模板语言进行渲染
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Shuaige</title> </head> <body> <div> <table> <thead> <tr> <th>邮箱</th> <th>密码</th> </tr> </thead> <tbody> {% for line in user_info_list %} <tr> <td>{{ line.email }}</td> <td>{{ line.pwd }}</td> </tr> {% endfor %} </tbody> </table> </div> </body> </html>
2、添加数据
在index.html中再加一个表单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Shuaige</title> </head> <body> <div> <form action="/index/" method="post"> <input type="text" name="em" /> <input type="text" name="pw" /> <input type="submit" value="添加" /> </form> </div> <div> <table> <thead> <tr> <th>邮箱</th> <th>密码</th> </tr> </thead> <tbody> {% for line in user_info_list %} <tr> <td>{{ line.email }}</td> <td>{{ line.pwd }}</td> </tr> {% endfor %} </tbody> </table> </div> </body> </html>
函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request): #数据库去数据 #数据和HTML渲染 #如果想使用数据库,需要先导入(需要在开头导入) from app01 import models if request.method =='POST': #添加数据 input_em = request.POST['em'] input_pw = request.POST['pw'] #创建数据也得用model models.UserInfo.objects.create(email=input_em,pwd=input_pw) #他就表示去创建一条数据 #获取UserInfo表中的数据,下面一行语句是固定搭配 user_info_list = models.UserInfo.objects.all() #user_info 列表,列表的元素就是一行.每一行有两个字段:一个是email 一个pwd return render(request,'index.html',{'user_info_list':user_info_list},)
3、查、删除数据
要删除肯定的先找到他,通过filter去找到,然后后面加delete删除
models.UserInfo.objects.filter(email=input_em).delete() #找到email=input_em的数据并删除
函数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def index(request): #数据库去数据 #数据和HTML渲染 #如果想使用数据库,需要先导入(需要在开头导入) from app01 import models if request.method =='POST': #添加数据 input_em = request.POST['em'] input_pw = request.POST['pw'] #创建数据也得用model #models.UserInfo.objects.create(email=input_em,pwd=input_pw) #他就表示去创建一条数据 models.UserInfo.objects.filter(email=input_em).delete() #找到email=input_em的数据并删除 #获取UserInfo表中的数据,下面一行语句是固定搭配 user_info_list = models.UserInfo.objects.all() #user_info 列表,列表的元素就是一行.每一行有两个字段:一个是email 一个pwd return render(request,'index.html',{'user_info_list':user_info_list},)
Form
django中的Form一般有两种功能:
- 输入html
- 验证用户输入
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#!/usr/bin/env python # -*- coding:utf-8 -*- import re from django import forms from django.core.exceptions import ValidationError def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') if not mobile_re.match(value): raise ValidationError('手机号码格式错误') class PublishForm(forms.Form): user_type_choice = ( (0, u'普通用户'), (1, u'高级用户'), ) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice, attrs={'class': "form-control"})) title = forms.CharField(max_length=20, min_length=5, error_messages={'required': u'标题不能为空', 'min_length': u'标题最少为5个字符', 'max_length': u'标题最多为20个字符'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'标题5-20个字符'})) memo = forms.CharField(required=False, max_length=256, widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3})) phone = forms.CharField(validators=[mobile_validate, ], error_messages={'required': u'手机不能为空'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号码'})) email = forms.EmailField(required=False, error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'})) def __init__(self, *args, **kwargs): super(SampleImportForm, self).__init__(*args, **kwargs) self.fields['idc'].widget.choices = models.IDC.objects.all().order_by('id').values_list('id','display') self.fields['business_unit'].widget.choices = models.BusinessUnit.objects.all().order_by('id').values_list('id','name') Forms
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def publish(request): ret = {'status': False, 'data': '', 'error': '', 'summary': ''} if request.method == 'POST': request_form = PublishForm(request.POST) if request_form.is_valid(): request_dict = request_form.clean() print request_dict ret['status'] = True else: error_msg = request_form.errors.as_json() ret['error'] = json.loads(error_msg) return HttpResponse(json.dumps(ret)) Views
扩展:ModelForm
在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
class AdminModelForm(forms.ModelForm): class Meta: model = models.Admin #fields = '__all__' fields = ('username', 'email') widgets = { 'email' : forms.PasswordInput(attrs={'class':"alex"}), }