django
2017-10-27 14:16:04 194 举报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 formsclass 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一样使用各种钩子 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.querySELECT "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就好了。from django.core import serializersret = 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.dispatchpizza_done = django.dispatch.Signal(providing_args=["toppings", "size"]) |
def callback(sender, **kwargs): print("callback") print(sender,kwargs)pizza_done.connect(callback)from 路径 import pizza_donepizza_done.send(sender='seven',toppings=123, size=456)