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',
]
INSTALLED_APPS
最后一步,在终端输入
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})
Views.py
这里我们查找的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') #
v = models.UserInfo.objects.values('id','name','ut__title') #
# select * from userinfo left join usertype v1 = models.UserType.objects.values('id','title')
print(v1) #
v2 = models.UserType.objects.values('id','title','userinfo__name')
print(v2) #
# 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
Django ORM操作
# """
# 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))
ORM测试
#每次跨表取值的时候都要进行一次数据库查询
# 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类型的数字。
django枚举
所以我们对字段的操作与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})
django自带分页
<!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>
页面.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})
自定义分页1.0
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)
自定义分页模块pager.py
<!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>
page.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
xx.py
{% 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:"" }}
{% my_lower "ALEX" "x" "SB" "V" %}
{% if name|my_bool %}
<h3>真</h3>
{% else %}
<h3>假</h3>
{% endif %}
</body>
</html>
test.html
最后所在app一定要注册才能完成。
在django中导入小组件使用include导入,
pub.html
<div>
<h3>特别漂亮的组件</h3>
<div class="title">标题:{{ name }}</div>
<div class="content">内容:{{ name }}</div>
</div>
组件的html
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=='':
# 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/')
session操作
Django中默认支持Session,其内部提供了5种类型的Session供开发者使用,分别是db,缓存,file,缓存+db,和加密cookie(这种意义上与cookie加密没什么实质上的区别)。
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # 引擎(默认)
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' # 引擎
缓存+数据库Session
ESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' # 引擎
加密cookie Session
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
认证session的登陆装饰器
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,默认修改之后才保存(默认)
setting.py
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失效策略。
操作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表单的使用
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='',initial='',
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>
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)
Ajax后端
<!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
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'})
)
select单选框
xx = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id','title'),
widget=widgets.SelectMultiple
) obj = FooForm({'cls_id':[1,2,3]})
select多选框
# 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
# )
生成各类input标签
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()#还是扩展功能
forms.py
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表单上传文件
上传文件需要前端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)
PIL的基本操作
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
图片验证码
django入门基础的更多相关文章
- 【Python全栈-后端开发】Django入门基础-2
Django入门基础知识-2 一 .模版 一.模版的组成 HTML代码+逻辑控制代码 二.逻辑控制代码的组成 1 变量(使用双大括号来引用变量) {{var_name}} 2 标签(tag)的使用 ...
- 【Python全栈-后端开发】Django入门基础
Django基础知识 一. 什么是web框架? 框架,即framework,特指为解决一个开放性问题而设计的具有一定约束性的支撑结构,使用框架可以帮你快速开发特定的系统,简单地说,就是你用别人搭建好的 ...
- Django入门------基础(1)
基础 Django 是用 Python 写的一个自由和开放源码 web 应用程序框架.web框架是一套组件,能帮助你更快.更容易地开发web站点.当你开始构建一个web站点时,你总需要一些相似的组件: ...
- Django入门基础详解
本次使用django版本2.1.2 安装django 安装最新版本 pip install django 安装指定版本 pip install django==1.10.1 查看本机django版本 ...
- python学习笔记--Django入门四 管理站点
上一节 Django入门三 Django 与数据库的交互:数据建模 "管理员界面"是基础功能中的重要部分. django.contrib 包 Django自动管理工具是djang ...
- Django入门与实践 17-26章总结
Django入门与实践-第17章:保护视图 Django 有一个内置的视图装饰器 来避免它被未登录的用户访问: 现在如果用户没有登录,将被重定向到登录页面: 现在尝试登录,登录成功后,应用程序会跳转到 ...
- python web框架Django入门
Django 简介 背景及介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以 ...
- mybatis入门基础(二)----原始dao的开发和mapper代理开发
承接上一篇 mybatis入门基础(一) 看过上一篇的朋友,肯定可以看出,里面的MybatisService中存在大量的重复代码,看起来不是很清楚,但第一次那样写,是为了解mybatis的执行步骤,先 ...
- 01shell入门基础
01shell入门基础 为什么学习和使用shell编程 shell是一种脚本语言,脚本语言是相对于编译语言而言的.脚本语言不需要编译,由解释器读取程序并且执行其中的语句,而编译语言需要编译成可执行代码 ...
随机推荐
- SQL企业级面试题
链接:90root MySQL企业面试题 1. 开发有一堆数据插入,如何防止插入的中文数据产生乱码? 2. 如何批量更改数据库表的引擎,如:myisam改为innodb 3. 如何批量更改数据库字符集 ...
- alias 命令详解
alias 命令 作用: 设置命令别名,可以将较长的命令进行简化,使用alias 时,用户必须使用单引号将原来的命令引起来,防止特殊字符导致错误. 如要永久生效则将alias 命令存放到bash 的 ...
- 02.将SDK获取到的ECS主机信息入库
1.通过调用阿里SDK,将获取到的ECS信息存入数据库,如果不知道SDK怎么使用,查看:01.阿里云SDK调用,获取ESC主机详细信息 2.import aliSDK应用的是01.阿里云SDK调用,获 ...
- solr 的 field, copyfield ,dynamic field
Field: Field就是一个字段,定义一个Field很简单: <field name="price" type="sfloat" indexed=&q ...
- 例子:韩顺平JavaScript----JS乌龟抓小鸡游戏
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 人工智能一:Al学习路线
想要跨入AI的大门,如何跨?终于找到了一套学习方法 努力向你靠近 2017-12-03 07:14:51 当下人工智能领域的发展已经有了燎原之势,麦肯锡全球研究院就认为人工智能促进对社会的转变速度将比 ...
- SQL 多列合并一列
select rtrim(姓)+ rtrim(名) as 姓名 from tb
- NoFragment重大bug
在activity中切换fragment,有以下几点问题需要注意: 例如做一个类似于这样的tab切换fragment的,有以下几点问题 1.切换fragment后,前几个fragment能透视,解决方 ...
- swiper轮播问题之二:默认显示3张图片,中间显示全部两边显示部分
其二:项目遇到比较有点要求的轮播图,默认显示3张图片,中间显示全部,两边显示部分.如图: 网上找了也没有找到合适的,最后经过自己摸索写了出来,贴出代码分享给大家. CSS .swipe ...
- timeline自适应时间轴
近期项目一直有类似QQ空间那样的时间轴,来展示公司新闻动态,或者流程之类的设计UI. 每每出现,不以为然,这次总结了下,精简下 ================= ================== ...