python  Django教程  之 模型(数据库)、自定义Field、数据表更改、QuerySet API

一、Django 模型(数据库)

Django 模型是与数据库相关的,与数据库相关的代码一般写在 models.py 中,Django 支持 sqlite3, MySQL, PostgreSQL等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用。

本节的代码:(Django 1.6, Python 2.7 测试环境)

大家按照我步骤来开始做:

django-admin.py startproject learn_models # 新建一个项目
cd learn_models # 进入到该项目的文件夹
django-admin.py startapp people # 新建一个 people 应用(app)

补充:新建app也可以用 python manage.py startapp people, 需要指出的是,django-admin.py 是安装Django后多出的一个命令,并不是指一个 django-admin.py 脚本在当前目录下。

那么project和app什么关系呢,一个项目一般包含多个应用,一个应用也可以用在多个项目中。

将我们新建的应用(people)添加到 settings.py 中的 INSTALLED_APPS中,也就是告诉Django有这么一个应用。

INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'people',
)

我们打开 people/models.py 文件,修改其中的代码如下:

from django.db import models

class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()

我们新建了一个Person类,继承自models.Model, 一个人有姓名和年龄。这里用到了两种Field,更多Field类型可以参考教程最后的链接。

我们来同步一下数据库

python manage.py syncdb # 进入 manage.py 所在的那个文件夹下输入这个命令

注意:Django 1.7 及以上的版本需要用以下命令
python manage.py makemigrations
python manage.py migrate

我们会看到,Django生成了一系列的表,也生成了我们新建的people_person这个表,那么如何使用这个表呢?

Django提供了丰富的API, 下面演示如何使用它。

$ python manage.py shell

>>> from people.models import Person
>>> Person.objects.create(name="WeizhongTu", age=24)
<Person: Person object>
>>>

我们新建了一个用户WeizhongTu 那么如何从数据库是查询到它呢?

>>> Person.objects.get(name="WeizhongTu")
<Person: Person object>
>>>

我们用了一个 .objects.get() 方法查询出来符合条件的对象,但是大家注意到了没有,查询结果中显示<Person: Person object>,这里并没有显示出与WeizhongTu的相关信息,如果用户多了就无法知道查询出来的到底是谁,查询结果是否正确,我们重新修改一下 people/models.py

name 和 age 等字段中不能有 __(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等)

也不能有Python中的关键字,name 是合法的,student_name 也合法,但是student__name不合法,try, class, continue 也不合法,因为它是Python的关键字( import keyword; print(keyword.kwlist) 可以打出所有的关键字)

from django.db import models

class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField() def __unicode__(self):
# 在Python3中使用 def __str__(self)
return self.name

按CTRL + C退出当前的Python shell, 重复上面的操作,我们就可以看到:

新建一个对象的方法有以下几种:

  1. Person.objects.create(name=name,age=age)

  2. p = Person(name="WZ", age=23)

    p.save()

  3. p = Person(name="TWZ")

    p.age = 23

    p.save()

  4. Person.objects.get_or_create(name="WZT", age=23)

    这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False, 新建时返回的是True, 已经存在时返回False.

获取对象有以下方法:

  1. Person.objects.all()

  2. Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存

  3. Person.objects.get(name=name)

    get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter

  4. Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人

  5. Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件

  6. Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人

  7. Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写

  8. Person.objects.filter(name__regex="^abc") # 正则表达式查询

  9. Person.objects.filter(name__iregex="^abc")# 正则表达式不区分大小写

    filter是找出满足条件的,当然也有排除符合某条件的

  10. Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象

  11. Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的

 数据字段含义

1、models.AutoField  自增列 = int(11)
  如果没有的话,默认会生成一个名称为 id 的列,如果要显示的自定义一个自增列,必须将给列设置为主键 primary_key=True。
2、models.CharField  字符串字段
  必须 max_length 参数
3、models.BooleanField  布尔类型=tinyint(1)
  不能为空,Blank=True
4、models.ComaSeparatedIntegerField  用逗号分割的数字=varchar
  继承CharField,所以必须 max_lenght 参数
5、models.DateField  日期类型 date
  对于参数,auto_now = True 则每次更新都会更新这个时间;auto_now_add 则只是第一次创建添加,之后的更新不再改变。
6、models.DateTimeField  日期类型 datetime
  同DateField的参数
7、models.Decimal  十进制小数类型 = decimal
  必须指定整数位max_digits和小数位decimal_places
8、models.EmailField  字符串类型(正则表达式邮箱) =varchar
  对字符串进行正则表达式
9、models.FloatField  浮点类型 = double
10、models.IntegerField  整形
11、models.BigIntegerField  长整形
  integer_field_ranges = {
    'SmallIntegerField': (-32768, 32767),
    'IntegerField': (-2147483648, 2147483647),
    'BigIntegerField': (-9223372036854775808, 9223372036854775807),
    'PositiveSmallIntegerField': (0, 32767),
    'PositiveIntegerField': (0, 2147483647),
  }
12、models.IPAddressField  字符串类型(ip4正则表达式)
13、models.GenericIPAddressField  字符串类型(ip4和ip6是可选的)
  参数protocol可以是:both、ipv4、ipv6
  验证时,会根据设置报错
14、models.NullBooleanField  允许为空的布尔类型
15、models.PositiveIntegerFiel  正Integer
16、models.PositiveSmallIntegerField  正smallInteger
17、models.SlugField  减号、下划线、字母、数字
18、models.SmallIntegerField  数字
  数据库中的字段有:tinyint、smallint、int、bigint
19、models.TextField  字符串=longtext
20、models.TimeField  时间 HH:MM[:ss[.uuuuuu]]
21、models.URLField  字符串,地址正则表达式
22、models.BinaryField  二进制
23、models.ImageField 图片
24、models.FilePathField 文件

字段含义

字段参数

1、null=True
  数据库中字段是否可以为空
2、blank=True
  django的 Admin 中添加数据时是否可允许空值
3、primary_key = False
  主键,对AutoField设置主键后,就会代替原来的自增 id 列
4、auto_now 和 auto_now_add
  auto_now 自动创建---无论添加或修改,都是当前操作的时间
  auto_now_add 自动创建---永远是创建时的时间
5、choices
GENDER_CHOICE = (
(u'M', u'Male'),
(u'F', u'Female'),
)
gender = models.CharField(max_length=2,choices = GENDER_CHOICE)
6、max_length
7、default  默认值
8、verbose_name  Admin中字段的显示名称
9、name|db_column  数据库中的字段名称
10、unique=True  不允许重复
11、db_index = True  数据库索引
12、editable=True  在Admin里是否可编辑
13、error_messages=None  错误提示
14、auto_created=False  自动创建
15、help_text  在Admin中提示帮助信息
16、validators=[]
17、upload-to

字段参数

数据库基本操作

1.基本操作

    • 增加:创建实例,并调用save
    • 更新:a.获取实例,再sava;b.update(指定列)
    • 删除:a. filter().delete(); b.all().delete()
    • 获取:a. 单个=get(id=1) ;b. 所有 = all()
    • 过滤:filter(name='xxx');filter(name__contains='');(id__in = [1,2,3]) ;
      icontains(大小写无关的LIKE),startswith和endswith, 还有range(SQLBETWEEN查询)'gt', 'in', 'isnull', 'endswith', 'contains', 'lt', 'startswith', 'iendswith', 'icontains','range', 'istartswith'
    • 排序:order_by("name") =asc ;order_by("-name")=desc
    • 返回第n-m条:第n条[0];前两条[0:2]
    • 指定映射:values
    • 数量:count()
    • 聚合:from django.db.models import Min,Max,Sum objects.all().aggregate(Max('guest_id'))
    • 原始SQL
    • cursor = connection.cursor()
      cursor.execute('''SELECT DISTINCT first_name ROM people_person WHERE last_name = %s""", ['Lennon'])
      row = cursor.fetchone()
# 增
#
# models.Tb1.objects.create(c1='xx', c2='oo') 增加一条数据,可以接受字典类型数据 **kwargs # obj = models.Tb1(c1='xx', c2='oo')
# obj.save() # 查
#
# models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议)
# models.Tb1.objects.all() # 获取全部
# models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 # 删
#
# models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据 # 改
# models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs
# obj = models.Tb1.objects.get(id=1)
# obj.c1 = '111'
# obj.save() # 修改单条数据

基本操作

2、进阶操作(了不起的双下划线)

利用双下划线将字段和对应的操作连接起来

# 获取个数
#
# models.Tb1.objects.filter(name='seven').count() # 大于,小于
#
# models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
# models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
# models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in
#
# models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
# models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # contains
#
# models.Tb1.objects.filter(name__contains="ven")
# models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
# models.Tb1.objects.exclude(name__icontains="ven") # range
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似
#
# startswith,istartswith, endswith, iendswith, # order by
#
# models.Tb1.objects.filter(name='seven').order_by('id') # asc
# models.Tb1.objects.filter(name='seven').order_by('-id') # desc # limit 、offset
#
# models.Tb1.objects.all()[10:20] # group by
from django.db.models import Count, Min, Max, Sum
# models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

进阶操作

3、连表操作(了不起的双下划线)

利用双下划线和 _set 将表之间的操作连接起来

class UserProfile(models.Model):
user_info = models.OneToOneField('UserInfo')
username = models.CharField(max_length=64)
password = models.CharField(max_length=64) def __unicode__(self):
return self.username class UserInfo(models.Model):
user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),
)
user_type = models.IntegerField(choices=user_type_choice)
name = models.CharField(max_length=32)
email = models.CharField(max_length=32)
address = models.CharField(max_length=128) def __unicode__(self):
return self.name class UserGroup(models.Model): caption = models.CharField(max_length=64) user_info = models.ManyToManyField('UserInfo') def __unicode__(self):
return self.caption class Host(models.Model):
hostname = models.CharField(max_length=64)
ip = models.GenericIPAddressField()
user_group = models.ForeignKey('UserGroup') def __unicode__(self):
return self.hostname

表结构实例

user_info_obj = models.UserInfo.objects.filter(id=1).first()
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password user_info_obj = models.UserInfo.objects.filter(id=1).values('email', 'userprofile__username').first()
print user_info_obj.keys()
print user_info_obj.values()

一对一操作

类似一对一
1、搜索条件使用 __ 连接
2、获取值时使用 . 连接

一对多

user_info_obj = models.UserInfo.objects.get(name=u'武沛齐')
user_info_objs = models.UserInfo.objects.all() group_obj = models.UserGroup.objects.get(caption='CEO')
group_objs = models.UserGroup.objects.all() # 添加数据
#group_obj.user_info.add(user_info_obj)
#group_obj.user_info.add(*user_info_objs) # 删除数据
#group_obj.user_info.remove(user_info_obj)
#group_obj.user_info.remove(*user_info_objs) # 添加数据
#user_info_obj.usergroup_set.add(group_obj)
#user_info_obj.usergroup_set.add(*group_objs) # 删除数据
#user_info_obj.usergroup_set.remove(group_obj)
#user_info_obj.usergroup_set.remove(*group_objs) # 获取数据
#print group_obj.user_info.all()
#print group_obj.user_info.all().filter(id=1) # 获取数据
#print user_info_obj.usergroup_set.all()
#print user_info_obj.usergroup_set.all().filter(caption='CEO')
#print user_info_obj.usergroup_set.all().filter(caption='DBA')

多对多

# F 使用查询条件的值
#
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1) # Q 构建搜索条件
from django.db.models import Q
# con = Q()
#
# q1 = Q()
# q1.connector = 'OR'
# q1.children.append(('id', 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))
#
# con.add(q1, 'AND')
# con.add(q2, 'AND')
#
# models.Tb1.objects.filter(con) #
# from django.db import connection
# cursor = connection.cursor()
# cursor.execute("""SELECT * from tb where name = %s""", ['Lennon'])
# row = cursor.fetchone()
复制代码

其他操作

注意:xx_set中的[_set]是多对多中的固定搭配

扩展:

a、自定义上传

def upload_file(request):
if request.method == "POST":
obj = request.FILES.get('fafafa')
f = open(obj.name, 'wb')
for chunk in obj.chunks():
f.write(chunk)
f.close()
return render(request, 'file.html')

自定义上传文件

b、Form上传文件实例

class FileForm(forms.Form):
ExcelFile = forms.FileField()

From

from django.db import models

class UploadFile(models.Model):
userid = models.CharField(max_length = 30)
file = models.FileField(upload_to = './upload/')
date = models.DateTimeField(auto_now_add=True)

module

def UploadFile(request):
uf = AssetForm.FileForm(request.POST,request.FILES)
if uf.is_valid():
upload = models.UploadFile()
upload.userid = 1
upload.file = uf.cleaned_data['ExcelFile']
upload.save() print upload.file

View

django中的Form一般有两种功能:

  • 输入html
  • 验证用户输入
复制代码
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
from django import forms
from django.core.exceptions import 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(value):
raise ValidationError('手机号码格式错误') class PublishForm(forms.Form): user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),
) user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,
attrs={'class': "form-control"})) title = forms.CharField(max_length=20,
min_length=5,
error_messages={'required': u'标题不能为空',
'min_length': u'标题最少为5个字符',
'max_length': u'标题最多为20个字符'},
widget=forms.TextInput(attrs={'class': "form-control",
'placeholder': u'标题5-20个字符'})) memo = forms.CharField(required=False,
max_length=256,
widget=forms.widgets.Textarea(attrs={'class': "form-control no-radius", 'placeholder': u'详细描述', 'rows': 3})) phone = forms.CharField(validators=[mobile_validate, ],
error_messages={'required': u'手机不能为空'},
widget=forms.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'})) email = forms.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=forms.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

From

def publish(request):
ret = {'status': False, 'data': '', 'error': '', 'summary': ''}
if request.method == 'POST':
request_form = PublishForm(request.POST)
if request_form.is_valid():
request_dict = request_form.clean()
print request_dict
ret['status'] = True
else:
error_msg = request_form.errors.as_json()
ret['error'] = json.loads(error_msg)
return HttpResponse(json.dumps(ret))

View

二、Django 自定义Field

Django 的官方提供了很多的 Field,但是有时候还是不能满足我们的需求,不过Django提供了自定义 Field 的方法:

提示:如果现在用不到可以跳过这一节,不影响后面的学习,等用到的时候再来学习不迟。

来一个简单的例子吧。

1. 减少文本的长度,保存数据的时候压缩,读取的时候解压缩,如果发现压缩后更长,就用原文本直接存储:

Django 1.7 以下

from django.db import models

class CompressedTextField(models.TextField):
""" model Fields for storing text in a compressed format (bz2 by default) """
__metaclass__ = models.SubfieldBase def to_python(self, value):
if not value:
return value try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value def get_prep_value(self, value):
if not value:
return value try:
value.decode('base64')
return value
except Exception:
try:
tmp = value.encode('utf-8').encode('bz2').encode('base64')
except Exception:
return value
else:
if len(tmp) > len(value):
return value return tmp

to_python 函数用于转化数据库中的字符到 Python的变量, get_prep_value 用于将Python变量处理后(此处为压缩)保存到数据库,使用和Django自带的 Field 一样。

Django 1.8 以上版本,可以用

#coding:utf-8
from django.db import models class CompressedTextField(models.TextField):
"""
model Fields for storing text in a compressed format (bz2 by default)
""" def from_db_value(self, value, expression, connection, context):
if not value:
return value
try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value def to_python(self, value):
if not value:
return value
try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value def get_prep_value(self, value):
if not value:
return value
try:
value.decode('base64')
return value
except Exception:
try:
return value.encode('utf-8').encode('bz2').encode('base64')
except Exception:
return value

Django 1.8及以上版本中,from_db_value 函数用于转化数据库中的字符到 Python的变量

2. 比如我们想保存一个 列表到数据库中,在读取用的时候要是 Python的列表的形式,我们来自己写一个 ListField

这个ListField继承自 TextField,代码如下:

from django.db import models
import ast class ListField(models.TextField):
__metaclass__ = models.SubfieldBase
description = "Stores a python list" def __init__(self, *args, **kwargs):
super(ListField, self).__init__(*args, **kwargs) def to_python(self, value):
if not value:
value = [] if isinstance(value, list):
return value return ast.literal_eval(value) def get_prep_value(self, value):
if value is None:
return value return unicode(value) # use str(value) in Python 3 def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)

使用它很简单,首先导入 ListField,像自带的 Field 一样使用:

class Article(models.Model):
labels = ListField()

在终端上尝试(运行 python manage.py shell 进入):

>>> from app.models import Article
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]

三、Django 数据表更改

我们设计数据库的时候,早期设计完后,后期会发现不完善,需要对数据表进行更改.

Django 1.7.x 及以后的版本集成了 South 的功能,在修改models.py了后运行:

python manage.py makemigrations
python manage.py migrate

这两行命令就会对我们的models.py 进行检测,自动发现需要更改的,应用到数据库中去。

Django 1.6.x 及以前:

写过Django项目的同学,必然会遇到这个问题:

在Django 1.6以及以前的版本中,我们测试,当发现model要改,怎么办?

我们修改了 models.py 之后,我们运行:

python manage.py syncdb

这句话只会将我们在 models.py 中新加的类创建相应的表。

对于原来有的,现在删除了的类,Django 会询问是否要删除数据库中已经存在的相关数据表。

如果在原来的类上增加字段或者删除字段,可以参考这个命令:

python manage.py sql appname

给出的SQL语句,然后自己手动到数据库执行 SQL 。但是这样非常容易出错!

Django 的第三方 app South 就是专门做数据库表结构自动迁移工作,Jacob Kaplan-Moss 曾做过一次调查,South 名列最受欢迎的第三方 app。事实上,它现在已经俨然成为 Django 事实上的数据库表迁移标准,很多第三方 app 都会带 South migrations 脚本,Django 1.7 中集成了 South 的功能。

1, 安装South

(sudo) pip install South

2. 使用方法

一个好的程序使用起来必定是简单的,South和它的宗旨一样,使用简单。只需要简单几步,针对已经建好model和创建完表的应用。

把south加入到settings.py中的INSTALL_APPS中

# Application definition
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles', 'blog',
'south',
)

修改好后运行一次 python manage.py syncdb,Django会新建一个 south_migrationhistory 表,用来记录数据表更改(Migration)的历史纪录。

$ python manage.py syncdb
Syncing...
Creating tables ...
Creating table south_migrationhistory
Installing custom SQL ...
Installing indexes ...
No fixtures found. Synced:
> django.contrib.admin
> django.contrib.auth
> django.contrib.contenttypes
> django.contrib.sessions
> django.contrib.messages
> django.contrib.staticfiles
> blog
> south Not synced (use migrations):

如果要把之前建好的比如 blog 这个 app 使用 South 来管理:

$ python manage.py convert_to_south blog

你会发现blog文件夹中多了一个 migrations 目录,里面有一个 0001_initial.py 文件。

注:如果 blog 这个 app 之前就创建过相关的表,可以用下面的来“假装”用 South 创建(伪创建,在改动 models.py 之前运行这个)

python manage.py migrate blog --fake

意思是这个表我以前已经建好了,用 South 只是纪一下这个创建记录,下次 migrate 的时候不必再创建了。

原理就是 south_migrationhistory 中记录下了 models.py 的修改的历史,下次再修改时会和最近一次记录比较,发现改变了什么,然后生成相应的对应文件,最终执行相应的 SQL 更改原有的数据表。

接着,当你对 Blog.models 做任何修改后,只要执行:

$ python manage.py schemamigration blog --auto

South就会帮助我们找出哪些地方做了修改,如果你新增的数据表没有给default值,并且没有设置null=True, south会问你一些问题,因为新增的column对于原来的旧的数据不能为Null的话就得有一个值。顺利的话,在migrations文件夹下会产生一个0002_add_mobile_column.py,但是这一步并没有真正修改数据库的表,我们需要执行 python manage.py migrate :

$ python manage.py migrate
Running migrations for blog:
- Migrating forwards to 0002_add_mobile_column.
> blog:0002_add_mobile_column
- Loading initial data for blog.
No fixtures found.

这样所做的更改就写入到了数据库中了。

恢复到以前

South好处就是可以随时恢复到之前的一个版本,比如我们想要回到最开始的那个版本:

> python manage.py migrate blog 0001
- Soft matched migration 0001 to 0001_initial.
Running migrations for blog:
- Migrating backwards to just after 0001_initial.
< blog:0002_add_mobile_column

这样就搞定了,数据库就恢复到以前了,比你手动更改要方便太多了。

四、Django QuerySet API

从数据库中查询出来的结果一般是一个集合,这个集合叫做 QuerySet。

文中的例子大部分是基于这个 blog/models.py

from django.db import models

class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField() def __unicode__(self): # __str__ on Python 3
return self.name class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField() def __unicode__(self): # __str__ on Python 3
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 __unicode__(self): # __str__ on Python 3
return self.headline

1. QuerySet 创建对象的方法

>>> from blog.models import Blog
>>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
>>> b.save() 总之,一共有四种方法
# 方法 1
Author.objects.create(name="WeizhongTu", email="tuweizhong@163.com") # 方法 2
twz = Author(name="WeizhongTu", email="tuweizhong@163.com")
twz.save() # 方法 3
twz = Author()
twz.name="WeizhongTu"
twz.email="tuweizhong@163.com" # 方法 4,首先尝试获取,不存在就创建,可以防止重复
Author.objects.get_or_create(name="WeizhongTu", email="tuweizhong@163.com")
# 返回值(object, True/False)

备注:前三种方法返回的都是对应的 object,最后一种方法返回的是一个元组,(object, True/False),创建时返回 True, 已经存在时返回 False

当有一对多,多对一,或者多对多的关系的时候,先把相关的对象查询出来

>>> from blog.models import Entry
>>> entry = Entry.objects.get(pk=1)
>>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
>>> entry.blog = cheese_blog
>>> entry.save()

2. 获取对象的方法(上一篇的部分代码)

Person.objects.all() # 查询所有
Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存,不支持负索引,后面有相应解决办法,第7条
Person.objects.get(name="WeizhongTu") # 名称为 WeizhongTu 的一条,多条会报错 get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter
Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人
Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件 Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人
Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写 Person.objects.filter(name__regex="^abc") # 正则表达式查询
Person.objects.filter(name__iregex="^abc")# 正则表达式不区分大小写 # filter是找出满足条件的,当然也有排除符合某条件的
Person.objects.exclude(name__contains="WZ") # 排除包含 WZ 的Person对象
Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的

3. QuerySet 是可迭代的,比如:

es = Entry.objects.all()
for e in es:
print(e.headline)

Entry.objects.all() 或者 es 就是 QuerySet 是查询所有的 Entry 条目。

注意事项:

(1). 如果只是检查 Entry 中是否有对象,应该用 Entry.objects.all().exists()

(2). QuerySet 支持切片 Entry.objects.all()[:10] 取出10条,可以节省内存

(3). 用 len(es) 可以得到Entry的数量,但是推荐用 Entry.objects.count()来查询数量,后者用的是SQL:SELECT COUNT(*)

(4). list(es) 可以强行将 QuerySet 变成 列表

4. QuerySet 是可以用pickle序列化到硬盘再读取出来的

>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.

5. QuerySet 查询结果排序

作者按照名称排序

Author.objects.all().order_by('name')
Author.objects.all().order_by('-name') # 在 column name 前加一个负号,可以实现倒序

6. QuerySet 支持链式查询

Author.objects.filter(name__contains="WeizhongTu").filter(email="tuweizhong@163.com")
Author.objects.filter(name__contains="Wei").exclude(email="tuweizhong@163.com") # 找出名称含有abc, 但是排除年龄是23岁的
Person.objects.filter(name__contains="abc").exclude(age=23)

7. QuerySet 不支持负索引

Person.objects.all()[:10] 切片操作,前10条
Person.objects.all()[-10:] 会报错!!! # 1. 使用 reverse() 解决
Person.objects.all().reverse()[:2] # 最后两条
Person.objects.all().reverse()[0] # 最后一条 # 2. 使用 order_by,在栏目名(column name)前加一个负号
Author.objects.order_by('-id')[:20] # id最大的20条

8. QuerySet 重复的问题,使用 .distinct() 去重

一般的情况下,QuerySet 中不会出来重复的,重复是很罕见的,但是当跨越多张表进行检索后,结果并到一起,可以会出来重复的值(我最近就遇到过这样的问题)

qs1 = Pathway.objects.filter(label__name='x')
qs2 = Pathway.objects.filter(reaction__name='A + B >> C')
qs3 = Pathway.objects.filter(inputer__name='WeizhongTu') # 合并到一起
qs = qs1 | qs2 | qs3
这个时候就有可能出现重复的 # 去重方法
qs = qs.distinct()

参考文档:

Django models 官方教程: https://docs.djangoproject.com/en/dev/topics/db/models/

Fields相关官方文档:https://docs.djangoproject.com/en/dev/ref/models/fields/

Django数据库操作官方文档: QuerySet API: https://docs.djangoproject.com/en/dev/ref/models/querysets/

 

python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API的更多相关文章

  1. python django中使用sqlite3数据库 存储二进制数据ByteArray

    在python中使用sqlite3数据库存储二进制流数据ByteArray,在django使用sqlite3数据库时,有时候也要注意最好使用二进制流ByteArray插入字符串. 使用ByteArra ...

  2. python django -2 ORM模型

    ORM简介 MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库 ORM是“对象-关系-映射”的简称 ...

  3. python Django教程 之模板渲染、循环、条件判断、常用的标签、过滤器

    python3.5 manage.py runserver python Django教程 之模板渲染.循环.条件判断.常用的标签.过滤器 一.Django模板渲染模板 1. 创建一个 zqxt_tm ...

  4. python Django教程 之 安装、基本命令、视图与网站

    python  Django教程  之 安装.基本命令.视图与网站 一.简介 Django 中提供了开发网站经常用到的模块,常见的代码都为你写好了,通过减少重复的代码,Django 使你能够专注于 w ...

  5. django学习-10.django连接mysql数据库和创建数据表

    1.django模型 Django对各种数据库提供了很好的支持,包括:PostgreSQL.MySQL.SQLite.Oracle. Django为这些数据库提供了统一的调用API. 我们可以根据自己 ...

  6. ThinkPHP 学习笔记 ( 三 ) 数据库操作之数据表模型和基础模型 ( Model )

    //TP 恶补ing... 一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: publ ...

  7. ThinkPHP 数据库操作之数据表模型和基础模型 ( Model )

    一.定义数据表模型 1.模型映射 要测试数据库是否正常连接,最直接的办法就是在当前控制器中实例化数据表,然后使用 dump 函数输出,查看数据库的链接状态.代码: public function te ...

  8. 孤荷凌寒自学python第四十九天继续研究跨不同类型数据库的通用数据表操作函数

    孤荷凌寒自学python第四十九天继续研究跨不同类型数据库的通用数据表操作函数 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 今天继续建构自感觉用起来顺手些的自定义模块和类的代码. 不同类型 ...

  9. 孤荷凌寒自学python第四十八天通用同一数据库中复制数据表函数最终完成

    孤荷凌寒自学python第四十八天通用同一数据库中复制数据表函数最终完成 (完整学习过程屏幕记录视频地址在文末) 今天继续建构自感觉用起来顺手些的自定义模块和类的代码. 今天经过反复折腾,最终基本上算 ...

随机推荐

  1. php 添加钩子实例

    <?php/*定义钩子函数*/function add($hook,$actionFunc){    global $emHooks;    if(isset($emHooks[$hook])) ...

  2. Android循环滑动寻找元素,直接代码

    #coding=utf-8from appium import webdriverimport time,unittestclass Android_test(unittest.TestCase): ...

  3. 初学Laravel

    之前一直用开tp和ot,本来觉得学会一个tp便可走遍天下,tp的确强大.但后来听到很多同行的同学说他们的公司都开始转型往lv走了,我的同学没有学过lv,然而公司给足时间去让他们去学.当然,缺人可能是占 ...

  4. WPF-流文档元素

    1.Block元素 用于分组其他元素 Paragraph是块级别元素,文本段落 Paragraph.Inlines集合内. 设置第一行缩进量Paragraph.TextIndet 2.Inline元素 ...

  5. install Hadoop

    Installing Java Hadoop runs on both Unix and Windows operating systems, and requires Java to beinsta ...

  6. Oracle 11g r2 安装

    Help Center:http://docs.oracle.com/cd/E11882_01/install.112/e24326/toc.htm#i1011296 前提:linux需要安装图形化介 ...

  7. HTML CSS

    HTML CSS css是英文Cascading Style Sheets的缩写,称为层叠样式表,用于对页面进行美化.存在方式有三种:元素内联.页面嵌入和外部引入,比较三种方式的优缺点.语法:styl ...

  8. JQuery 1.*速成版之二

    过滤选择器简称:过滤器.它其实也是一种选择器,而这种选择器类似与 CSS3(http://t.mb5u.com/css3/)里的伪类,可以让不支持 CSS3 的低版本浏览器也能支持.和常规选择器一样, ...

  9. SQLite常用语句

    // 错误存储路径 - (NSString *)dataFilePath{ NSString *dbPath = [NSSearchPathForDirectoriesInDomains(NSLibr ...

  10. [SQL]查询数据库里都有哪些自己建的表

    方法一:SELECT * FROM FTCE_ACCS.INFORMATION_SCHEMA.TABLES Where TABLE_TYPE='BASE TABLE'----------------- ...