django
2018-11-25 17:50:30 3 举报Referer请求头
在Django社区上会经常看到django.newforms这个词语。当人们讨论django.newforms,其实就是我们本章里面介绍的django.forms。
改名其实有历史原因的。 当Django一次向公众发行时,它有一个复杂难懂的表单系统:django.forms。后来它被完全重写了,新的版本改叫作:django.newforms,这样人们还可以通过名称,使用旧版本。 当Django 1.0发布时,旧版本django.forms就不再使用了,而django.newforms也终于可以名正言顺的叫做:django.forms。
from
django
import
forms
class
ContactForm(forms.Form):
subject
=
forms.CharField()
email
=
forms.EmailField(required
=
False
)
message
=
forms.CharField()
>>>
print
f.as_ul()
<li><label
for
=
"id_subject"
>Subject:<
/
label> <
input
type
=
"text"
name
=
"subject"
id
=
"id_subject"
/
><
/
li>
<li><label
for
=
"id_email"
>Email:<
/
label> <
input
type
=
"text"
name
=
"email"
id
=
"id_email"
/
><
/
li>
<li><label
for
=
"id_message"
>Message:<
/
label> <
input
type
=
"text"
name
=
"message"
id
=
"id_message"
/
><
/
li>
>>>
print
f.as_p()
<p><label
for
=
"id_subject"
>Subject:<
/
label> <
input
type
=
"text"
name
=
"subject"
id
=
"id_subject"
/
><
/
p>
<p><label
for
=
"id_email"
>Email:<
/
label> <
input
type
=
"text"
name
=
"email"
id
=
"id_email"
/
><
/
p>
<p><label
for
=
"id_message"
>Message:<
/
label> <
input
type
=
"text"
name
=
"message"
id
=
"id_message"
/
><
/
p>
>>>
print
f[
'subject'
]
<
input
type
=
"text"
name
=
"subject"
id
=
"id_subject"
/
>
>>>
print
f[
'message'
]
<
input
type
=
"text"
name
=
"message"
id
=
"id_message"
/
>
f
=
ContactForm({
'subject'
:
'Hello'
,
'email'
:
'adrian@example.com'
,
'message'
:
'Nice site!'
}
>>> f
=
ContactForm({
'subject'
:
'Hello'
,
'message'
:
'Nice site!'
})
>>> f.is_valid()
True
>>> f
=
ContactForm({
'subject'
:
'Hello'
})
>>> f.is_valid()
False
>>> f
=
ContactForm({
'subject'
:
'Hello'
,
'message'
: ''})
>>> f.is_valid()
False
>>> f
=
ContactForm({
'subject'
:
'Hello'
,
'message'
: ''})
>>> f[
'message'
].errors
[u
'This field is required.'
]
>>> f[
'subject'
].errors
[]
>>> f[
'email'
].errors
[]
>>> f
=
ContactForm({
'subject'
:
'Hello'
,
'message'
: ''})
>>> f.errors
{
'message'
: [u
'This field is required.'
]}
>>> f
=
ContactForm({subject': Hello, email: adrian@example.com, message: Nice site!})
>>> f.is_valid()
True
>>> f.cleaned_data
{message': uNice site!, email: uadrian@example.com, subject: uHello}
name = models.CharField(max_length=64)
name = forms.CharField(validators=[validate_name],label='姓名',error_messages={'required':'必填'})
email=forms.EmailField(error_messages={'required':'默认报错方式','invalid':"邮箱格式错误"})
email=forms.EmailField(error_messages={'required':'默认报错','invalid':"邮箱格式错误"},
widget=forms.EmailInput(
attrs={
"class": "form-control email", "placeholder": "email","id":"exampleInputemal1"
}))
def clean(self):
if self.cleaned_data.get("password") is not None:
if self.cleaned_data.get("password")==self.cleaned_data.get("reqeat_password"):
return self.cleaned_data
else:
raise ValidationError("抛出两次密码不一致错误")
else:
return self.cleaned_data
def clean_password(self):
if len(self.cleaned_data.get("password"))>8:
return self.cleaned_data.get("password")
else:
raise ValidationError("密码长度不到8位")
if obj.is_valid():
obj.save() # 创建数据
修改表数据是,记得把instance
信息也传进去,不然是新建数据,而不是对某行数据进行修改。
编辑用户信息,新url方式保留默认数据
is_valid
是在BaseForm中定义的,所以ModelForm也能和Form一样使用各种钩子 class Meta:
model=StudentStudyRecord
# 需要编辑校验的字段,不编辑的不协商__all__所有字段
fields=["score","homework_note"]
class RecordScoreView2(View):
def get(self, request,class_study_record_id):
model_formset_cls = modelformset_factory(model=StudentStudyRecord, form=StudentStudyRecordModelForm2, extra=0) #extra=0设置额外增添的编辑表单个数
queryset = StudentStudyRecord.objects.filter(classstudyrecord=class_study_record_id)
# 把当前班级对象下面的所有学生学习记录对象传给ModelFormSet
# formset每一条学生记录对象做成一个表单对象
formset = model_formset_cls(queryset=queryset)
return render(request,"study_record/record_score_formset.html",locals())
def post(self, request,class_study_record_id):
model_formset_cls = modelformset_factory(model=StudentStudyRecord, form=StudentStudyRecordModelForm2, extra=0)
queryset = StudentStudyRecord.objects.filter(classstudyrecord=class_study_record_id)
print("request.POST",request.POST)
formset=model_formset_cls(request.POST)
if formset.is_valid():
formset.save()
print(formset.errors)
return redirect(reverse("class_study_record"))
<html lang="en">
<head>
<meta charset="UTF-8">
<title>录入成绩</title>
<link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
</head>
<body>
<div class="panel panel-default">
<div class="panel-heading">学习记录</div>
<div class="panel-body">
<div style="width: 680px;margin: 0 auto;">
<form method="post" action="">
{% csrf_token %}
<!--用formset必须加这句话-->
{{ formset.management_form }}
<table class="table table-bordered">
<thead>
<tr>
<th>姓名</th>
<th>考勤</th>
<th>作业成绩</th>
<th>作业评语</th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr>
{{ form.id }}
提交的时候 modelformset需要id
<td>{{ form.instance.student }}</td>
<!--instance 不编辑的字段,设置为原值不渲染-->
<td>{{ form.instance.get_record_display }} </td>
<td>{{ form.score }} </td>
<td>{{ form.homework_note }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<input type="submit" class="btn btn-success pull-right baocun" value="保存">
</form>
</div>
</div>
</div>
<hr>
<script src="/static/AdminLTE-2.3.3/plugins/jQuery/jQuery-2.2.0.min.js"></script>
<script>
$(function () {
$(".baocun").click(function () {
alert("保存成功!")
})
})
</script>
</body>
</html>
姓名<input type="text" id="user">
文件<input type="file" name="file_obj" id="file">
<input type="button" class="filebtn" value="提交">
<p class="msg"></p>
</div>
var formdata=new FormData();
formdata.append("file_obj",$("#file")[0].files[0]);
formdata.append("user",$("#user").val());
$.ajax({
url:"/file_put/",
type:"post",
// Ajax上传文件必备参数
processData: false , // 不处理数据
contentType: false, // 不设置内容类型
data:formdata,
success:function (response) {
console.log(response);
if (response=="OK"){
$(".msg").html("提交成功!")
}
}
})
})
def get_valid_img(request):
# 生成随机背景颜色
def get_random_color():
return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
# 生成随机图片
img = Image.new("RGB", (200, 40), get_random_color())
draw = ImageDraw.Draw(img)
font = ImageFont.truetype("static/fonts/yzmfont/kumo.ttf", 32)
keep_str = "" # 存放已经生成的验证码
for i in range(4):
random_num = str(random.randint(0, 9))
random_lowalf = chr(random.randint(65, 90))
random_upperalf = chr(random.randint(97, 122))
random_char = random.choice([random_num, random_lowalf, random_upperalf])
draw.text((30*i+50,0),random_char,get_random_color(),font=font)
keep_str += random_char
# 加造点 加划线
# width = 350
# height = 38
# # 画线
# for i in range(10):
# x1 = random.randint(0, width)
# x2 = random.randint(0, width)
# y1 = random.randint(0, height)
# y2 = random.randint(0, height)
# draw.line((x1, y1, x2, y2), fill=get_random_color())
#
# 加点
# for i in range(80):
# draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color())
# x = random.randint(0, width)
# y = random.randint(0, height)
# draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color())
# 把图片放到内存中
f = BytesIO()
img.save(f,"png")
data = f.getvalue()
# 把验证码保存到session 中 ,方便与输入的校验
request.session["keep_str"] = keep_str
print(keep_str)
return HttpResponse(data)
通俗的讲就是把context的内容, 加载进templates中定义的文件, 并通过浏览器渲染呈现.
def
index(request):
models.HostRelation.objects.create(
user
=
models.UserMap.objects.get(
id
=
1
),
host
=
models.HostInfo.objects.get(
id
=
1
)
)
return
HttpResponse(
'OK'
)
class
UserType(models.Model):
caption
=
models.CharField(max_length
=
32
)
class
UserInfo(models.Model):
user_type
=
models.ForeignKey(
'UserType'
)
#这个user_type是一个对象,对象里面封装了ID和caption
username
=
models.CharField(max_length
=
32
)
age
=
models.IntegerField()
def
index(request):
ret
=
models.UserInfo.objects.
all
()
#咱们看下他执行的什么SQL语句
print
( ret.query)
'''
SELECT "app01_userinfo"."id", "app01_userinfo"."user_type_id", "app01_userinfo"."username", "app01_userinfo"."age" FROM "app01_userinfo"
'''
def
user_info(request):
ret
=
models.UserInfo.objects.
all
().select_related()
#咱们看下他执行的什么SQL语句
print
ret.query
SELECT
"app01_userinfo"
.
"id"
,
"app01_userinfo"
.
"user_type_id"
,
"app01_userinfo"
.
"username"
,
"app01_userinfo"
.
"age"
,
"app01_usertype"
.
"id"
,
"app01_usertype"
.
"caption"
FROM
"app01_userinfo"
INNER JOIN
"app01_usertype"
ON (
"app01_userinfo"
.
"user_type_id"
=
"app01_usertype"
.
"id"
)
第一步:
#生成一个搜索对象
search_q
=
Q()
#在生成两个搜索对象
search1
=
Q()
search2
=
Q()
第二步:
#标记search1中的搜索条件为 ‘ 或’ 查询
search1.connector
=
'OR'
#把搜索条件加入到search1中
search1.children.append((
'字段名'
,
'字段内容'
))
search1.children.append((
'字段名'
,
'字段内容'
))
search1.children.append((
'字段名'
,
'字段内容'
))
search1.children.append((
'字段名'
,
'字段内容'
))
#标记search2中的搜索条件为 ‘ 或’ 查询
search2.connector
=
'OR'
#把搜索条件加入到search2中
search2.children.append((
'字段名'
,
'字段内容'
))
search2.children.append((
'字段名'
,
'字段内容'
))
search2.children.append((
'字段名'
,
'字段内容'
))
search2.children.append((
'字段名'
,
'字段内容'
))
第三步:
#把多个搜索条件进行合并
search_q.add(search1,
'AND'
)
search_q.add(search2,
'AND'
)
第四步:
#执行搜索
models.HostInfo.objects.
filter
(search_q)
from django.db.models import Avg,Min,Sum,Max 从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有 图书的集合。 >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的 标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定 一个名称,可以向聚合子句提供它: >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 如果你也想知道所有图书价格的最大值和最小值,可以这样查询: >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # # models.Tb1.objects.filter(name__contains="ven") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # # startswith,istartswith, endswith, iendswith,
# ret3=models.Book.objects.filter(title='Python').values('id') # print(ret3)#[{'id': 1}] #正向查找(条件)之一对多 ret4=models.Book.objects.filter(title='Python').values('publisher__city') print(ret4) #[{'publisher__city': '北京'}] #正向查找(条件)之多对多 ret5=models.Book.objects.filter(title='Python').values('author__name') print(ret5) ret6=models.Book.objects.filter(author__name="alex").values('title') print(ret6) #注意 #正向查找的publisher__city或者author__name中的publisher,author是book表中绑定的字段 #一对多和多对多在这里用法没区别
#反向查找之一对多: ret8=models.Publisher.objects.filter(book__title='Python').values('name') print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] #反向查找之多对多: ret10=models.Author.objects.filter(book__title='Python').values('name') print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}] #注意 #正向查找的book__title中的book是表名Book #一对多和多对多在这里用法没区别
<1> 第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据)。
<2>在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。
#---------------- update方法直接设定对应属性---------------- models.Book.objects.filter(id=3).update(title="PHP") ##sql: ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)
#--------------- save方法会将所有属性重新设定一遍,效率低----------- obj=models.Book.objects.filter(id=3)[0] obj.title="Python" obj.save() # SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", # "app01_book"."color", "app01_book"."page_num", # "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; # # UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556, # "publisher_id" = 1 WHERE "app01_book"."id" = 3;
>>> Book.objects.filter(id=1).delete() (3, {'app01.Book_authors': 2, 'app01.Book': 1})
Django 的 Model 驱动对数据库层面上的实现细节关注的非常少,开发者定义模型的过程非常接近声明式而非过程式,对于新项目来说,可能是这个原因让 Django Model 比 SQLAlchemy 讨人喜欢。
传统的 SQLAlchemy 的使用方法是不入侵模型,在单独的地方定义表结构、映射规则,然后用 SQLAlchemy 驱动注入到模型类里去,这种方法可以完全避免模型与数据库的耦合,但是定义繁琐,要求开发者完全明白 engine、metadata、table、column、mapper 等概念,如果没有读过《企业应用架构模式》一类书籍会被弄得很乱。
现在 SQLAlchemy 提供了 declarative 的方式,和 Django Model 很像,但是和声明式模型还是有一定的距离,好在对于灵活性几乎没损失。但是我对比了一下 Django Model 和 SQLAlchemy declarative,发现 Django Model 还是更简洁一些。例如对于类关联,Django 只要直接声明外键,就自动产生关联对象,而 SQLAlcyhemy 要定义外键、relationship 对象,如果有多个外键还要自己指定 join 规则…… 总之灵活性是好东西,但是不是什么情况下都讨人喜欢的。
我本来想说这个是 ActiveRecord style 和 Data Mapper style 区别导致的,但是细想了一下,Django Model 并不是简单的 ActiveRecord,其对于复杂关联甚至继承的映射都有很好的适应性,应该和 SQLAlchemy 的 declarative 是同类型的,是对 Data Mapper 的 Active Record style 包装。sqlalchemy使用上有两个层次,1是使用sql expression, 说白可以让你用python写sql, 2是它的orm, orm是使用session的,自行管理session生存期,自行在多个过程中传递session,自行管理事务。写法上是通常的transaction script(java常说的贫血的domain model)模式。实际编码通常1和2混合编程。
django通过中间件部分隐藏了连接/事务管理的概念,写法上也比较简单,接近java常说的充血的domain model. 内容上也没有sqlalchemy 的sql expression层次。 易用性就体现出来了。
因为在Django世界中它的ORM就是事实标准。Django最重要的特色是什么?从ORM快速生成后台管理界面!另外还有ModelForm、数据迁移(migration)等等从Django ORM延伸出去的概念……如果你选用了SQLAlchemy,那么这一切都没有了,你必须自行搭建,或者选用第三方库。话说,没有了内置ORM、没有了内置后台管理界面、没有了内置ModelForm、没有了数据迁移的Django,还是Django吗?不如直接用其它更轻量或更松散的框架好了!
另外,在Django之外单独使用Django ORM是不靠谱的。如果是其它环境,使用SQLAlchemy就好了。name = models.CharField( max_length=32)
courses=models.ManyToManyField("Courses",through="Score")
class Course(models.Model):
name = models.CharField( max_length=32)
class Score(models.Model):
student=models.ForeignKey("Student")
course=models.ForeignKey("Course")
score=models.IntegerField()
through 建表的时候添加的
from
django.core
import
serializers
ret
=
models.BookType.objects.
all
()
data
=
serializers.serialize(
"json"
, ret)
import
json
#ret = models.BookType.objects.all().values('caption')
ret
=
models.BookType.objects.
all
().values_list(
'caption'
)
ret
=
list
(ret)
result
=
json.dumps(ret)
import
json
from
datetime
import
date
from
datetime
import
datetime
class
JsonCustomEncoder(json.JSONEncoder):
def
default(
self
, field):
if
isinstance
(field, datetime):
return
o.strftime(
'%Y-%m-%d %H:%M:%S'
)
elif
isinstance
(field, date):
return
o.strftime(
'%Y-%m-%d'
)
else
:
return
json.JSONEncoder.default(
self
, field)
Model signals
pre_init
# django的modal执行其构造方法前,自动触发
post_init
# django的modal执行其构造方法后,自动触发
pre_save
# django的modal对象保存前,自动触发
post_save
# django的modal对象保存后,自动触发
pre_delete
# django的modal对象删除前,自动触发
post_delete
# django的modal对象删除后,自动触发
m2m_changed
# django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared
# 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
pre_migrate
# 执行migrate命令前,自动触发
post_migrate
# 执行migrate命令后,自动触发
Request
/
response signals
request_started
# 请求到来前,自动触发
request_finished
# 请求结束后,自动触发
got_request_exception
# 请求异常后,自动触发
Test signals
setting_changed
# 使用test测试修改配置文件时,自动触发
template_rendered
# 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created
# 创建数据库连接时,自动触发
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
import django.dispatch pizza_done = django.dispatch.Signal(providing_args = [ "toppings" , "size" ])
|
def
callback(sender,
*
*
kwargs):
print
(
"callback"
)
print
(sender,kwargs)
pizza_done.connect(callback)
from
路径
import
pizza_done
pizza_done.send(sender
=
'seven'
,toppings
=
123
, size
=
456
)