前面豆子已经陆陆续续地学习了在Django中如何操作数据库


单表的基本操作 http://beanxyz.blog.51cto.com/5570417/1945887

常见字段的使用 http://beanxyz.blog.51cto.com/5570417/1945909

最基本的查询方式 http://beanxyz.blog.51cto.com/5570417/1950806

一对多的基本操作和实例  http://beanxyz.blog.51cto.com/5570417/1946602

多对多的基本操作和实例 http://beanxyz.blog.51cto.com/5570417/1952243


下面补充一些高级操作。


条件的过滤

下面是常见的条件设置,除了可以基本的filter之外,我们还有大量的条件语句可以使用。


查询数据库获取的QuerySet类型,对于这个类型我们类似Jquery一样使用链式编程,可以无限制的通过.来添加新的条件来过滤,可以看见大部分条件都是通过双下划线__来实现的


# 获取个数
        # models.Tb1.objects.filter(name='seven').count()

        # 大于,小于
        # models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
        # models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
        # models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
        # models.Tb1.objects.filter(id__lte=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

        # isnull
        # Entry.objects.filter(pub_date__isnull=True)

        # 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

        # 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"

        # limit 、offset
        # models.Tb1.objects.all()[10:20]

        # regex正则匹配,iregex 不区分大小写
        # Entry.objects.get(title__regex=r'^(An?|The) +')
        # Entry.objects.get(title__iregex=r'^(an?|the) +')

        # date
        # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
        # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

        # year
        # Entry.objects.filter(pub_date__year=2005)
        # Entry.objects.filter(pub_date__year__gte=2005)

        # month
        # Entry.objects.filter(pub_date__month=12)
        # Entry.objects.filter(pub_date__month__gte=6)

        # day
        # Entry.objects.filter(pub_date__day=3)
        # Entry.objects.filter(pub_date__day__gte=3)

        # week_day
        # Entry.objects.filter(pub_date__week_day=2)
        # Entry.objects.filter(pub_date__week_day__gte=2)

        # hour
        # Event.objects.filter(timestamp__hour=23)
        # Event.objects.filter(time__hour=5)
        # Event.objects.filter(timestamp__hour__gte=12)

        # minute
        # Event.objects.filter(timestamp__minute=29)
        # Event.objects.filter(time__minute=46)
        # Event.objects.filter(timestamp__minute__gte=29)

        # second
        # Event.objects.filter(timestamp__second=31)
        # Event.objects.filter(time__second=2)
        # Event.objects.filter(timestamp__second__gte=31)


上面这些方法可以实现大部分常见的简单查询过滤。有的时候,我们需要实现一些更复杂的查询语句,上面的语句就不够用了,这个时候可以通过extra来扩展。例如,主要看看select和where的自定义


# extra
    # extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
    #    Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
    #    Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
    #    Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
    #    Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])



反向查询


之前的博文里面,我们都是通过正向查找外键或者中间表来获取另外一个表的信息;如果希望倒过来,也是通过双下划线,比如 表名__字段 这种形式来实现



实例:

3张表,分别是单表,1对多和多对多的关系

#业务线
class Business(models.Model):
    # id
    caption = models.CharField(max_length=32)
#主机
class Host(models.Model):
    nid = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32,db_index=True)
    ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
    port = models.IntegerField()
    b = models.ForeignKey(to="Business", to_field='id')
#程序
class Application(models.Model):
    name = models.CharField(max_length=32,unique=True)
    r = models.ManyToManyField("Host")

视图函数

def tt(request):
    #1对多正向查找
    print('1对多正向查找'.center(40, '-'))
    obj=models.Host.objects.filter(nid__gt=1)
    print(obj[0].nid,obj[0].hostname,obj[0].b.caption)
    #一些过滤条件
    print('过滤条件'.center(40,'-'))
    obj=models.Business.objects.filter(caption__contains='SALE').first()
    print(obj.id,obj.caption)
    obj=models.Business.objects.all().values('id','caption')
    print(obj, obj.order_by('id').reverse())
    obj=models.Application.objects.filter(name__exact='SQL Server').values('name','r__hostname','r__nid')
    print(obj)
    #1对多反向查找
    print('1对多反向查找'.center(40,'-'))
    obj=models.Business.objects.all().values('id','caption','host__ip','host__hostname')
    print(obj[0])
    #多对多正向查找
    print('多对多正向查找'.center(40, '-'))
    obj=models.Application.objects.all().first()
    print(obj.name, obj.r.all()[0].hostname)
    #多对多反向查询
    print('多对多反向查找'.center(40, '-'))
    obj=models.Host.objects.all().filter(nid__gt=1).values('nid','application__name').first()
    print(obj)
    return HttpResponse('ok')



执行结果

----------------1对多正向查找-----------------
183 SYDMGM01 SALE
------------------过滤条件------------------
5 SALE
<QuerySet [{'id': 5, 'caption': 'SALE'}, {'id': 19, 'caption': 'IT'}, {'id': 20, 'caption': 'HR'}]> <QuerySet [{'id': 20, 'caption': 'HR'}, {'id': 19, 'caption': 'IT'}, {'id': 5, 'caption': 'SALE'}]>
<QuerySet [{'name': 'SQL Server', 'r__hostname': 'SYDMGM01', 'r__nid': 183}, {'name': 'SQL Server', 'r__hostname': 'SYDAV01', 'r__nid': 190}, {'name': 'SQL Server', 'r__hostname': 'SYDMGM02', 'r__nid': 191}]>
----------------1对多反向查找-----------------
{'id': 5, 'caption': 'SALE', 'host__ip': '10.2.1.1', 'host__hostname': 'SYDMGM01'}
----------------多对多正向查找-----------------
AA SYDMGM01
----------------多对多反向查找-----------------
{'nid': 183, 'application__name': 'AA'}



性能


假设我们有一个Use表通过外键ut绑定了一个UserType表


默认情况下,如果我们直接使用下面代码,如果uses获取了10行数据,那么数据库实际上查询11次,对user查询一次,然后在for循环里面对usertype查询10次;这样效率很低

users = models.User.objects.all()
for row in users:
    print(row.user,row.pwd,row.ut_id)
    print(row.ut.name)


我们可以通过select_related进行优化,这样第一次查询的时候就进行一个跨表查询,获取指定外键的所有数据

users = models.User.objects.all().select_related('ut')
for row in users:
    print(row.user,row.pwd,row.ut_id)
    print(row.ut.name)


如果数据比较多,外键也多,那么速度可能还会比较慢,比较跨表查询的效率比较低,那么进一步的我们可以通过prefetch_related优化。他的基本原理是进行多次单表查询;比如第一次查询User表,然后第二次查询外键关联的表,然后把所有数据都放在内存里面,这样访问的速度就会快很多了。

users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu')
# select * from users where id > 30
# 获取上一步骤中所有的ut_id=[1,2]
# select * from user_type where id in [1,2]
# select * from user_type where id in [1,2]
for row in users:
    print(row.user,row.pwd,row.ut_id)
    print(row.ut.name)



参考资料:http://www.cnblogs.com/wupeiqi/articles/5246483.html