django入门基础
首先要说的是django与其他的框架不同,django是一个封装的及其完善的框架,我们使用django也不会像之前写学生系统那样,django操作数据库使用自带的ORM来进行操作,Torando与Flask的数据库使用就很自由(当然也可以使用我们之前的SQL helper),并且Flask的模板都不是自带的而是使用的第三方插件。
首先创建一个工程:
django-admin startproject mysite
创建一个应用:
python manage.py starapp app01
每一个新建的app下都有几个文件:
migrations文件夹,存放数据库的相关信息;
admin.py文件,Django自带后台管理相关配置;
modals.py,ORM的相关操作;
tests.py,单元测试;
views文件夹或者文件,业务处理。
路由系统
之前的学生系统传递nid都是通过url进行传递,这样导致链接的seo权重比较低,所以我们可以进行简单的优化。
动态路由:当一条url被路由url(r'^edit/(\w+)/', views.edit)匹配到,那么在视图函数中定义的函数就要传入额外的参数来盛放被正则匹配到的值。
#url url(r'^edit/(\w+)/(\w+)/', views.edit) #视图函数 def edit(request,a1,a2): print(a1) print(a2) return HttpResponse('...')
上面的例子是顺序传参,当然也可以指定传参,
#指定传参 url(r'^edit/(?P<a2>\w+)/(?P<a1>\w+)/', views.edit)
#视图函数
def edit(request,a2,a1):
print(a1)
print(a2)
return HttpResponse('...')
注:这两种传参方式不可以混着用,与函数传参不同的是此处的传参不可以用*args和**kwargs接收。
伪静态:
终止符: ^edit$ 伪静态: url(r'^edit/(\w+).html$', views.edit),
路由分发
为了防止不同业务线(app)的开发过程中出现的名称重复,所以才有了路由分发的必要性。
from django.conf.urls import url,include urlpatterns = [ url(r'^app01/', include('app01.urls')),]
这样路由进来就被分发到app01里面的urls进行进一步路由匹配。
别名与反向生成url
url(r'^edit/(\w+)/(\w+)/', views.edit,name='n2') #name就是别名 #在视图函数中: from django.urls import reverse v = reverse('n2',args=(1111,)) #url要是指定传参,reverse里就要用kwargs存放字典指定返回值。
反生成url可应用在html页面中,
#url(r'^login/', views.login,name='m1'), # def login(request): # return render(request,'login.html') #login.html <form method="POST" action="{% url "m1" %}"> <input />
这里的{% url "m1" %}会被渲染成能被^login/匹配的地址。
url(r'^edit/(\w+)/', views.edit,name='n2'),
{% url "n2" i %}与/edit/{{ i }}/一样,这里n2先被替换成正则前半部分,后面匹配的部分由i传参进去。
ORM框架
ORM一般用来做原生数据库语句转换,使我们开发更为高效。ORM不仅可以进行数据行的增删改查操作,还可以创建,修改,删除数据表(所以功能很强大,可以完成大部分的数据库操作)。
注:python没有办法创建数据库,只能是连接数据库。
ORM利用pymysql等第三方工具连接数据库(不能直接链接数据库,并且默认不是连接MySQL而是SQLlite)。而且默认链接MySQL的是MySQLDB模块。python3默认没装MySQLDB模块,所以最方便就是更改它默认连接MySQL的配置。
#默认 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } } #连接MySQL DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'dbname', 'USER': 'root', 'PASSWORD': 'xxx', 'HOST': '', 'PORT': '', } }
最后,我们还需要更改project下面的init,
import pymysql pymysql.install_as_MySQLdb()
这样我们就可以使用ORM操作pymysql连接MySQL数据库了。
models文件中创建数据表的各项属性:
from django.db import models class UserInfo(models.Model):#必须继承 """ 员工 """ nid = models.BigAutoField(primary_key=True)#自增可不写 user = models.CharField(max_length=32) password = models.CharField(max_length=64)#位数也很重要 age = models.IntegerField(default=1)
接着要在INSTALLED_APPS[]里加入我们注册的app名。
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'mysite', ]
最后一步,在终端输入
python manage.py makemigrations
python manage.py migrate
再去查看数据库就会增加很多表,其中app01_userinfo才是我们刚才创建的那张表。
这两行操作就是在app下migrations生成配置文件相关操作,migrate拿着配置信息去数据库进行相关操作。
以后我们要是修改表的名字就直接修改类,然后再次在终端执行这两条命令即可,当然比如要增加一行数据(例如UserInfo表里的最后一行age),如果之前有数据,之前的数据可没有age参数,那该怎么办呢,要么把这行数据设置为null允许为空,要么就设置默认值。
外键创建
class UserGroup(models.Model): """ 部门 """ title = models.CharField(max_length=32) class UserInfo(models.Model): """ 员工 """ nid = models.BigAutoField(primary_key=True) user = models.CharField(max_length=32) password = models.CharField(max_length=64) age = models.IntegerField(default=1) # ug_id ug = models.ForeignKey("UserGroup",null=True)
ug = models.ForeignKey("UserGroup",null=True)默认在数据库中多出来的数据是ug_id。
ORM的增删改查
增
models.UserGroup.objects.create(title='销售部')
查
group_list = models.UserGroup.objects.all()#所有 group_list = models.UserGroup.objects.filter(id=1)#过滤 group_list = models.UserGroup.objects.filter(id__gt=1)#神奇的双下划线
删
models.UserGroup.objects.filter(id=2).delete()
更新
models.UserGroup.objects.filter(id=2).update(title='公关部')
# 数据库相关操作 def index(request): # 增删改查 from app01 import models # 新增 # models.UserGroup.objects.create(title='销售部') # models.UserInfo.objects.create(user='root',password='pwd',age=18,ug_id=1) # 查找 # group_list = models.UserGroup.objects.all() # group_list = models.UserGroup.objects.filter(id=1) # group_list = models.UserGroup.objects.filter(id__gt=1) # group_list = models.UserGroup.objects.filter(id__lt=1) # 删除 # models.UserGroup.objects.filter(id=2).delete() # 更新 models.UserGroup.objects.filter(id=2).update(title='公关部') # group_list QuerySet类型(列表) # QuerySet类型[obj,obj,obj] # print(group_list) # for row in group_list: # print(row.id,row.title) # models.UserInfo.objects.all() group_list = models.UserGroup.objects.all() return render(request,'newindex.html',{"group_list": group_list})
这里我们查找的group_list是形似列表的QuerySet类型。QuerySet类型可以理解为[obj,obj,obj]。
for row in group_list: print(row.id,row.title)
同理在前端一样适用
{% for row in group_list %} <li>{{ row.id }} === {{ row.title }}</li> {% endfor %}
ORM之连表操作
我们之前的两张表,通过外键连接,其中ut直接连接UserType,ut代指了usertype的一行数据,我们可以直接使用ut.title直接跨表查询。
from django.db import models class UserType(models.Model): """ 用户类型 """ title = models.CharField(max_length=32) class UserInfo(models.Model):#必须继承 """ 员工 """ id = models.BigAutoField(primary_key=True)#自增可不写 name = models.CharField(max_length=32) age = models.IntegerField(default=1) # ug_id ut = models.ForeignKey("UserType",null=True)#外键操作 #创建用户数据 from app01 import models def test(request): # 创建数据 # models.UserType.objects.create(title='普通用户') # models.UserType.objects.create(title='二逼用户') # models.UserType.objects.create(title='牛逼用户') #不要重复添加 # models.UserInfo.objects.create(name='方',age=18,ut_id=1) # models.UserInfo.objects.create(name='由',age=18,ut_id=2) # models.UserInfo.objects.create(name='刘',age=18,ut_id=2) # models.UserInfo.objects.create(name='陈',age=18,ut_id=3) # models.UserInfo.objects.create(name='王',age=18,ut_id=3) # models.UserInfo.objects.create(name='杨',age=18,ut_id=1) return HttpResponse('...')
# 获取 # result取到的是QuerySet[obj,obj,obj] result = models.UserInfo.objects.all() for obj in result: print(obj.name,obj.age,obj.ut_id,obj.ut.title)#直接就连表取到值
# UserInfo,ut是FK字段 - 正向操作 PS: 一个用户只有一个用户类型 obj = models.UserInfo.objects.all().first()#取出第一个queryset数据 print(obj.name,obj.age,obj.ut.title)#因为obj只是一个数据只能对应一种用户
# UserType, 表名小写_set.all() - 反向操作 PS: 一个用户类型下可以有很多用户 obj = models.UserType.objects.all().first() print('用户类型',obj.id,obj.title) for row in obj.userinfo_set.all():#obj.userinfo_set里是userinfo的query对象,.all()取出所有数据 print(row.name,row.age)
#反向取出每一行的queryset result = models.UserType.objects.all() for item in result:#相当于取出每一行用户类型对应的用户的queryset for i in item.userinfo_set.all(): print(i.name) #过滤每一行 result = models.UserType.objects.all().filter(id= 1 )#<QuerySet [<UserType: UserType object>]> for item in result: print(item.userinfo_set.filter(id =1))#<QuerySet [<UserInfo: UserInfo object>]>
result = models.UserInfo.objects.all().values('id','name')#只取两列 # 并且每列的QuerySet[{'id':'xx','name':'xx'} ]都是这样的字典 for row in result: print(row) result2 = models.UserInfo.objects.all().first() print(result2)#UserInfo object
result = models.UserInfo.objects.all().values_list('id','name') # QuerySet[(1,'f'), ] for row in result: print(row)
当取出的queryset里面是obj对象的时候,可以直接在for循环里跨表,但是当取出的queryset里是字典或者元祖对象的时候,那么在for循环里就不可以跨表了,这时候就要使用神奇的双下划线 models.UserInfo.objects.filter(id__gt=1).values('id','name')#可以取到id>1的queryset对象。 # [{id:1,name:fd},{id:1,name:fd},{id:1,name:fd},] # models.UserInfo.objects.all().values('id','name') # models.UserInfo.objects.filter(id__gt=1).values('id','name') # 无法跨表 # result = models.UserInfo.objects.all().values('id','name') # for item in result: # print(item['id'],item['name']) # 夸表 __ # result = models.UserInfo.objects.all().values('id','name',"ut__title") # for item in result: # print(item['id'],item['name'],item['ut__title']) # [(1,df),(2,'df')] # models.UserInfo.objects.all().values_list('id','name') # models.UserInfo.objects.filter(id__gt=1).values_list('id','name') # 无法跨表 # result = models.UserInfo.objects.all().values_list('id','name') # for item in result: # print(item[0],item[1]) # 夸表 __ # result = models.UserInfo.objects.all().values_list('id','name',"ut__title") # for item in result: # print(item[0],item[1],item[2])
v = models.UserInfo.objects.values('id','name') # 6 v = models.UserInfo.objects.values('id','name','ut__title') # 6 # select * from userinfo left join usertype v1 = models.UserType.objects.values('id','title') print(v1) # 4 v2 = models.UserType.objects.values('id','title','userinfo__name') print(v2) #7 # select * from usertype left join userinfo #正向跨表与反向跨表与left join类似,谁在前面就是查他的所有,没有匹配到的那一行值为none填充 正: 1. q = UserInfo.objects.all().first() q.ug.title 2. UserInfo.objects.values('nid','ug_id') UserInfo.objects.values('nid','ug_id','ug__title') 3. UserInfo.objects.values_list('nid','ug_id','ug__title') 反: 1. 小写的表名_set obj = UserGroup.objects.all().first() result = obj.userinfo_set.all() [userinfo对象,userinfo对象,] 2. 小写的表名 v = UserGroup.objects.values('id','title') v = UserGroup.objects.values('id','title','小写的表名称') v = UserGroup.objects.values('id','title','小写的表名称__age') 3. 小写的表名 v = UserGroup.objects.values_list('id','title') v = UserGroup.objects.values_list('id','title','小写的表名称') v = UserGroup.objects.values_list('id','title','小写的表名称__age')
正向:
xxxx.filter(ut__title='超级用户').values('id','name','ut__title')
反向:
xxxx.filter(表名称__title='超级用户').values('id','name','表名称__title')
# 1.增删改查 # 2. 一般: # models.UserInfo.objects.filter(id__gt=1) # models.UserInfo.objects.filter(id__lt=1) # models.UserInfo.objects.filter(id__lte=1) # models.UserInfo.objects.filter(id__gte=1) # models.UserInfo.objects.filter(id__in=[1,2,3]) # models.UserInfo.objects.filter(id__range=[1,2]) # models.UserInfo.objects.filter(name__startswith='xxxx') # models.UserInfo.objects.filter(name__contains='xxxx') # models.UserInfo.objects.exclude(id=1) # 3. 排序 user_list = models.UserInfo.objects.all().order_by('-id','name') # 4. 分组 from django.db.models import Count,Sum,Max,Min # v =models.UserInfo.objects.values('ut_id').annotate(xxxx=Count('id')) # print(v.query) # v =models.UserInfo.objects.values('ut_id').annotate(xxxx=Count('id')).filter(xxxx__gt=2) # print(v.query)having # v =models.UserInfo.objects.filter(id__gt=2).values('ut_id').annotate(xxxx=Count('id')).filter(xxxx__gt=2) # print(v.query)where # 5. F,更新时用于获取原来的值 # from django.db.models import F,Q # models.UserInfo.objects.all().update(age=F("age")+1) # 6. Q,用于构造复杂查询条件 # 应用一: # models.UserInfo.objects.filter(Q(id__gt=1)) # models.UserInfo.objects.filter(Q(id=8) | Q(id=2)) # models.UserInfo.objects.filter(Q(id=8) & Q(id=2)) # 应用二: # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id__gt', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # # # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # # q3 = Q() # q3.connector = 'AND' # q3.children.append(('id', 1)) # q3.children.append(('id', 2)) # q2.add(q3,'OR') # # con = Q() # con.add(q1, 'AND') # con.add(q2, 'AND') # (id=1 or id = 10 or id=9 or (id=1 and id=2)) and (c1=1 or c1=10 or c1=9) # models.UserInfo.objects.filter(con) # condition_dict = { # 'k1':[1,2,3,4], # 'k2':[1,], # } # con = Q() # for k,v in condition_dict.items(): # q = Q() # q.connector = 'OR' # for i in v: # q.children.append(('id', i)) # con.add(q,'AND') # models.UserInfo.objects.filter(con) # 7. extra, 额外查询条件以及相关表,排序 models.UserInfo.objects.filter(id__gt=1) models.UserInfo.objects.all() # id name age ut_id models.UserInfo.objects.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None) # a. 映射 # select # select_params=None # select 此处 from 表 # b. 条件 # where=None # params=None, # select * from 表 where 此处 # c. 表 # tables # select * from 表,此处 # c. 排序 # order_by=None # select * from 表 order by 此处 models.UserInfo.objects.extra( select={'newid':'select count(1) from app01_usertype where id>%s'}, select_params=[1,], where = ['age>%s'], params=[18,], order_by=['-age'], tables=['app01_usertype'] ) """ select app01_userinfo.id, (select count(1) from app01_usertype where id>1) as newid from app01_userinfo,app01_usertype where app01_userinfo.age > 18 order by app01_userinfo.age desc """ result = models.UserInfo.objects.filter(id__gt=1).extra( where=['app01_userinfo.id < %s'], params=[100,], tables=['app01_usertype'], order_by=['-app01_userinfo.id'], select={'uid':1,'sw':"select count(1) from app01_userinfo"} ) print(result.query) # SELECT (1) AS "uid", (select count(1) from app01_userinfo) AS "sw", "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."age", "app01_userinfo"."ut_id" FROM "app01_userinfo" , "app01_usertype" WHERE ("app01_userinfo"."id" > 1 AND (app01_userinfo.id < 100)) ORDER BY ("app01_userinfo".id) DESC # 8. 原生SQL语句 from django.db import connection, connections cursor = connection.cursor() # connection=default数据 cursor = connections['db2'].cursor() cursor.execute("""SELECT * from auth_user where id = %s""", [1]) row = cursor.fetchone() row = cursor.fetchall() - extra - 原生SQL语句 - raw result = models.UserInfo.objects.raw('select * from userinfo') [obj(UserInfo),obj,] result = models.UserInfo.objects.raw('select id,1 as name,2 as age,4 as ut_id from usertype') [obj(UserInfo),obj,] v1 = models.UserInfo.objects.raw('SELECT id,title FROM app01_usertype',translations=name_map) # 9. 简单的操作 http://www.cnblogs.com/wupeiqi/articles/6216618.html
# """ # select # id, # name, # (select count(1) from tb) as n # from xb where .... # """ # # v = models.UserInfo.objects.all().extra( # select={ # 'n':"select count(1) from app01_usertype where id=%s or id=%s", # 'm':"select count(1) from app01_usertype where id=%s or id=%s", # }, # select_params=[1,2,3,4]) # for obj in v: # print(obj.name,obj.id,obj.n) # models.UserInfo.objects.extra( # where=["id=1","name='alex'"] # ) # models.UserInfo.objects.extra( # where=["id=1 or id=%s ","name=%s"], # params=[1,"alex"] # ) # models.UserInfo.objects.extra( # tables=['app01_usertype'], # ) # """select * from app01_userinfo,app01_usertype""" # result = models.UserInfo.objects.filter(id__gt=1) # print(result.query) # result = models.UserInfo.objects.filter(id__gt=1).extra( # where=['app01_userinfo.id < %s'], # params=[100,], # tables=['app01_usertype'], # order_by=['-app01_userinfo.id'], # select={'uid':1,'sw':"select count(1) from app01_userinfo"} # ) # print(result.query) # v = models.UserInfo.objects.all().order_by('-id','name') # v = models.UserInfo.objects.all().order_by('-id','name').reverse()#只有加order by才可以用reverse # v = models.UserInfo.objects.all().order_by('id','-name') # print(v) # v = models.UserInfo.objects.all() # [obj] # v = models.UserInfo.objects.all().only('id','name')#只拿id,only,还是obj,能取到age但是不要用only还去取,因为每次取都增加一次查询 # v = models.UserInfo.objects.all().defer('name')#除了name以外的数据 # # [obj] # for obj in v: # obj.id,obj.name # models.UserInfo.objects.values('id','name') # [{id,nam}] # models.UserInfo.objects.all().using('db2')#指定去哪个数据库取 # models.UserInfo.objects.all().filter().all().exclude().only().defer() # models.UserInfo.objects.none() # result = models.UserInfo.objects.aggregate(k=Count('ut_id', distinct=True), n=Count('id')) # print(result) # v = models.UserInfo.objects.all().first() # # models.UserInfo.objects.get(id=1) # # obj = models.UserType.objects.create(title='xxx') # obj = models.UserType.objects.create(**{'title': 'xxx'}) # print(obj.id) # # obj = models.UserType(title='xxx') # obj.save() # objs = [ # models.UserInfo(name='r11'), # ] # models.UserInfo.objects.bulk_create(objs, 10) # obj, created = models.UserInfo.objects.get_or_create( # username='root1', # pwd='ff', # defaults={'email': '1111111','u_id': 2, 't_id': 2}) # models.UserInfo.objects.filter(id__in=[1,2,3]) # models.UserInfo.objects.in_bulk([1,2,3]) # name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} # models.UserInfo.objects.raw('SELECT * FROM app01_usertype', translations=name_map) name_map = {'title': 'name'} v1 = models.UserInfo.objects.raw('SELECT id,title FROM app01_usertype',translations=name_map) for i in v1: print(i,type(i))
#每次跨表取值的时候都要进行一次数据库查询 # q = models.UserInfo.objects.all() # select * from userinfo # for row in q: # print(row.name,row.ut.title) # select_related: 查询主动做连表(for循环里面不会再查表了) # q = models.UserInfo.objects.all().select_related('ut','gp') # select * from userinfo inner join usertype on ... # for row in q: # print(row.name,row.ut.title) # prefetch_related: 不做连表,做多次查询(这里是两次单表查询,更快) # q = models.UserInfo.objects.all().prefetch_related('ut') # select * from userinfo; # Django内部:ut_id = [2,4] # select * from usertype where id in [2,4] # for row in q: # print(row.id,row.ut.title)
ORM操作多对多
#数据表操作 class Boy(models.Model): name = models.CharField(max_length=32) m = models.ManyToManyField('Girl',through="Love",through_fields=('b','g',)) class Girl(models.Model): nick = models.CharField(max_length=32) # m = models.ManyToManyField('Boy') class Love(models.Model): b = models.ForeignKey('Boy') g = models.ForeignKey('Girl') class Meta: unique_together = [ ('b','g'), ] #数据行添加 objs = [ models.Boy(name='方'), models.Boy(name='由'), models.Boy(name='陈'), models.Boy(name='闫'), models.Boy(name='吴'), ] models.Boy.objects.bulk_create(objs,5) objss = [ models.Girl(nick='小鱼'), models.Girl(nick='小周'), models.Girl(nick='小猫'), models.Girl(nick='小狗'), ] models.Girl.objects.bulk_create(objss,5) models.Love.objects.create(b_id=1,g_id=1) models.Love.objects.create(b_id=1,g_id=4) models.Love.objects.create(b_id=2,g_id=4) models.Love.objects.create(b_id=2,g_id=2)
# 和方有关系的姑娘 方法一: obj = models.Boy.objects.filter(name='方').first()#obj而不是queryset print(obj)#Boy object love_list = obj.love_set.all() print(love_list)#<QuerySet [<Love: Love object>, <Love: Love object>]> for row in love_list: print(row)#Love object print(row.g.nick)#在循环里多次跨表 方法二: love_list = models.Love.objects.filter(b__name='方') for row in love_list: print(row.g.nick)#与第一个方法相似,也是多次跨表 方法三: love_list = models.Love.objects.filter(b__name='方').values('g__nick') for item in love_list: print(item['g__nick'])#只循环前多查一次数据库 方法四: love_list = models.Love.objects.filter(b__name='方').select_related('g') for obj in love_list: print(obj.g.nick)#与三类似,只是obj不是字典组成queryset
在这里要的是,第三张Love表这里是可以写也可以不写的,
m = models.ManyToManyField('Girl',through="Love",through_fields=('b','g',))#这种写法就是指定了第三张表
这一行代码为我们创建了一张boy与girl的多对多表,但是这张表仅包含boy与girl的对应关系,如果想要增加其他的信息,那么第三张表就必须要自己写了。
manytomany创建的第三张表,因为他不存在类创建,所以我们对他进行增删改查都只能在创建者的obj中修改它。
obj = models.Boy.objects.filter(name='方').first() print(obj.id,obj.name) obj.m.add(3) obj.m.add(2,4) obj.m.add(*[1,]) obj.m.remove(1) obj.m.remove(2,3) obj.m.remove(*[4,]) obj.m.set([1,]) q = obj.m.all()#这里的q取到的其实是[Girl对象] # obj = models.Boy.objects.filter(name='方').first() # girl_list = obj.m.all() # girl_list = obj.m.filter(nick='小鱼') # print(girl_list) # obj = models.Boy.objects.filter(name='方').first() # obj.m.clear() # obj = models.Girl.objects.filter(nick='小鱼').first() # print(obj.id,obj.nick) # v = obj.boy_set.all()#反向找到与小鱼有关的boy的信息 # print(v) obj = models.Boy.objects.filter(name='方').first() v=obj.m.all()#v取到的就是<QuerySet [<Girl: Girl object>, <Girl: Girl object>]>
与MySQL操作一样,ORM也提供了很多的数据库数据类型供我们使用,但是有些字段类型,我们限制为邮箱类型,但实际上用create方法还是可以写成字符串写进去,但是在admin后台却有着明确的限制。具体字段在武sir的博客里有(6216618)。
color_list = ( (1,'黑色'), (2,'白色'), (3,'蓝色')) color = models.IntegerField(choices=color_list) 在django后台admin里会生成下拉框的选择,但是在MySQL中显示的依然是int类型的数字。
所以我们对字段的操作与admin里的操作并不相同,他们都有自己的一套字段。
class UserInfo(models.Model): nid = models.AutoField(primary_key=True) username = models.CharField(max_length=32) class Meta: # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 db_table = "table_name" # 联合索引 index_together = [ ("pub_date", "deadline"), ] # 联合唯一索引 unique_together = (("driver", "restaurant"),) # admin中显示的表名称 verbose_name # verbose_name加s verbose_name_plural
django之CBV
我们之前在工程的urls中写的路由对应在视图中是以函数的形式返回的HttpResponse对象,浏览器向服务器发送请求时包括了url等请求信息,服务器拿到信息后找到url和请求方式,再拿着url在后台进行匹配。除了这种方式以外还可以通过类进行return,就是CBV。
from app01 import views urlpatterns = [ url(r'^login.html$', views.Login.as_view()),#类名.as_view()是特殊写法 ]
class Login(View):#必须继承View类 """ ajax可以各种提交(默认是以下意思) get 查 post 创建 put 更新 delete 删除 """ def dispatch(self, request, *args, **kwargs):#我们自己也可以写dispatch,批量操作可以自己写 print('before') obj = super(Login,self).dispatch(request, *args, **kwargs) print('after') return obj def get(self,request):#request存放请求相关的所有数据,get与post等提交方式分离 # return HttpResponse('Login.get') return render(request,'login.html') def post(self,request): print(request.POST.get('user')) return HttpResponse('Login.post')
对于提交的各种方式类是怎么分辨出来的呢,反正肯定不是if判断,其实在类里面使用的是反射的方式实现的。
所以在函数执行get或post之前,其实是先执行了类里面的dispatch函数。
django分页
django本身自带分页功能(局限性使得不能广泛运用)
from django.core.paginator import Paginator,Page,PageNotAnInteger,EmptyPage def index(request): """ 分页 :param request: :return: """ # for i in range(300): # name = "root" + str(i) # models.UserInfo.objects.create(name=name,age=18,ut_id=1) current_page = request.GET.get('page') #所以页数要在url中传参 user_list = models.UserInfo.objects.all() paginator = Paginator(user_list,10) # per_page: 每页显示条目数量 # count: 数据总个数 # num_pages:总页数 # page_range:总页数的索引范围,如: (1,10),(1,200) # page: page对象 try: posts = paginator.page(current_page) except PageNotAnInteger as e: posts = paginator.page(1) except EmptyPage as e: posts = paginator.page(1) # has_next 是否有下一页 # next_page_number 下一页页码 # has_previous 是否有上一页 # previous_page_number 上一页页码 # object_list 分页之后的数据列表posts.object_list # number 当前页 # paginator paginator对象 return render(request,'index.html',{'posts':posts})
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> </head> <body> <ul> {% for item in posts %} <li>{{ item }}</li> {% endfor %} </ul> <div class="pagination"> <span class="step-links"> {% if posts.has_previous %} <a href="?p={{ posts.previous_page_number }}">Previous</a> {% endif %} <span class="current"> Page {{ posts.number }} of {{ posts.paginator.num_pages }}. </span> {% if posts.has_next %} <a href="?p={{ posts.next_page_number }}">Next</a> {% endif %} </span> </div> </body> </html>
def custom(request): current_page = request.GET.get('page') current_page = int(current_page) per_page = 10 start=(current_page-1)*per_page end=current_page * per_page user_list=models.UserInfo.objects.all()[start:end] return render(request,'custom.html',{'user_list':user_list})
class PageInfo(object): def __init__(self,current_page,all_count,per_page,base_url,show_page=11): """ :param current_page: :param all_count: 数据库总行数 :param per_page: 每页显示函数 :return: """ try: self.current_page = int(current_page)#非数字操作 except Exception as e: self.current_page = 1 self.per_page = per_page a,b = divmod(all_count,per_page)#all_count/per_page,b!=0则+1 if b: a = a +1 self.all_pager = a self.show_page = show_page self.base_url = base_url def start(self): return (self.current_page-1) * self.per_page def end(self): return self.current_page * self.per_page def pager(self): # v = "<a href='/custom.html?page=1'>1</a><a href='/custom.html?page=2'>2</a>" # return v page_list = [] half = int((self.show_page-1)/2) # 如果数据总页数 < 11 if self.all_pager < self.show_page: begin = 1 stop = self.all_pager + 1 # 如果数据总页数 > 11 else: # 如果当前页 <=5,永远显示1,11 if self.current_page <= half: begin = 1 stop = self.show_page + 1 else: if self.current_page + half > self.all_pager: begin = self.all_pager - self.show_page + 1 stop = self.all_pager + 1 else: begin = self.current_page - half stop = self.current_page + half + 1 if self.current_page <= 1: prev = "<li><a href='#'>上一页</a></li>" else: prev = "<li><a href='%s?page=%s'>上一页</a></li>" %(self.base_url,self.current_page-1,) page_list.append(prev) for i in range(begin,stop): if i == self.current_page: temp = "<li class='active'><a href='%s?page=%s'>%s</a></li>" %(self.base_url,i,i,) else: temp = "<li><a href='%s?page=%s'>%s</a></li>" %(self.base_url,i,i,) page_list.append(temp) if self.current_page >= self.all_pager: nex = "<li><a href='#'>下一页</a></li>" else: nex = "<li><a href='%s?page=%s'>下一页</a></li>" %(self.base_url,self.current_page+1,) page_list.append(nex) return ''.join(page_list)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css" /> </head> <body> <h1>用户列表</h1> <ul> {% for row in user_list %} <li>{{ row.name }}</li> {% endfor %} </ul> <nav aria-label="Page navigation"> <ul class="pagination"> {{ page_info.pager|safe }} </ul> </nav> </body> </html>
django模板
模板函数与自定义函数
{{ name|upper }}自带的函数使用这种方式就可以应用了。
自定义函数
首先要在应用里面新建一个templatetags文件夹,里面写一个xx.py文件用来创建自定义函数。
from django import template register = template.Library()#变量名register还不能改 @register.filter#可以放在条件语句后面,但最多能传两个值 def my_upper(value,arg): return value + arg @register.filter def my_bool(value): return False @register.simple_tag#不能放在条件语句中 def my_lower(value,a1,a2,a3): return value + a1 + a2 + a3
{% load xx %}//导入写的xx.py <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>111</h1> {% csrf_token %}//csrf值 {{ name|my_upper:"666" }} {% my_lower "ALEX" "x" "SB" "V" %} {% if name|my_bool %} <h3>真</h3> {% else %} <h3>假</h3> {% endif %} </body> </html>
最后所在app一定要注册才能完成。
在django中导入小组件使用include导入,
pub.html <div> <h3>特别漂亮的组件</h3> <div class="title">标题:{{ name }}</div> <div class="content">内容:{{ name }}</div> </div>
test.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> {% include 'pub.html' %} {% include 'pub.html' %} {% include 'pub.html' %} </body> </html>
Session
一句话简述Session,就是存储在服务端的数据,cookie则是存在客户端浏览器上的键值对。Session和cookie要做的事情都是一样的,都是为了解决http的短链接而生的,在账户提交post信息的时候,给客户端发一个cookie钥匙,那么客户端浏览器下一次来的时候带着这个键值对,服务器端就知道他是谁了。但是我觉得这样不好,因为信息都放在客户端,这样客户端要是篡改了信息(即使你做了加密也是有风险的),那么问题就很严重,而Session做的是同样的事,但是信息都是放在服务器端的,这样用户提交post信息给我,我就生成一把钥匙,同时给客户端浏览器一把,自己留一把,并且在数据库保存这把钥匙对应的数据资料,下一次有人来的时候进行验证,这就是Session的原理。
Session其实还是依赖于cookie的。
def login1(request): if request.method == 'GET': return render(request,'login.html') else: u = request.POST.get('user') p = request.POST.get('pwd') if u=='jeff'and p=='123': # 1. 生成随机字符串 # 2. 通过cookie发送给客户端 # 3. 服务端保存 # { # 随机字符串1: {'username':'jeff','email':x''...} # } request.session['username'] = 'jeff' request.session['email'] = 'jeff@123456' return redirect('/index/') else: return render(request,'login.html',{'msg':'用户名或密码错误'}) def index(request): # 1. 获取客户端端cookie中的随机字符串 # 2. 去session中查找有没有随机字符 # 3. 去session对应key的value中查看是否有 username v = request.session.get('username') if v: return HttpResponse('登录成功:%s' %v) else: return redirect('/login1/')
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用,分别是db,缓存,file,缓存+db,和加密cookie(这种意义上与cookie加密没什么实质上的区别)。
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
SESSION_ENGINE = 'django.contrib.sessions.backends.cache' # 引擎 SESSION_CACHE_ALIAS = 'default' # 使用的缓存别名(默认内存缓存,也可以是memcache),此处别名依赖缓存的设置
SESSION_ENGINE = 'django.contrib.sessions.backends.file' # 引擎 SESSION_FILE_PATH = None # 缓存文件路径,如果为None,则使用tempfile模块获取一个临时地址tempfile.gettempdir()
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db' # 引擎
ESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
def login(func): def wrap(request, *args, **kwargs): # 如果未登陆,跳转到指定页面 if request.path == '/test/': return redirect('http://www.baidu.com') return func(request, *args, **kwargs) return wrap
Django默认支持Session,并且默认是将Session数据存储在数据库中,即:django_session 表中。
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认) SESSION_COOKIE_NAME = "sessionid" # Session的cookie保存在浏览器上时的key,即:sessionid=随机字符串(默认) SESSION_COOKIE_PATH = "/" # Session的cookie保存的路径(默认) SESSION_COOKIE_DOMAIN = None # Session的cookie保存的域名(默认) SESSION_COOKIE_SECURE = False # 是否Https传输cookie(默认) SESSION_COOKIE_HTTPONLY = True # 是否Session的cookie只支持http传输(默认) SESSION_COOKIE_AGE = 1209600 # Session的cookie失效日期(2周)(默认) SESSION_EXPIRE_AT_BROWSER_CLOSE = False # 是否关闭浏览器使得Session过期(默认) SESSION_SAVE_EVERY_REQUEST = False # 是否每次请求都保存Session,默认修改之后才保存(默认)
def index(request): # 获取、设置、删除Session中数据 request.session['k1'] request.session.get('k1',None) request.session['k1'] = 123 request.session.setdefault('k1',123) # 存在则不设置 del request.session['k1'] # 所有 键、值、键值对 request.session.keys() request.session.values() request.session.items() request.session.iterkeys() request.session.itervalues() request.session.iteritems() # 用户session的随机字符串 request.session.session_key # 将所有Session失效日期小于当前日期的数据删除 request.session.clear_expired() # 检查 用户session的随机字符串 在数据库中是否 request.session.exists("session_key") # 删除当前用户的所有Session数据 request.session.delete("session_key") request.session.set_expiry(value) * 如果value是个整数,session会在些秒数后失效。 * 如果value是个datatime或timedelta,session就会在这个时间后失效。 * 如果value是0,用户关闭浏览器session就会失效。 * 如果value是None,session会依赖全局session失效策略。
Form表单
form可以帮我们完成很多表单提交的数据认证的问题,将重复操作简单化。其内部包含了正则表达式匹配。
from django.forms import Form from django.forms import fields class LoginForm(Form): # 正则验证: 不能为空,6-18 username = fields.CharField( max_length=18, min_length=6, required=True, error_messages={ 'required': '用户名不能为空', 'min_length': '太短了', 'max_length': '太长了', } ) # 正则验证: 不能为空,16+ password = fields.CharField(min_length=16,required=True) # email = fields.EmailField() # email = fields.GenericIPAddressField() # email = fields.IntegerField() def login(request): if request.method == "GET": return render(request,'login.html') else: obj = LoginForm(request.POST) if obj.is_valid(): # 用户输入格式正确 print(obj.cleaned_data) # 字典类型 return redirect('http://www.baidu.com') else: # 用户输入格式错误 return render(request,'login.html',{'obj':obj})
form表单里定义的class中的字段与前端传递的name一致,这是因为form内部验证is_valid分为两个步骤,第一步在实例化LoginForm时,获取类中所有的字段和与之对应的正则表达式,做成一个字典,
self.fields={ 'user': 正则表达式 'pwd': 正则表达式}
第二步,循环这个字典,
flag = True for k,v in self.fields.items(): # 1. user,正则表达式 input_value = request.POST.get(k)#这个时用户提交过来的信息 正则表达式和input_value进行匹配 匹配失败改变flag flag = False return flag
所以class定义的字段与前端表单信息一致。
django定义了很多字段类型,但是肯定是不够我们用的,所以时常我们需要自己定制字段。
class TestForm(Form): t1 = fields.CharField( required=True, max_length=8, min_length=2, error_messages={ 'required': '不能为空', 'max_length': '太长', 'min_length': '太短', } ) t2 = fields.IntegerField( min_value=10, max_value=1000, error_messages={ 'required': 't2不能为空', 'invalid': 't2格式错误,必须是数字',#格式错误都是 'min_value': '必须大于10', 'max_value': '必须小于1000', }, ) t3 = fields.EmailField( error_messages={ 'required': 't3不能为空', 'invalid': 't3格式错误,必须是邮箱格式', } )
自定制字段RegexField,继承CharField。
phone = fields.RegexField('139\d+')
form表单的插件(自动生成input标签)
Field required=True, 是否允许为空 widget=None, as_p中指定input生成的类型 label=None, 用于生成Label标签或显示内容 initial=None, as_p初始值 help_text='', 帮助信息(在标签旁边显示) error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) validators=[], 自定义验证规则 localize=False, 是否支持本地化 disabled=False, as_p是否可以编辑 label_suffix=None Label内容后缀as_p
as_p 给所有的fields生成input标签
class TestForm(Form): t1 = fields.CharField(required=True,max_length=8,min_length=2,label='用户名', help_text='111',initial='333', error_messages={ 'required': '不能为空', 'max_length': '太长', 'min_length': '太短', }, ) t2 = fields.EmailField() def test(request): if request.method == "GET": obj = TestForm() return render(request,'test.html',{'obj': obj}) else: obj = TestForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'test.html',{'obj':obj}) <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="/test/" method="POST" novalidate> {% csrf_token %} <p> {{ obj.t1.label }}{{ obj.as_p }}{{ obj.t1.help_text}}{{ obj.errors.t1.0 }} </p> <p> {{ obj.t2 }}{{ obj.errors.t2.0 }} </p> <input type="submit" value="提交" /> </form> </body> </html>
class RegiterForm(Form): user = fields.CharField(min_length=8) email = fields.EmailField() password = fields.CharField() phone = fields.RegexField('139\d+') def register(request): if request.method == 'GET': obj = RegiterForm()#此时生成的html没有数据 return render(request,'register.html',{'obj':obj}) else: obj = RegiterForm(request.POST)#此时生成html里面有post的值 if obj.is_valid(): print(obj.cleaned_data) else: print(obj.errors) return render(request,'register.html',{'obj':obj}) <form action="/register/" method="POST" novalidate> {% csrf_token %} <p> {{ obj.user }} {{ obj.errors.user.0 }} </p> <p> {{ obj.email }} {{ obj.errors.email.0 }} </p> <p> {{ obj.password }} {{ obj.errors.password.0 }} </p> <p> {{ obj.phone }} {{ obj.errors.phone.0 }} </p> <input type="submit" value="提交" /> </form>
Ajax表单提交
def login(request): return render(request,'login.html') def ajax_login(request): import json ret = {'status': True,'msg': None} obj = LoginForm(request.POST) if obj.is_valid(): print(obj.cleaned_data) else: # print(obj.errors) # obj.errors对象 ret['status'] = False ret['msg'] = obj.errors v = json.dumps(ret) return HttpResponse(v)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>用户登录</h1> <form id="f1" action="/login/" method="POST"> {% csrf_token %} <p> <input type="text" name="user" />{{ obj.errors.user.0 }} </p> <p> <input type="password" name="pwd" />{{ obj.errors.pwd.0 }} </p> <a onclick="submitForm();">提交</a> </form> <script src="/static/jquery-1.12.4.js"></script> <script> function submitForm(){ $('.c1').remove();//每次都清空之前提示 $.ajax({ url: '/ajax_login/', type: 'POST', data: $('#f1').serialize(),// user=alex&pwd=456&csrftoen=dfdf\把整个表单都扔到后台 dataType:"JSON", success:function(arg){ console.log(arg); if(arg.status){ }else{ $.each(arg.msg,function(index,value){//index就是user或pwd console.log(index,value); var tag = document.createElement('span'); tag.innerHTML = value[0]; tag.className = 'c1'; $('#f1').find('input[name="'+ index +'"]').after(tag); }) } } }) } </script> </body> </html>
Ajax提交时不会自动刷新页面,所以可以保留上次提交的信息,所以这种方式是一个不错的选择。
form表单使用(学生数据表操作)
obj.cleaned_data取出来的值是字典,所以表单提交成功可以直接写到数据库中:
models.Classes.objects.create(**obj.cleaned_data)
ClassForm进行实例化的时候里面传的参数应当为字典类型(request.POST实质上也是字典),所以为了让编辑时能在页面取到编辑的班级,所以第一步需要传进点击的班级的id号,在数据库中取出他的课程名:
方法一:手动取obj中参数 row = models.Classes.objects.filter(id=nid).first() obj = ClassForm(initial={'title': row.title})#只能传入字典,首次不校验 obj = ClassForm(initial=row) 方法二:直接生成字典传进去 row = models.Classes.objects.filter(id=nid).values('title').first() obj = ClassForm(initial=row)
添加学生列表需要将课程展示为select下拉框,要用插件widgets完成。
cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) widget=widgets.Select(choices=models.Classes.objects.values_list('id','title')))
choice传入的值是列表中套元组的形式,显示title,传递的value是id的值。
form表单select框
xx=fields.CharField(widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'multiple':'multiple'}))
此处的select虽然给了multiple的参数,但是接收时相当与get而不是getlist,所以这种方式只适合传单个参数。
如何满足可以在页面上显示下拉框还可以传递多个参数呢。
cls_id = fields.IntegerField( # widget=widgets.Select(choices=[(1,'上海'),(2,'北京')]) widget=widgets.Select(choices=models.Classes.objects.values_list('id','title'),attrs={'class': 'form-control'}) ) cls_id = fields.ChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.Select(attrs={'class': 'form-control'}) )
xx = fields.MultipleChoiceField( choices=models.Classes.objects.values_list('id','title'), widget=widgets.SelectMultiple ) obj = FooForm({'cls_id':[1,2,3]})
# class TestForm(Form): # t1 = fields.CharField( # widget=widgets.Textarea(attrs={}) # ) # # # t2 = fields.CharField( # widget=widgets.CheckboxInput # ) # # t3 = fields.MultipleChoiceField( # choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')], # widget=widgets.CheckboxSelectMultiple # ) # # t4 = fields.ChoiceField( # choices=[(1,'篮球'),(2,'足球'),(3,'溜溜球')], # widget=widgets.RadioSelect # ) # t5 = fields.FileField( # widget=widgets.FileInput # )
is_valid进行验证时发生了哪些事?
跟着源码来一探究竟:
def is_valid(self): return self.is_bound and not self.errors
这里的self是我们实例化的form对象,self.is_bound的true与false决定了数据是否验证(data与initials)。
def full_clean(self): self._errors = ErrorDict() if not self.is_bound: return self.cleaned_data = {} if self.empty_permitted and not self.has_changed(): return self._clean_fields()#hasattr(self, 'clean_%s' % name) #执行循环第一个字段先匹配正则,成功执行clean_字段函数(扩展高级功能) self._clean_form()#扩展功能 self._post_clean()#还是扩展功能
from django.core.exceptions import ValidationError class TestForm(Form): user = fields.CharField(validators=[]) pwd = fields.CharField() def clean_user(self): v = self.cleaned_data['user'] if models.Student.objects.filter(name=v).count():#找一下数据库是否已经有这个值了 raise ValidationError('用户名已经存在') return self.cleaned_data['user'] def clean_pwd(self): return self.cleaned_data['pwd'] def clean(self): # user = self.cleaned_data.get('user') # email = self.cleaned_data.get('email') # if models.Student.objects.filter(user=user,email=email).count(): # raise ValidationError('用户名和邮箱联合已经存在') return self.cleaned_data # def _post_clean(self):#还是用于扩展 # """ # An internal hook for performing additional cleaning after form cleaning # is complete. Used for model validation in model forms. # """ # pass def test(request): obj = TestForm(initial={'t3':[2,3]}) obj.is_valid() return render(request,'test.html',{'obj':obj})
class MyForm(Form): user = fields.CharField( validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], )
class testform(Form): tes=fields.FileField( widget=widgets.FileInput ) def test(request): if request.method=='GET': obj=testform() return render(request,'test.html',{'obj':obj}) else: import os obj = testform(request.POST) print(request.FILES.get('tes'))#文件对象 print(request.FILES.get('tes').name)#文件对象 print(request.FILES.get('tes').size)#文件对象 f=open(os.path.join('static',request.FILES.get('tes').name),'wb') all=request.FILES.get('tes').chunks()#拿到整个文件 for trunk in all: f.write(trunk) f.close() return render(request, 'test.html',{'obj':obj})
def test(request): if request.method=='GET': obj=testform() return render(request,'test.html',{'obj':obj}) else: import os obj = testform(data=request.POST,files=request.FILES) if obj.is_valid(): print(obj.cleaned_data.get('tes').name) print(obj.cleaned_data.get('tes').size) f=open(os.path.join('static',obj.cleaned_data.get('tes').name),'wb') all=obj.cleaned_data.get('tes').chunks()#拿到整个文件 for trunk in all: f.write(trunk) f.close() return render(request, 'test.html',{'obj':obj})
上传文件需要前端form中一定要有一个attr:
<form method="POST" enctype="multipart/form-data">
没有enctype="multipart/form-data",后台无法接收到前端传回来的文件对象。
生成随机验证码
Python生成随机验证码,需要使用PIL模块。
pip3 install pillow
#创建图片
from PIL import Image
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
# 在图片查看器中打开
# img.show()
# 保存在本地
with open('code.png','wb') as f:
img.save(f,format='png')
#创建画笔,用于在图片上画任意内容
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
#画点
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示坐标
# 第二个参数:表示颜色
draw.point([100, 100], fill="red")
draw.point([300, 300], fill=(255, 255, 255))
#画线
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标和结束坐标
# 第二个参数:表示颜色
draw.line((100,100,100,300), fill='red')
draw.line((100,100,300,100), fill=(255, 255, 255))
#画圆
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标和结束坐标(圆要画在其中间)
# 第二个参数:表示开始角度
# 第三个参数:表示结束角度
# 第四个参数:表示颜色
draw.arc((100,100,300,300),0,90,fill="red")
#写文本
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
draw.text([0,0],'python',"red")
#特殊字体文字
img = Image.new(mode='RGB', size=(120, 30), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示字体文件路径
# 第二个参数:表示字体大小
font = ImageFont.truetype("kumo.ttf", 28)
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
# 第四个参数:表示颜色
draw.text([0, 0], 'python', "red", font=font)
import random
def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB')
def rndChar():
"""
生成随机字母
:return:
"""
return chr(random.randint(65, 90))
def rndColor():
"""
生成随机颜色
:return:
"""
return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255))
# 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
# 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
# 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor())
# 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor())
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
return img,''.join(code)
if __name__ == '__main__':
# 1. 直接打开
# img,code = check_code()
# img.show()
# 2. 写入文件
# img,code = check_code()
# with open('code.png','wb') as f:
# img.save(f,format='png')
# 3. 写入内存(Python3)
# from io import BytesIO
# stream = BytesIO()
# img.save(stream, 'png')
# stream.getvalue()
# 4. 写入内存(Python2)
# import StringIO
# stream = StringIO.StringIO()
# img.save(stream, 'png')
# stream.getvalue()
pass