python 学习笔记十七 django深入学习二 form,models
表单
GET 和 POST
处理表单时候只会用到GET 和 POST 方法。
Django 的登录表单使用POST 方法,在这个方法中浏览器组合表单数据、对它们进行编码以用于传输、将它们发送到服务器然后接收它的响应。
相反,GET 组合提交的数据为一个字符串,然后使用它来生成一个URL。这个URL 将包含数据发送的地址以及数据的键和值。如果你在Django 文档中做一次搜索,你会立即看到这点,此时将生成一个https://docs.djangoproject.com/search/?q=forms&release=1 形式的URL。
GET 和POST 用于不同的目的。
用于改变系统状态的请求 —— 例如,给数据库带来变化的请求 —— 应该使用POST。GET 只应该用于不会影响系统状态的请求。
GET 还不适合密码表单,因为密码将出现在URL 中,以及浏览器的历史和服务器的日志中,而且都是以普通的文本格式。它还不适合数据量大的表单和二进制数据,例如一张图片。使用GET 请求作为管理站点的表单具有安全隐患:攻击者很容易模拟表单请求来取得系统的敏感数据。POST,如果与其它的保护措施结合将对访问提供更多的控制,例如Django 的CSRF 保护。
另一个方面,GET 适合网页搜索这样的表单,因为这种表示一个GET 请求的URL 可以很容易地作为书签、分享和重新提交。
Django form
处理表单是一件很复杂的事情。考虑一下Django 的Admin 站点,不同类型的大量数据项需要在一个表单中准备好、渲染成HTML、使用一个方便的界面编辑、返回给服务器、验证并清除,然后保存或者向后继续处理。
Django 的表单功能可以简化并自动化大部分这些工作,而且还可以比大部分程序员自己所编写的代码更安全。
Django form的作用:
- 为数据创建HTML 表单
- 用来做用户提交的验证
可以手工编写代码来实现,但是Django 可以帮你完成所有这些工作。
假设我们定义一个注册用户的提交表单,我们想要获取提交的数据就需要一条条get,假设注册项过多,我们需要获取和验证数据有效性就会写很多判断和获取语句。。。。
from django.shortcuts import render # Create your views here. def user_list(request): username = request.POST.get('username') password = request.POST.get('password')
mail = request.POST.get('mail')
mobile = request.POST.get('mobile') #验证: #输入不能为空,并且有的可以为空有的不可以为空
#如果email = 111111 这样合法吗?
#如果mobile = 1111 这样合法吗?
#如果sex = 111111 这样合法吗?
... ''' 你在这里是不是需要做一大堆的输入验证啊?并且有很多这种页面会存在这种情况,如果每个函数都这样做估计就累死了 ''' return render(request,'user_list.html')
使用django构建表单:
views
def name_validate(value):
#自定义验证方法(正则匹配)
name_re = re.compile(r'^[a-zA-Z]{1}\w+$')
if not name_re.match(value):
raise ValidationError('姓名格式错误')
def sex_vaildate(value):
li = [1,2]
if value not in li:
raise ValidationError('请选择性别')
def mobile_validate(value): mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') #正则匹配 if not mobile_re.match(str(value)): raise ValidationError('手机号码格式错误') # 如果没有匹配到主动触发一个错误
class Author(forms.Form):
#添加select标签
GENDER_CHOICE =
(('0', '-----'),
('1', '男'),
('2', '女'),
)
#创建一个表单类时,最重要的部分是定义表单的字段。每个字段都可以有自定义的验证逻辑,以及一些其它的钩子。
#更多表单字段见:http://python.usyiyi.cn/django/ref/forms/fields.html
name = forms.CharField(max_length=32,
error_messages={'required': u'姓名不能为空', # required 默认不能为空 设置False可以为空 'max_length': u'不能超过32位字符'}, validators=[name_validate,], # validators 自定义验证方法 #widget 负责渲染网页上HTML 表单的输入元素和提取提交的原始数据。但是,Widget 需要赋值给表单字段。 widget=forms.widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'用户名'})) # attr为标签设置样式 # widgets默认为TextInput 更多请参考:http://python.usyiyi.cn/django/ref/forms/widgets.html
mobiles = forms.IntegerField(error_messages={'required': u'手机号不能为空'}, validators=[mobile_validate, ],
widget=forms.widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'手机号'})) email = forms.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=forms.widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'})) gender = forms.IntegerField(error_messages={'required': u'性别不能为空'}, widget=forms.widgets.Select(choices=GENDER_CHOICE,), # 设置Select属性 定义choices字段 接收 GENDER_CHOICE 定义的value值 validators=[sex_vaildate,])
def addAuthor(request):
obj = Author() #实例化类
if request.method == "POST": #接收用户的post请求
user_input_obj = Author(request.POST)
#request.POST包含咱们提交过来的数据,把它当做参数,实例化Author类,它会自动提交接收到的数据;实例化后的obj对象将会包含Author类中定义的字段.
form的实例具有一个is_valid()方法,它为所有的字段运行验证的程序。当调用这个方法时,如果所有的字段都包含合法的数据,它将返回True; 将表单的数据放到cleaned_data 属性中。
if user_input_obj.is_valid():
data = user_input_obj.clean() #obj.clean() 获取通过验证的数据
new_author = models.Author(**data) #添加数据
new_author.save() #保存
else:
error_msg = user_input_obj.errors #obj.errors 可以拿到用户提交数据出现的异常
return render(request, "app01/addAuthor.html", {"obj":user_input_obj,"error":error_msg }) 返回obj对象 和 错误信息
return render(request, "app01/addAuthor.html",{"obj": obj}) #返回obj
如果访问视图的是一个GET 请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一次访问该URL 时预期发生的情况。
如果表单的提交使用POST 请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:user_input_obj = Author(request.POST)。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。
我们调用表单的is_valid()方法;如果它不为True,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。
如果is_valid() 为True,我们将能够在cleaned_data 属性或 clean() 中找到所有合法的表单数据。
在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。
templates
{% block tables %} <div class="container"> <div class="mains"> <h1>Add Author</h1> <form action="/index/author/add/" method="POST"> <div class="form-group"> <label for="id_name">name</label> {{ obj.name }} <span>{{ error.name }}</span> </div> <div class="form-group"> <label for="id_mobiles">mobiles</label> {{ obj.mobiles }} <span>{{ error.mobiles }}</span> </div> <div class="form-group"> <label for="id_email">email</label> {{ obj.email }}<span>{{ error.email }}</span> </div> <div class="form-group"> <label for="id_gender">gender</label> {{ obj.gender }}<span>{{ error.gender }}</span> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div> </div> {% endblock %}
models
class Author(models.Model):
GENDER_CHOICE = ( #定义下拉框数据
('0', '-----'),
('1', '男'),
('2', '女'),
)
name = models.CharField(max_length=32) #字符串类型
email = models.EmailField(null=True) #字符串类型带正则匹配
mobiles = models.IntegerField() #数字类型
gender = models.CharField(max_length=2,choices=GENDER_CHOICE,default=0)
#choice用于接收select标签数据
#自定义显示字段 py2.7
def __unicode__(self):
return self.name
#自定义显示字段py3.5
def __str__(self):
return self.name
用户提交的数据通过data = obj.clean(),我们打印data就可以拿到数据不用一条条get了
{'name': 'aaaaaaaaa', 'gender': 1, 'mobiles': 11111111111, 'email': '111@qq.com'}
错误信息:
<ul class="errorlist"><li>name<ul class="errorlist"><li>姓名格式错误</li></ul></li><li>mobiles<ul class="errorlist"><li>Enter a whole number.</li></ul></li>
<li>email<ul class="errorlist"><li>Enter a valid email address.</li>< /ul></li></ul>
django form提供的便利:
- 不用在前端写js验证;
- 可以自定义生成html标签;
- 不用再定义repost.POST.get["xxx"]获取数据
- .....
关于验证的问题:
前端js的验证并不是必须的,因为浏览器可以关闭页面的js功能访问,所以后端一定要有验证功能。
form 动态更新select标签数据
回顾之间实现的select标签
class Author(forms.Form):
GENDER_CHOICE =
(('0', '-----'),
('1', '男'),
('2', '女'),
)
gender = forms.IntegerField(error_messages={'required': u'性别不能为空'}, widget=forms.widgets.Select(choices=GENDER_CHOICE,))
咱们定义的select标签是静态的无法动态更新,当然本例只有这两个选项;例如我们添加出版社的时候,是可以动态增加的,我们先将数据写入一个文件的方法来简单实现。
publisher文件 通过json.dumps生成的文本
[[1, "aa出版社"], [2, "bb出版社"]]
class Publisher(forms.Form):
f = open('publisher')
data = f.read()
data_ret = json.loads(data)
f.close()
yw = forms.IntegerField(
widget=forms.Select(choices=data_ret)
)
现在是动态生成了,我现在在yw_manager里在增加一个字段
[[1, "aa出版社"], [2, "bb出版社"],[3, "cc出版社"]]
当我们刷新的时候,新增加的数据并没有出现。why?
在面向对象中,类变量在类创建之后就保存在内存中了,当你实例化Publisher类的时候,类变量不会改变。
在上例中,我们没有定义Publisher类的初始化方法,默认使用父类的方法,所以我们可以在实例化类的时候,增加一个修改字段的操作即可。这样每次就可以加载新的内容。
class Publisher(forms.Form): host = forms.IntegerField( widget=forms.Select() ) def __init__(self, *args, **kwargs): super(ImportForm, self).__init__(*args, **kwargs) f = open('publisher') data = f.read() data_ret = json.loads(data) f.close() #通过self.fields找到host这类变量的.wideget.choices属性并重新给他赋值! self.fields['host'].widget.choices = data_tuple
以上是通过写入文件来动态生成select标签,大部分情况我们直接从数据库取数据来动态生成。
form
class Book(forms.Form): title = forms.CharField(max_length=64, error_messages={'required': u'姓名不能为空', 'max_length': u'不能超过64位字符'}, widget=forms.widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'书名'})) authors = forms.CharField(widget=forms.widgets.SelectMultiple(attrs={'class': "form-control"})) publisher = forms.IntegerField(widget=forms.widgets.Select(attrs={'class': "form-control"})) publication_date = forms.DateField(widget=forms.widgets.DateInput( attrs={'class': "form-control", 'placeholder': u'时间'})) def __init__(self, *args, **kwargs): super(Book, self).__init__(*args, **kwargs)
# 重写init 每次从数据库取出数据给指定字段赋值 self.fields["authors"].widget.choices = models.Author.objects.all().order_by('id').values_list('id', "name") self.fields["publisher"].widget.choices = models.Publisher.objects.all().order_by('id').values_list("id", "name")
views
def addBook(request): obj = Book() if request.method == 'POST': obj = Book(request.POST) if obj.is_valid(): data = obj.cleaned_data #两种方式
#第一种方式先获取对象,通过对象的方式添加!
pub_obj = models.UserGroup.objects.get(id=data['publisher'])
print(pub_obj)
newbook = models.Book(title=data['title'],
publisher=pub_obj,
publication_date=data['publication_date'])
# 第二种方式直接添加_id值 newbook = models.Book(title=data['title'], publisher_id=data['publisher'], publication_date=data['publication_date']) newbook.save() # data["authors"]:"[list_data]" 需要将字符串类型转换成列表 newbook.authors.add(*eval(data["authors"])) print(obj.errors) return render(request, 'app01/addBook.html', {'obj': obj, 'error': obj.errors}) return render(request, 'app01/addBook.html', {'obj': obj})
html
{% extends "app01/index.html" %} {% block tables %} <div class="container"> <div class="mains"> <h1>Add Book</h1> <form action="/index/book/add/" method="POST"> <div class="form-group"> <label for="id_title">title</label> {{ obj.title }} </div> <div class="form-group"> <label for="id_author">authors</label> {{ obj.authors }} {{ error.authors }} </div> <div class="form-group"> <label for="id_publisher">publisher</label> {{ obj.publisher }} {{ error.publisher }} </div> <div class="form-group"> <label for="id_publication_date">publication_date</label> {{ obj.publication_date }} </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div> </div> {% endblock %}
另一种比较low的解决办法,手动生成select标签,不通过django form生成。
views
class book_form(forms.Form): title = forms.CharField(max_length=64) #publisher_id = forms.IntegerField(widget=forms.Select()) publication_date = forms.DateField() def book(request): book_obj = book_form() if request.method == "POST": book_obj = book_form(request.POST) if book_obj.is_valid(): data = book_obj.cleaned_data data["publisher_id"] = request.POST.get("publisher_id") #手动获取并添加进字典 newbook = models.Book(**data) newbook.save() else: print(book_obj.errors) publisher_list = models.Publisher.objects.all() #将查询到的数据返回给前端显示 return render(request, 'app01/book.html', {'book_form': book_obj, 'publishers': publisher_list})
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="" method="post"> {% csrf_token %} {{ book_form }} <select name="publisher_id"> {% for publisher in publishers %} <option value="{{ publisher.id }}">{{ publisher.name }}</option> {% endfor %} </select> <input type="submit" value="创建图书" /> </form> </body> </html>
扩展ModelForm
在使用Model和Form时,都需要对字段进行定义并指定类型,通过ModelForm则可以省去From中字段的定义。
class AdminModelForm(forms.ModelForm):
class Meta:
model = models.Admin
#fields = '__all__' #选择所有默认
fields = ('username', 'email') #具体选择其中几个
widgets = {
'email' : forms.PasswordInput(attrs={'class':"alex"}),
}
继续来解决上面的问题。。。
class book_modelform(forms.ModelForm):
class Meta:
model = models.Book #继承Book
exclude = () #排除空,即选择所有
widgets = { #为input标签添加样式
"title": forms.TextInput(attrs={})
}
def book_model(request):
book_obj = book_modelform() #实例化类
if request.method == "POST": #POST请求
book_obj = book_modelform(request.POST) #获取数据
if book_obj.is_valid(): #通过验证
book_obj.save() #直接保存即可!!!
print(book_obj.errors)
return render(request, 'app01/book_form.html', {'book_form': book_obj})
html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <form action="" method="post">{% csrf_token %} {% for i in book_form %} <div>{{ i.name }} {{ i }} {{ i.errors }}</div> {% endfor %} <input type="submit" value="创建图书"> </form> </body> </html>
充分利用ModelForm
利用modelform展示和修改数据
def customer_detail(request, customer_id): customer_obj = models.Customer.objects.get(id=customer_id) if request.method == "POST": # 将后台取出的数据显示并修改,需要将obj当作参数instance=obj 传入ModelForm,如果instance不写,下面这条语句就会认为 创建一条新数据 form = forms.CustomerModelForm(request.POST, instance=customer_obj) # print(request.POST) if form.is_valid(): form.save() print('url:', request.path) base_url = "/".join(request.path.split("/")[:-2]) print('url:', base_url) return redirect(base_url) else: form = forms.CustomerModelForm(instance=customer_obj) return render(request, 'crm/customer_detail.html', {'customer_form': form})
给字段添加样式
from django.forms import Form,ModelForm from crm import models class CustomerModelForm(ModelForm): class Meta: model = models.Customer exclude = () # 使用这种方式,样式有时候显示不出来,需要重新刷新几次 # def __init__(self,*args,**kwargs): # super(CustomerModelForm, self).__init__(*args, **kwargs) # # 单条数据添加样式 # # self.fields['qq'].widget.attrs["class"] = "form-control" # # 批量添加样式 # for field_name in self.base_fields: # field = self.base_fields[field_name] # field.widget.attrs.update({'class': 'form-control'}) def __new__(cls, *args, **kwargs): for field_name in cls.base_fields: field = cls.base_fields[field_name] attr_dic = { "class": "form-control", "placeholder": field.help_text, } field.widget.attrs.update(attr_dic) return ModelForm.__new__(cls)
html
{% extends 'base.html' %} {% load custom_tags %} {% block page-header %} 客户详细信息 {% endblock %} {% block page-content %} <form class="form-horizontal" method="post" >{% csrf_token %} {% for field in customer_form %} <div class="form-group"> {% if field.field.required %} {# 判断字段是否必填 #} <label class="col-sm-2 control-label text-left">*{{ field.label }}</label> {% else %} <label style="font-weight: normal" class="col-sm-2 control-label text-left">{{ field.label }}</label> {% endif %} <div class="col-sm-9"> {{ field }} {% if field.errors %} {# 有多个错误一起显示出来 #} <ul> {% for error in field.errors %} <li style="color:red">{{ error }}</li> {% endfor %} </ul> {% endif %} </div> </div> {% endfor %} <div class="col-sm-11"> <input class="btn btn-success pull-right" type="submit" value="Save"> </div> </form> {% endblock %}
Django Model
模型是你的数据的唯一的、权威的信息源。它包含你所储存数据的必要字段和行为。通常,每个模型对应数据库中唯一的一张表。
基础:
- 每个模型都是django.db.models.Model 的一个Python 子类。
- 模型的每个属性都表示为数据库中的一个字段。
- Django 提供一套自动生成的用于数据库访问的API;详见执行查询。
关联的对象
Django 提供了三种最常见的数据库关系:
- 多对一(many-to-one)
- 多对多(many-to-many)
- 一对一(one-to-one)
一般创建外键关联的时候,就有一对多,或者多对多 一对一仅在Django中出现。
应用场景:
- 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择)
例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
- 多对多:在某表中创建一行数据是,有一个可以多选的下拉框
例如:创建用户信息,需要为用户指定多个爱好
- 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了
例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据
class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): # __unicode__ on Python 2 return self.name class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() def __str__(self): # __unicode__ on Python 2 return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): # __unicode__ on Python 2 return self.headline
处理带外键关联(多对一关系)
前向关联查询
entry = models.Entry.objects.get(pk=1) tech_blog = models.Blog.objects.get(id=2) print(entry,entry.blog,tech_blog) entry.blog = tech_blog entry.save()
特殊情况:如果ForeignKey 字段有null=True 设置(即它允许NULL 值),你可以分配None 来删除对应的关联性。
>>> e = Entry.objects.get(id=2)
>>> e.blog = None
>>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
注意select_related() 查询集方法递归地预填充所有的一对多关系到缓存中,不用再查询数据库了。例如:
>>> e = Entry.objects.select_related().get(id=2)
>>> print(e.blog) # Doesn't hit the database; uses cached version.
>>> print(e.blog) # Doesn't hit the database; uses cached version.
反向关联查询
#查看当前文章哪些属于生活版块
blog = models.Entry.objects.filter(blog__name= "生活")
#查看当前版块有哪些文章
entries = models.Blog.objects.filter(entry__headline__icontains="Python")
# 如果模型有一个ForeignKey,那么该ForeignKey 所指的模型实例可以通过一个管理器返回前一个模型的所有实例。
# 默认情况下,这个管理器的名字为foo_set,其中foo 是源模型的小写名称。
from app01 import models as book_models
pub_obj = book_models.Publisher.objects.last()
print(pub_obj.name,pub_obj.book_set.select_related())
# 反向关联查询 通过出版社查书 使用管理器需要通过get(), first(), last()方式查询,使用filter(),all()没有此方法
blog_obj = models.Blog.objects.get(name="python")
print('========', blog_obj.entry_set.all())
blog_obj = models.Blog.objects.last()
print(blog_obj.entry_set.select_related())
提示:
- 1、搜索条件使用 __ 连接
- 2、获取值时使用 . 连接
多对多关联
多对多关系的两端都会自动获得访问另一端的API。这些API 的工作方式与上面提到的“方向”一对多关系一样。
唯一的区别在于属性的名称:定义 ManyToManyField 的模型使用该字段的属性名称,而“反向”模型使用源模型的小写名称加上'_set'(和一对多关系一样)。
#m2m关联from blog.models import Author joe = Author.objects.create(name="Joe") entry.authors.add(joe) #添加数据 e = models.Entry.objects.get(id=3) print('e',e) print(e.authors.all()) # Returns all Author objects for this Entry. print(e.authors.count()) #返回数量 print(e.authors.filter(name__contains='John')) #过滤包含关键字的条目 a = models.Author.objects.get(id=5) print(a) print(a.entry_set.all()) #反向查询 如果Entry 中的ManyToManyField 指定related_name='entries',那么Author 实例将使用 entries 属性而不是entry_set。
print(a.entry_set.select_related())
一对一关系
一对一关联关系。概念上讲,这个字段很像是ForeignKey 设置了unique=True,不同的是它会直接返回关系另一边的单个对象。
它最主要的用途是作为扩展自另外一个模型的主键;例如,多表继承就是通过对子模型添加一个隐式的一对一关联关系到父模型实现的。
需要一个位置参数:与该模型关联的类。 它的工作方式与ForeignKey 完全一致,包括所有与递归关系和惰性关系相关的选项。
如果你没有指定OneToOneField 的related_name 参数,Django 将使用当前模型的小写的名称作为默认值。
更多请见:http://python.usyiyi.cn/django/ref/models/fields.html#django.db.models.OneToOneField
单表查询操作
all_entries = models.Entry.objects.all() #查询所有条目
year1 = models.Entry.objects.filter(pub_date__year=2016)#查询所有pub_date为2016年的纪录
year2 = models.Entry.objects.all().filter(pub_date__year=2016) #效果同上
ret = models.Entry.objects.filter(headline__startswith="python").exclude(pub_date__gte=datetime.date.today()) #链式查询
one_entry = models.Entry.objects.get(pk=1) #当条查询
limit_entry = models.Entry.objects.all()[:5] #查询中的limit [1,2,3,4,5]
offset_entry = models.Entry.objects.all()[1:3] #offset 算尾不算头 [2,3]
order_entry = models.Entry.objects.order_by("headline")[0] #排序取第一条
__ = models.Entry.objects.filter(pub_date__gte="2016-05-20") #按时间过滤,查找大于5月20日的数据
#__lte,小于 __exact,精确 __iexact 不区分大小写精确查询 contains_entry = models.Entry.objects.get(headline__contains="python") #包含查询 #print(one_entry,offset_entry,order_entry, contains_entry)
#values and values_list
>>> models.Customer.objects.filter(pk=1).values('qq','stu_id','name')
[{'name': '黑娃', 'qq': '123456789', 'stu_id': 'st10000'}]
>>> models.Customer.objects.filter(pk=1).values_list('qq','stu_id','name')
[('123456789', 'st10000', '黑娃')]
对同一表内不同的字段进行对比查询
F表达式: http://python.usyiyi.cn/django/ref/models/expressions.html#django.db.models.F
F()表达式的效率上的优点主要体现在:
- 直接通过数据库操作而不是Python
- 减少数据库查询次数
使用 F() 的另一个好处是通过数据库而不是Python来更新字段值以避免竞态条件.
objs = models.Entry.objects.filter(n_comments__lte=F("n_pingbacks")) #select n_comments,n_pingbacks from Entry where n_comments <= n_pingbacks; f_obj = models.Entry.objects.update(n_pingbacks=F("n_pingbacks")+1) #根据模型中的一个字段更新另外一个字段。这对于在当前值的基础上加上一个值特别有用
复杂表达式
filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。
Q 对象 (django.db.models.Q) 对象用于封装一组关键字参数。
# obj2 = models.Entry.objects.filter(Q(pub_date__gt="2016-05-20")|Q(pub_date="2016-05-15")) # print(obj2) q_obj = models.Entry.objects.filter(Q(n_comments__lte=F('n_pingbacks'))|Q(pub_date__gte='2016-05-21')) #print("----->", q_obj)
查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面
Poll.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), question__startswith='Who')
分类聚合
from django.db.models import Avg,Max,Min,Count
from app01 import models as book_models # Count使用
pub_objs = book_models.Publisher.objects.annotate(book_nums=Count('book')) # 查询出版社 统计出版的书数量 for publisher in pub_objs: # 分类聚合 循环每一个出版社 print(publisher.book_nums) # 查看出版社下的出版书籍量
# Aggregate使用 print(models.Entry.objects.all().aggregate(Avg("n_pingbacks"),Min("n_pingbacks"),Max("n_pingbacks")))
更新对象
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') 查询到的所有数据 将其中一个字段更新为一个特定的值
更新外键关联字段
>>> b = Blog.objects.get(pk=1) # Change every Entry so that it belongs to this Blog. >>> Entry.objects.all().update(blog=b)
缓存和查询集
每个查询集都包含一个缓存来最小化对数据库的访问。在一个新创建的查询集中,缓存为空。首次对查询集进行求值 —— 同时发生数据库查询 ——Django 将保存查询的结果到查询集的缓存中并返回明确请求的结果
(例如,如果正在迭代查询集,则返回下一个结果)。
>>> print([e.headline for e in Entry.objects.all()]) >>> print([e.pub_date for e in Entry.objects.all()])
这意味着相同的数据库查询将执行两次,显然倍增了你的数据库负载。同时,还有可能两个结果列表并不包含相同的数据库记录,因为在两次请求期间有可能有Entry被添加进来或删除掉。通过下面的方式可以解决:
>>> queryset = Entry.objects.all() >>> print([p.headline for p in queryset]) # Evaluate the query set. >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.