django中遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。
一、创建表
1、基本结构
1 from django.db import models 2 3 class userinfo(models.Model): 4 name = models.CharField(max_length=30) 5 email = models.EmailField() 6 memo = models.TextField()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 AutoField(Field) 2 - int自增列,必须填入参数 primary_key=True 3 4 BigAutoField(AutoField) 5 - bigint自增列,必须填入参数 primary_key=True 6 7 注:当model中如果没有自增列,则自动会创建一个列名为id的列 8 from django.db import models 9 10 class UserInfo(models.Model): 11 # 自动创建一个列名为id的且为自增的整数列 12 username = models.CharField(max_length=32) 13 14 class Group(models.Model): 15 # 自定义自增列 16 nid = models.AutoField(primary_key=True) 17 name = models.CharField(max_length=32) 18 19 SmallIntegerField(IntegerField): 20 - 小整数 -32768 ~ 32767 21 22 PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) 23 - 正小整数 0 ~ 32767 24 IntegerField(Field) 25 - 整数列(有符号的) -2147483648 ~ 2147483647 26 27 PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) 28 - 正整数 0 ~ 2147483647 29 30 BigIntegerField(IntegerField): 31 - 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807 32 33 自定义无符号整数字段 34 35 class UnsignedIntegerField(models.IntegerField): 36 def db_type(self, connection): 37 return 'integer UNSIGNED' 38 39 PS: 返回值为字段在数据库中的属性,Django字段默认的值为: 40 'AutoField': 'integer AUTO_INCREMENT', 41 'BigAutoField': 'bigint AUTO_INCREMENT', 42 'BinaryField': 'longblob', 43 'BooleanField': 'bool', 44 'CharField': 'varchar(%(max_length)s)', 45 'CommaSeparatedIntegerField': 'varchar(%(max_length)s)', 46 'DateField': 'date', 47 'DateTimeField': 'datetime', 48 'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)', 49 'DurationField': 'bigint', 50 'FileField': 'varchar(%(max_length)s)', 51 'FilePathField': 'varchar(%(max_length)s)', 52 'FloatField': 'double precision', 53 'IntegerField': 'integer', 54 'BigIntegerField': 'bigint', 55 'IPAddressField': 'char(15)', 56 'GenericIPAddressField': 'char(39)', 57 'NullBooleanField': 'bool', 58 'OneToOneField': 'integer', 59 'PositiveIntegerField': 'integer UNSIGNED', 60 'PositiveSmallIntegerField': 'smallint UNSIGNED', 61 'SlugField': 'varchar(%(max_length)s)', 62 'SmallIntegerField': 'smallint', 63 'TextField': 'longtext', 64 'TimeField': 'time', 65 'UUIDField': 'char(32)', 66 67 BooleanField(Field) 68 - 布尔值类型 69 70 NullBooleanField(Field): 71 - 可以为空的布尔值 72 73 CharField(Field) 74 - 字符类型 75 - 必须提供max_length参数, max_length表示字符长度 76 77 TextField(Field) 78 - 文本类型 79 80 EmailField(CharField): 81 - 字符串类型,Django Admin以及ModelForm中提供验证机制 82 83 IPAddressField(Field) 84 - 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制 85 86 GenericIPAddressField(Field) 87 - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6 88 - 参数: 89 protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6" 90 unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启刺功能,需要protocol="both" 91 92 URLField(CharField) 93 - 字符串类型,Django Admin以及ModelForm中提供验证 URL 94 95 SlugField(CharField) 96 - 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号) 97 98 CommaSeparatedIntegerField(CharField) 99 - 字符串类型,格式必须为逗号分割的数字 100 101 UUIDField(Field) 102 - 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证 103 104 FilePathField(Field) 105 - 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能 106 - 参数: 107 path, 文件夹路径 108 match=None, 正则匹配 109 recursive=False, 递归下面的文件夹 110 allow_files=True, 允许文件 111 allow_folders=False, 允许文件夹 112 113 FileField(Field) 114 - 字符串,路径保存在数据库,文件上传到指定目录 115 - 参数: 116 upload_to = "" 上传文件的保存路径 117 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage 118 119 ImageField(FileField) 120 - 字符串,路径保存在数据库,文件上传到指定目录 121 - 参数: 122 upload_to = "" 上传文件的保存路径 123 storage = None 存储组件,默认django.core.files.storage.FileSystemStorage 124 width_field=None, 上传图片的高度保存的数据库字段名(字符串) 125 height_field=None 上传图片的宽度保存的数据库字段名(字符串) 126 127 DateTimeField(DateField) 128 - 日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ] 129 130 DateField(DateTimeCheckMixin, Field) 131 - 日期格式 YYYY-MM-DD 132 133 TimeField(DateTimeCheckMixin, Field) 134 - 时间格式 HH:MM[:ss[.uuuuuu]] 135 136 DurationField(Field) 137 - 长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型 138 139 FloatField(Field) 140 - 浮点型 141 142 DecimalField(Field) 143 - 10进制小数 144 - 参数: 145 max_digits,小数总长度 146 decimal_places,小数位长度 147 148 BinaryField(Field) 149 - 二进制类型
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 null 数据库中字段是否可以为空 2 db_column 数据库中字段的列名 3 db_tablespace 4 default 数据库中字段的默认值 5 primary_key 数据库中字段是否为主键 6 db_index 数据库中字段是否可以建立索引 7 unique 数据库中字段是否可以建立唯一索引 8 unique_for_date 数据库中字段【日期】部分是否可以建立唯一索引 9 unique_for_month 数据库中字段【月】部分是否可以建立唯一索引 10 unique_for_year 数据库中字段【年】部分是否可以建立唯一索引 11 12 verbose_name Admin中显示的字段名称 13 blank Admin中是否允许用户输入为空 14 editable Admin中是否可以编辑 15 help_text Admin中该字段的提示信息 16 choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作 17 如:gf = models.IntegerField(choices=[(0, '何穗'),(1, '大表姐'),],default=1) 18 19 error_messages 自定义错误信息(字典类型),从而定制想要显示的错误信息; 20 字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date 21 如:{'null': "不能为空.", 'invalid': '格式错误'} 22 23 validators 自定义错误验证(列表类型),从而定制想要的验证规则 24 from django.core.validators import RegexValidator 25 from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\ 26 MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator 27 如: 28 test = models.CharField( 29 max_length=32, 30 error_messages={ 31 'c1': '优先错信息1', 32 'c2': '优先错信息2', 33 'c3': '优先错信息3', 34 }, 35 validators=[ 36 RegexValidator(regex='root_\d+', message='错误了', code='c1'), 37 RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'), 38 EmailValidator(message='又错误了', code='c3'), ] 39 )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 class UserInfo(models.Model): 2 nid = models.AutoField(primary_key=True) 3 username = models.CharField(max_length=32) 4 class Meta: 5 # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名 6 db_table = "table_name" 7 8 # 联合索引 9 index_together = [ 10 ("pub_date", "deadline"), 11 ] 12 13 # 联合唯一索引 14 unique_together = (("driver", "restaurant"),) 15 16 # admin中显示的表名称 17 verbose_name 18 19 # verbose_name加s 20 verbose_name_plural 21 22 更多:https://docs.djangoproject.com/en/1.10/ref/models/options/
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 1.触发Model中的验证和错误提示有两种方式: 2 a. Django Admin中的错误信息会优先根据Admiin内部的ModelForm错误信息提示,如果都成功,才来检查Model的字段并显示指定错误信息 3 b. 调用Model对象的 clean_fields 方法,如: 4 # models.py 5 class UserInfo(models.Model): 6 nid = models.AutoField(primary_key=True) 7 username = models.CharField(max_length=32) 8 9 email = models.EmailField(error_messages={'invalid': '格式错了.'}) 10 11 # views.py 12 def index(request): 13 obj = models.UserInfo(username='11234', email='uu') 14 try: 15 print(obj.clean_fields()) 16 except Exception as e: 17 print(e) 18 return HttpResponse('ok') 19 20 # Model的clean方法是一个钩子,可用于定制操作,如:上述的异常处理。 21 22 2.Admin中修改错误提示 23 # admin.py 24 from django.contrib import admin 25 from model_club import models 26 from django import forms 27 28 29 class UserInfoForm(forms.ModelForm): 30 username = forms.CharField(error_messages={'required': '用户名不能为空.'}) 31 email = forms.EmailField(error_messages={'invalid': '邮箱格式错误.'}) 32 age = forms.IntegerField(initial=1, error_messages={'required': '请输入数值.', 'invalid': '年龄必须为数值.'}) 33 34 class Meta: 35 model = models.UserInfo 36 # fields = ('username',) 37 fields = "__all__" 38 39 40 class UserInfoAdmin(admin.ModelAdmin): 41 form = UserInfoForm 42 43 44 admin.site.register(models.UserInfo, UserInfoAdmin)
注意:
model中包含很弱的验证功能,一般不用,小型项目、用modelform;耦合性特别强,解耦要重构,应该用model操作数据库;form来进行数据验证
models基本增删改查:
1 from cmdb import models # 导入models 2 3 def orm(request): 4 # 创建一条数据 5 # 推荐方式一: 6 # models.UserInfo.objects.create(username='root', password='123') # UserInfo为表名 7 8 # 第二种方式 9 # dic = {'username': 'eric', 'password': '123'} 10 # models.UserInfo.objects.create(**dic) 11 12 # 第三种方式: 13 # obj = models.UserInfo(username='alex', password='6666') 14 # obj.save() 15 16 17 # 查 18 # result = models.UserInfo.objects.all() 19 # result = models.UserInfo.objects.filter(username='root', password='123') 20 # for row in result: 21 # print(row.id, row.username, row.password) 22 # print(result) 23 24 # models.Tb1.objects.exclude(name='seven') # 获取指定条件取反的数据 25 26 27 28 # 删除: 29 # models.UserInfo.objects.filter(username='alex').delete() 30 31 # 更新: 32 # models.UserInfo.objects.filter(id=5).update(password='66666666666666666') 33 34 return HttpResponse('orm') 35
1 获取数据库表中数据的方法:values、values_list、filter、get 2 3 all() 取到的是QuerySet对象 4 5 all().values(‘id’, 'caption') 取到的元素是字典(记: values就是字典的key:values) 6 7 all().values_list('id','caption') 取到的元素是元组 8 9 10 11 12 获取单个值: 13 14 models.Business.objects.get(id=1) 获取一个对象,不存在直接报错 15 16 models.Business.objects.filter(id=1).first() 获取一个对象,不存在返回none 17 18
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from django.shortcuts import render 2 from app02 import models 3 # Create your views here. 4 5 6 def business(request): 7 v1 = models.Business.objects.all() 8 v2 = models.Business.objects.all().values('id', 'caption') # values返回值不再是对象而是字典[{'id':1, "caption":"运维部"}...] 9 v3 = models.Business.objects.all().values_list('id', 'caption') # [(1, 运维部),(2, 开发)] 10 11 return render(request, 'business.html', {'v1': v1, 'v2': v2, 'v3': v3}) 12 13 views.py
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>业务线列表(对象)</h1> 9 <ul> 10 {% for row in v1 %} 11 <li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li> 12 {% endfor %} 13 </ul> 14 <h1>业务线列表(字典)</h1> 15 <ul> 16 {% for row in v2 %} 17 <li>{{ row.id }} - {{ row.caption }}</li> 18 {% endfor %} 19 </ul> 20 <h1>业务线列表(元祖)</h1> 21 <ul> 22 {% for row in v3 %} 23 <li>{{ row.0 }} - {{ row.1 }}</li> 24 {% endfor %} 25 </ul> 26 </body> 27 </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from django.db import models 2 3 # Create your models here. 4 5 6 class Business(models.Model): 7 8 caption = models.CharField(max_length=32) 9 code = models.CharField(max_length=32, default="sa") # null=True 设置默认为空;default设置默认值为sa 10 11 12 class Host(models.Model): 13 nid = models.AutoField(primary_key=True) 14 hostname = models.CharField(max_length=32, db_index=True) 15 ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) 16 port = models.IntegerField() 17 b = models.ForeignKey(to="Business", to_field="id", on_delete=models.PROTECT)
一对多操作的三种方式, 双下滑下和 . 的区别:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from django.shortcuts import render 2 from app02 import models 3 # Create your views here. 4 5 6 def business(request): 7 v1 = models.Business.objects.all() 8 v2 = models.Business.objects.all().values('id', 'caption') # values返回值不再是对象而是字典[{'id':1, "caption":"运维部"}...] 9 v3 = models.Business.objects.all().values_list('id', 'caption') # [(1, 运维部),(2, 开发)] 10 11 return render(request, 'business.html', {'v1': v1, 'v2': v2, 'v3': v3}) 12 13 14 def host(request): 15 v1 = models.Host.objects.filter(nid__gt=0) 16 for row in v1: 17 print(row.nid, row.hostname, row.ip, row.port, row.b_id, row.b.caption, row.b.code, row.b.id, sep='\t') #通过对象跨表操作直接用点即可 18 19 v2 = models.Host.objects.filter(nid__gt=0).values('nid', 'hostname', 'b_id', 'b__caption') # 在models.Host.objects之后进行跨表时要用双下滑线,字符串的形式进行跨表操作 20 print(v2) # 模板语言对于字典类型,前端取值只能通过点 . 来取值 21 # <QuerySet [{'b_id': 1, 'b__caption': '运维', 'hostname': 'c1.com', 'nid': 1}, 22 # {'b_id': 1, 'b__caption': '运维', 'hostname': 'c2.com', 'nid': 2}, 23 # {'b_id': 2, 'b__caption': '开发', 'hostname': 'c3', 'nid': 3}]> 24 for row in v2: 25 print(row['nid'], row['hostname'], row['b_id'], row['b__caption']) 26 27 v3 = models.Host.objects.filter(nid__gt=0).values_list('nid', 'hostname', 'b_id', 'b__caption') 28 print(v3) # 模板语言对于元组类型,前端取值只能通过values_list中字符串顺序的索引来取值 29 # <QuerySet [(1, 'c1.com', 1, '运维'), (2, 'c2.com', 1, '运维'), (3, 'c3', 2, '开发')]> 30 for row in v3: 31 print(row[0], row[1], row[2], row[3]) 32 33 return render(request, 'host.html', {'v1': v1, 'v2': v2, 'v3': v3})
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Title</title> 6 </head> 7 <body> 8 <h1>业务线列表</h1> 9 <table border="1"> 10 <thead> 11 <tr> 12 <th>主机ID</th> 13 <th>主机名</th> 14 <th>IP</th> 15 <th>端口</th> 16 <th>业务线名称</th> 17 </tr> 18 </thead> 19 <tbody> 20 {% for row in v1 %} 21 <tr hid="{{ row.id }}" bid="{{ row.b_id }}"> 22 <td>{{ row.hostname }}</td> 23 <td>{{ row.ip }}</td> 24 <td>{{ row.port }}</td> 25 <td>{{ row.b_id }}</td> 26 <td>{{ row.b.caption }}</td> 27 </tr> 28 {% endfor %} 29 </tbody> 30 <tbody> 31 {% for row in v2 %} 32 <tr hid="{{ row.id }}" bid="{{ row.b_id }}"> 33 <td>{{ row.hostname }}</td> 34 <td>{{ row.b__caption }}</td> 35 </tr> 36 {% endfor %} 37 </tbody> 38 <tbody> 39 {% for row in v3 %} 40 <tr hid="{{ row.0 }}" bid="{{ row.2 }}"> 41 <td>{{ row.1 }}</td> 42 <td>{{ row.3 }}</td> 43 </tr> 44 {% endfor %} 45 </tbody> 46 </table> 47 48 </body> 49 </html>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 from django.db import models 2 3 # Create your models here. 4 5 6 class Business(models.Model): 7 8 caption = models.CharField(max_length=32) 9 code = models.CharField(max_length=32, default="sa") # null=True 设置默认为空;default设置默认值为sa 10 11 12 class Host(models.Model): 13 nid = models.AutoField(primary_key=True) 14 hostname = models.CharField(max_length=32, db_index=True) 15 ip = models.GenericIPAddressField(protocol="ipv4", db_index=True) 16 port = models.IntegerField() 17 b = models.ForeignKey(to="Business", to_field="id", on_delete=models.PROTECT) # 如果省略to_field参数则自动去Business中关联主键
1 小结: 2 跨表操作双下换线和点的区别 3 1、通过对象跨表操作直接用点即可; 4 2、在models.Host.objects之后进行跨表时要用双下滑线,字符串的形式进行跨表操作 5 6 7 模板语言: 8 1、模板语言对于字典类型,前端取值只能通过点 . 来取值 9 2、模板语言对于元组类型,前端取值只能通过values_list中字符串顺序的索引来取值
双下划线的操作方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 # 获取个数 2 # 3 # models.Tb1.objects.filter(name='seven').count() 4 5 # 大于,小于 6 # 7 # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 8 # models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值 9 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 10 # models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值 11 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 12 13 # in 14 # 15 # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 16 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in 17 18 # isnull 19 # Entry.objects.filter(pub_date__isnull=True) 20 21 # contains 22 # 23 # models.Tb1.objects.filter(name__contains="ven") # 相当于SQL中的like 24 # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感, 有的数据库可能不支持 25 # models.Tb1.objects.exclude(name__icontains="ven") 26 27 # range 28 # 29 # models.Tb1.objects.filter(id__range=[1, 2]) # 范围,SQL中的bettwen and 30 31 # 其他类似 32 # 33 # startswith,istartswith, endswith, iendswith, 34 35 # order by 36 # 37 # models.Tb1.objects.filter(name='seven').order_by('id') # asc,升序,从小到大;可以有多个参数,优先按照第一个参数排序,如果第一个参数重复,按照第二个参数排序 38 # models.Tb1.objects.filter(name='seven').order_by('-id') # desc,降序 39 40 # group by 41 # 42 # from django.db.models import Count, Min, Max, Sum 43 # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # 谁在annotate前面就表示根据谁进行group by分组,这里转换为SQL就是根据id分组,c=Count('num'):计算每组的个数即:COUNT("app01_tb1"."num") AS "c",count可以换成其他聚合函数 44 # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id" 45 46 # limit 、offset 47 # 48 # models.Tb1.objects.all()[10:20] 49 50 # regex正则匹配,iregex 不区分大小写 51 # 52 # Entry.objects.get(title__regex=r'^(An?|The) +') 53 # Entry.objects.get(title__iregex=r'^(an?|the) +') 54 55 # date 56 # 57 # Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1)) # __date:根据指定日期查找 58 # Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) 59 60 # year 61 # 62 # Entry.objects.filter(pub_date__year=2005) # 根据年查找 63 # Entry.objects.filter(pub_date__year__gte=2005) 64 65 # month 66 # 67 # Entry.objects.filter(pub_date__month=12) 68 # Entry.objects.filter(pub_date__month__gte=6) 69 70 # day 71 # 72 # Entry.objects.filter(pub_date__day=3) 73 # Entry.objects.filter(pub_date__day__gte=3) 74 75 # week_day 76 # 77 # Entry.objects.filter(pub_date__week_day=2) 78 # Entry.objects.filter(pub_date__week_day__gte=2) 79 80 # hour 81 # 82 # Event.objects.filter(timestamp__hour=23) 83 # Event.objects.filter(time__hour=5) 84 # Event.objects.filter(timestamp__hour__gte=12) 85 86 # minute 87 # 88 # Event.objects.filter(timestamp__minute=29) 89 # Event.objects.filter(time__minute=46) 90 # Event.objects.filter(timestamp__minute__gte=29) 91 92 # second 93 # 94 # Event.objects.filter(timestamp__second=31) 95 # Event.objects.filter(time__second=2) 96 # Event.objects.filter(timestamp__second__gte=31)
创建多对多关系的方式:
1 方式一:自定义关系表 2 class Host(models.Model): 3 nid = models.AutoField(primary_key=True) 4 hostname = models.CharField(max_length=32,db_index=True) 5 ip = models.GenericIPAddressField(protocol="ipv4",db_index=True) 6 port = models.IntegerField() 7 b = models.ForeignKey(to="Business", to_field='id') 8 # 10 9 class Application(models.Model): 10 name = models.CharField(max_length=32) 11 # 2 12 13 class HostToApp(models.Model): 14 hobj = models.ForeignKey(to='Host',to_field='nid') 15 aobj = models.ForeignKey(to='Application',to_field='id') 16 17 # HostToApp.objects.create(hobj_id=1,aobj_id=2) 18 19 20 21 方式二:自动创建关系表,自动关联两个表的主键作为第三张表的两个字段 22 class Host(models.Model): 23 nid = models.AutoField(primary_key=True) 24 hostname = models.CharField(max_length=32,db_index=True) 25 ip = models.GenericIPAddressField(protocol="ipv4",db_index=True) 26 port = models.IntegerField() 27 b = models.ForeignKey(to="Business", to_field='id') 28 # 10 29 class Application(models.Model): 30 name = models.CharField(max_length=32) 31 r = models.ManyToManyField("Host") 32 33 34 缺点:无法直接对第三张表进行操作 35 36 obj = Application.objects.get(id=1) 37 obj.name 38 39 # 第三张表操作 40 obj.r.add(1) 41 obj.r.add(2) 42 obj.r.add(2,3,4) 43 obj.r.add(*[1,2,3,4]) 44 45 obj.r.remove(1) 46 obj.r.remove(2,4) 47 obj.r.remove(*[1,2,3]) 48 49 obj.r.clear() 50 51 obj.r.set([3,5,7]) # 相当于update,set之后数据库关系表中只保留(1,3)(1,5)(1,7)其它全部删除,不可以传递*[3,5,7] 52 53 # 所有相关的主机对象“列表” QuerySet 54 obj.r.all() # 所有相关的主机Host对象
多对多之第三张表的操作方法:add、set、remove、clear
1 一对多: 2 class UserType(models.Model): 3 name = models.CharField(max_length=32) 4 5 6 class User(models.Model): 7 username = models.CharField(max_length=32) 8 email = models.EmailField() 9 user_type = models.ForeignKey("UserType") 10 11 user_list = User.objects.all() # 12 for obj user_list: 13 obj.username,obj.email,obj.user_type_id,obj.user_type.name,obj.user_type.id # 基于对象的跨表操作用点 14 15 user = User.objects.get(id=1) # 获取到具体的单个对象(包含user.username、user.email、user.user_type.name) 16 17 18 User.objects.all().values("username","user_type__name",) # 获取指定字段的数据,用双下划线跨表 19 20 21 多对多: 22 class UserType(models.Model): 23 name = models.CharField(max_length=32) 24 25 26 class User(models.Model): 27 username = models.CharField(max_length=32) 28 email = models.EmailField() 29 user_type = models.ForeignKey("UserType") 30 m = models.ManyToMany('UserGroup') 31 32 33 class UserGroup(models.Model): 34 name = .... 35 36 37 obj = User.objects.get(id=1) 38 obj.m.add(2) # 在关系表中增加UserGroup、User两个表中主键对应关系即:1 -- 2 39 obj.m.add(2,3) # 增加 1 -- 3 和 1 -- 3 40 obj.m.add(*[1,2,3]) # 增加 1 -- 1 1 -- 2 1 -- 3 41 obj.m.add(*QuerySet) # 用queryset集合方式添加 42 obj.m.remove(2) 43 obj.m.clear() # 在关系表中清空id=1和它对应的关联 44 45 46 obj.m.set([1,2,3,4,5]) # 此处不能加* 47 48 49 obj.m.all() # 多个组,UserGroup对象 50 obj.m.filter(name='CTO')
多表关系以及参数:
1 ForeignKey(ForeignObject) # ForeignObject(RelatedField) 2 to, # 要进行关联的表名 3 to_field=None, # 要关联的表中的字段名称 4 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 5 - models.CASCADE,删除关联数据,与之关联也删除 6 - models.DO_NOTHING,删除关联数据,引发错误IntegrityError,数据库级别抛出的异常 7 - models.PROTECT,删除关联数据,引发错误ProtectedError,jango级别抛出的异常 8 - models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空) 9 - models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值) 10 - models.SET,删除关联数据, 11 a. 与之关联的值设置为指定值,设置:models.SET(值) 12 b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象) 13 14 def func(): 15 return 10 16 17 class MyModel(models.Model): 18 user = models.ForeignKey( 19 to="User", 20 to_field="id" 21 on_delete=models.SET(func),) 22 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() (related_name参数当表自关联,在反向查找时区分用) 23 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') 24 25 26 # 上面几个常用,下面参数在admin中使用 27 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: 28 # 如: 29 - limit_choices_to={'nid__gt': 5} 30 - limit_choices_to=lambda : {'nid__gt': 5} 31 32 from django.db.models import Q 33 - limit_choices_to=Q(nid__gt=10) 34 - limit_choices_to=Q(nid=8) | Q(nid__gt=10) 35 - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 36 db_constraint=True # 是否在数据库中创建外键约束 37 parent_link=False # 在Admin中是否显示关联数据 38 39 40 OneToOneField(ForeignKey) 41 to, # 要进行关联的表名 42 to_field=None # 要关联的表中的字段名称 43 on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为 44 45 ###### 对于一对一 ###### 46 # 1. 一对一其实就是 一对多 + 唯一索引 47 # 2.当两个类之间有继承关系时,默认会创建一个一对一字段 48 # 如下会在A表中额外增加一个c_ptr_id列且唯一: 49 class C(models.Model): 50 nid = models.AutoField(primary_key=True) 51 part = models.CharField(max_length=12) 52 53 class A(C): 54 id = models.AutoField(primary_key=True) 55 code = models.CharField(max_length=1) 56 57 ManyToManyField(RelatedField) 58 to, # 要进行关联的表名 59 related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all() 60 related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名') 61 limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件: 62 # 如: 63 - limit_choices_to={'nid__gt': 5} 64 - limit_choices_to=lambda : {'nid__gt': 5} 65 66 from django.db.models import Q 67 - limit_choices_to=Q(nid__gt=10) 68 - limit_choices_to=Q(nid=8) | Q(nid__gt=10) 69 - limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root') 70 symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段 71 # 做如下操作时,不同的symmetrical会有不同的可选字段 72 models.BB.objects.filter(...) 73 74 # 可选字段有:code, id, m1 75 class BB(models.Model): 76 77 code = models.CharField(max_length=12) 78 m1 = models.ManyToManyField('self',symmetrical=True) 79 80 # 可选字段有: bb, code, id, m1 81 class BB(models.Model): 82 83 code = models.CharField(max_length=12) 84 m1 = models.ManyToManyField('self',symmetrical=False) 85 86 through=None, # 自定义第三张表时,使用字段用于指定关系表 87 through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表 88 from django.db import models 89 90 class Person(models.Model): 91 name = models.CharField(max_length=50) 92 93 class Group(models.Model): 94 name = models.CharField(max_length=128) 95 members = models.ManyToManyField( 96 Person, 97 through='Membership', 98 through_fields=('group', 'person'), 99 ) 100 101 class Membership(models.Model): 102 group = models.ForeignKey(Group, on_delete=models.CASCADE) 103 person = models.ForeignKey(Person, on_delete=models.CASCADE) 104 inviter = models.ForeignKey( 105 Person, 106 on_delete=models.CASCADE, 107 related_name="membership_invites", 108 ) 109 invite_reason = models.CharField(max_length=64) 110 db_constraint=True, # 是否在数据库中创建外键约束 111 db_table=None, # 默认创建第三张表时,数据库中表的名称
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 QUERYSET方法详细(下面方法都支持链式查询,values和values_list等不支持下面的链式查询方法): 2 3 ################################################################## 4 # PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET # 5 ################################################################## 6 7 def all(self) 8 # 获取所有的数据对象 9 10 def filter(self, *args, **kwargs) 11 # 条件查询 12 # 条件可以是:参数,字典,Q 13 14 def exclude(self, *args, **kwargs) 15 # 条件查询 16 # 条件可以是:参数,字典,Q 17 18 def select_related(self, *fields) 19 # 主动进行联表查询的参数 20 # 性能相关:表之间进行join连表操作,一次性获取关联的数据。 21 model.tb.objects.all().select_related() 22 model.tb.objects.all().select_related('外键字段') 23 model.tb.objects.all().select_related('外键字段__外键字段') 24 25 26 def prefetch_related(self, *lookups) 27 性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。 28 # 获取所有用户表 29 # 获取用户类型表where id in (用户表中的查到的所有用户ID) 30 models.UserInfo.objects.prefetch_related('外键字段') 31 32 33 34 from django.db.models import Count, Case, When, IntegerField 35 Article.objects.annotate( 36 numviews=Count(Case( 37 When(readership__what_time__lt=treshold, then=1), 38 output_field=CharField(), 39 )) 40 ) 41 42 students = Student.objects.all().annotate(num_excused_absences=models.Sum( 43 models.Case( 44 models.When(absence__type='Excused', then=1), 45 default=0, 46 output_field=models.IntegerField() 47 ))) 48 49 ''' 50 jango中的SQL性能优化举例: 51 class UserType(models.Model): 52 name = models.CharField(max_length=32) 53 54 55 class User(models.Model): 56 57 user = models.CharField(max_length=32) 58 pwd = models.CharField(max_length=64) 59 ut = models.ForeignKey(to='UserType', to_field='id', on_delete=models.PROTECT) 60 61 t = models.ManyToManyField('self') 62 63 64 65 视图函数: 66 def test1(request): 67 68 users = models.User.objects.all() 69 for row in users: 70 print(row.user, row.pwd, row.ut_id) 71 print(row.ut.name) # 再发起一次SQL请求,如果有10个用户,每次循环执行一次跨表操作,整个循环将发起11次SQL请求 72 73 users = models.User.objects.all().values('user', 'pwd', 'ut__name') 74 for row in users: # 这种方式跨表只发起一次SQL请求,单反回的非queryset对象,而是字典 75 print(row.user, row.pwd, row.ut_id) 76 77 users = models.User.objects.all().select_related('ut') # 一次性把关联的ut表数据取过来再进行操作,其他的关联表都不取,不加参数默认取所有关联的表的数据 78 for row in users: 79 print(row.user, row.pwd, row.ut_id) 80 print(row.ut.name) # 如果有10个用户,整个循环将发起1次SQL请求 81 82 83 # prefetch_related 84 users = models.User.objects.filter(id__gt=30).prefetch_related('ut') # 一次取到所有关联表数据 85 # prefetch 将做两步操作: 86 # select * from users where id > 30 87 # jango获取上一条SQL所有的ut_id,假设ut_id=[1, 2] 88 # select * from user_type where id in [1,2] 89 90 for row in users: 91 print(row.user, row.pwd, row.ut_id) 92 print(row.ut.name) # 如果有10个用户,整个循环将发起1次SQL请求 93 94 ''' 95 96 def annotate(self, *args, **kwargs) 97 # 用于实现聚合group by查询 98 99 from django.db.models import Count, Avg, Max, Min, Sum 100 101 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')) 102 # SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id 103 104 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1) # filter写在annotate后面,对应SQL中自动生成having;写在annotate之前,则生成where语句 105 # SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 106 107 v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1) # distinct对Count的结果去重,一般很少用 108 # SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1 109 110 def distinct(self, *field_names) 111 # 用于distinct去重 112 models.UserInfo.objects.values('nid').distinct() 113 # select distinct nid from userinfo 114 115 注:只有在PostgreSQL中才能使用distinct进行去重 116 117 def order_by(self, *field_names) 118 # 用于排序 119 models.UserInfo.objects.all().order_by('-id','age') 120 121 def extra(self, select=None, where=None, params=None, select_params=None,tables=None, order_by=None) # 最后两个参数不重要 122 # 构造额外的查询条件或者映射,如:子查询 123 124 Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,)) 125 Entry.objects.extra(select={'new_id': "func(id)"}) # 支持MySQL函数 126 Entry.objects.extra(where=['headline=%s'], params=['Lennon']) 127 Entry.objects.extra(where=['headline=1 or nid > 1']) # SQL中的or 128 Entry.objects.extra(where=['headline=1','nid > 1']) # SQL中的and 129 Entry.objects.extra(where=['func(ctime)=1 or nid > 1']) # 支持MySQL函数 130 131 Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"]) 132 Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid']) 133 134 135 def reverse(self): 136 # 倒序 137 models.UserInfo.objects.all().order_by('-nid').reverse() # reverse必须要和order_by一起使用,all().reverse()这样写reverse没有作用 138 # 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序 139 140 141 # defer和only方法:直接从对象中取对应字段的数据并发到对象中,而不再是字典或元组类型了;only和defer取到的是queryset对象,后面可以接所有查询方法 142 # 注意:通过对象的方式(defer和only取数据库中字段后,用的时候只用取到的字段,使用其他字段时jango重发请求会重新去数据库查询效率很低) 143 def defer(self, *fields): 144 #映射中排除某列数据 145 models.UserInfo.objects.defer('username','id') 146 或 147 models.UserInfo.objects.filter(...).defer('username','id') 148 149 150 def only(self, *fields): 151 #仅取某个表中的数据 152 models.UserInfo.objects.only('username','id') 153 或 154 models.UserInfo.objects.filter(...).only('username','id') 155 156 157 def using(self, alias): 158 指定使用的数据库,参数为数据库别名(Django全局setting中的设置); 159 可以做手动的读写分离, 读:models.UserInfo.objects.using('db_alias1'), 160 写:models.UserInfo.objects.using('db_alias2');数据库主从同步即可 161 162 163 164 ################################################## 165 # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # 166 ################################################## 167 168 def raw(self, raw_query, params=None, translations=None, using=None): 169 # 执行原生SQL 170 models.UserInfo.objects.raw('select * from userinfo') 171 172 # 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名 173 models.UserInfo.objects.raw('select id as nid from 其他表') 174 175 """ 176 假设:tb表有id、name、pwd三个字段 177 tb2有nid、username、email三个字段 178 obj = models.tb.objects.raw('select nid as id, username as name, email as pwd from tb2') # 这样可以把tb2的数据放到tb对象对应的字段中 179 """ 180 # 为原生SQL设置参数 181 models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,]) 182 183 # 将获取的到列名转换为指定列名 184 name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'} 185 Person.objects.raw('SELECT * FROM some_other_table', translations=name_map) 186 187 # 指定数据库 188 models.UserInfo.objects.raw('select * from userinfo', using="default") 189 190 ################### 原生SQL ################### 191 from django.db import connection, connections 192 cursor = connection.cursor() # cursor = connections['default'].cursor() 193 cursor.execute("""SELECT * from auth_user where id = %s""", [1]) 194 row = cursor.fetchone() # fetchall()/fetchmany(..) 195 196 197 def values(self, *fields): 198 # 获取每行数据为字典格式 199 200 def values_list(self, *fields, **kwargs): 201 # 获取每行数据为元祖 202 203 def dates(self, field_name, kind, order='ASC'): 204 # 根据时间进行某一部分进行去重查找并截取指定内容 205 # kind只能是:"year"(年), "month"(年-月), "day"(年-月-日) 206 # order只能是:"ASC" "DESC" 207 # 并获取转换后的时间 208 - year : 年-01-01 209 - month: 年-月-01 210 - day : 年-月-日 211 212 models.DatePlus.objects.dates('ctime','day','DESC') 213 214 def datetimes(self, field_name, kind, order='ASC', tzinfo=None): 215 # 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间 216 # kind只能是 "year", "month", "day", "hour", "minute", "second" 217 # order只能是:"ASC" "DESC" 218 # tzinfo时区对象 219 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC) 220 models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai')) 221 222 """ 223 pip3 install pytz 224 import pytz 225 pytz.all_timezones 226 pytz.timezone(‘Asia/Shanghai’) 227 """ 228 229 def none(self): 230 # 空QuerySet对象 231 232 233 #################################### 234 # METHODS THAT DO DATABASE QUERIES # 235 #################################### 236 237 def aggregate(self, *args, **kwargs): 238 # 聚合函数(整张表聚合),获取字典类型聚合结果 239 from django.db.models import Count, Avg, Max, Min, Sum 240 result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid')) # distinct=True 先去重,再聚合 241 ===> {'k': 3, 'n': 4} 242 243 def count(self): 244 # 获取个数 245 246 def get(self, *args, **kwargs): 247 # 获取单个对象 248 249 def create(self, **kwargs): 250 # 创建对象 251 252 def bulk_create(self, objs, batch_size=None): 253 # 批量插入 254 # batch_size表示一次插入的个数 255 objs = [ 256 models.DDD(name='r11'), 257 models.DDD(name='r22') 258 ] 259 models.DDD.objects.bulk_create(objs, 10) # 10 表示一次插入10条 260 261 def get_or_create(self, defaults=None, **kwargs): 262 # 如果存在,则获取,否则,创建并获取 263 # defaults 指定创建时,其他字段的值 264 obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2}) # created值为True、False,是否创建 265 266 def update_or_create(self, defaults=None, **kwargs): 267 # 如果存在,则更新,否则,创建 268 # defaults 指定创建时或更新时的其他字段 269 obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1}) 270 271 def first(self): 272 # 获取第一个 273 274 def last(self): 275 # 获取最后一个 276 277 def in_bulk(self, id_list=None): 278 # 根据主键ID进行查找 279 id_list = [11,21,31] 280 models.DDD.objects.in_bulk(id_list) 281 282 def delete(self): 283 # 删除 284 285 def update(self, **kwargs): 286 # 更新 287 288 def exists(self): 289 # 是否有结果
总结实践示例:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 class Book(models.Model): 2 name = models.CharField(max_length=20) 3 price = models.IntegerField() 4 pub_date = models.DateField() 5 author = models.CharField(max_length=32, null=False) 6 7 def __str__(self): 8 return self.name 9 10 11 class Author(models.Model): 12 name=models.CharField(max_length=32)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 CREATE TABLE `app03_book` ( 2 `id` int(11) NOT NULL AUTO_INCREMENT, 3 `name` varchar(20) NOT NULL, 4 `price` int(11) NOT NULL, 5 `pub_date` date NOT NULL, 6 `author` varchar(32) NOT NULL, 7 PRIMARY KEY (`id`) 8 ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8; 9 10 INSERT INTO `app03_book` VALUES ('1', 'python基础', '111', '2017-12-12', 'yuan'); 11 INSERT INTO `app03_book` VALUES ('2', 'Go', '12', '2017-03-09', 'alex'); 12 INSERT INTO `app03_book` VALUES ('3', 'php', '45', '2017-03-09', 'yuan'); 13 INSERT INTO `app03_book` VALUES ('4', '骆驼祥子', '67', '2017-03-10', '老舍'); 14 INSERT INTO `app03_book` VALUES ('5', '悲惨世界', '56', '2017-03-16', '雨果'); 15 INSERT INTO `app03_book` VALUES ('6', 'Go', '12', '2017-03-09', 'alex');
1 # 查询相关API: 2 3 # <1>filter(**kwargs): 如果有数据则返回一个QuerySet对象集合,可用for循环迭代;它包含了与所给筛选条件相匹配的对象;如果没有数据返回一个空的QuerySet,不会抛出异常 4 5 # <2>all(): 返回一个QuerySet对象集合,可用for循环迭代;查询所有结果 6 7 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个对象不可迭代,如果符合筛选条件的对象超过一个或0个有都会抛出错误。 8 9 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- 10 11 # <4>values(*field): 返回一个ValueQuerySet集合——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列(查询结果) 12 # <QuerySet [{'price': 111, 'name': 'python基础'}, {'price': 45, 'name': 'php'}]> 13 14 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的QuerySet对象集合 15 #<QuerySet [<Book: Go>, <Book: 骆驼祥子>, <Book: 悲惨世界>]> 16 #a = models.Book.objects.exclude(author='yuan') 17 #for item in a: 18 #print(item.name, item.price, item.pub_date, item.author) 19 #Go 12 2017-03-09 alex 20 #骆驼祥子 67 2017-03-10 老舍 21 #悲惨世界 56 2017-03-16 雨果 22 23 # <6>order_by(*field): 对查询结果排序 24 25 # <7>reverse(): 对查询结果反向排序 26 27 # <8>distinct(): 从返回结果中剔除重复纪录,在values后使用 28 # models.Book.objects.all().values('name').distinct() 29 # <QuerySet [{'name': 'python基础'}, {'name': 'Go'}, {'name': 'php'}, {'name': '骆驼祥子'}, {'name': '悲惨世界'}]> 30 31 32 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 33 #<QuerySet [('python基础', 111), ('php', 45)]> 34 35 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 36 37 # <11>first(): 返回第一条记录 38 39 # <12>last(): 返回最后一条记录 40 41 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。 42 43 44 # <14> 双下划线: 返回QuerySet对象集合 45 a = models.Book.objects.filter(price__gt=50)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 models: 2 3 class Publish(models.Model): 4 name = models.CharField(max_length=32) 5 city = models.CharField(max_length=32) 6 7 8 class Book(models.Model): 9 name = models.CharField(max_length=20) 10 price = models.IntegerField() 11 pub_date = models.DateField() 12 publish = models.ForeignKey("Publish", on_delete=models.CASCADE) 13 # publish1 = models.ForeignKey(Publish, on_delete=models.CASCADE) # Publish不加引号,Publish要放在Book类上面 14 15 def __str__(self): 16 return self.name 17 18 19 20 # 一对多 21 # 添加数据 22 # 方式一: 23 models.Book.objects.create(name='Linux', price=77, pub_date='2017-12-12', publish_id=2) # 外键直接赋值 24 25 # 方式二(通过对象方式): 26 publish_obj = models.Publish.objects.filter(name='人民出版社')[0] 27 models.Book.objects.create(name='Linux', price=77, pub_date='2017-12-12', publish=publish_obj) 28 # 把publish_obj对象赋值给Book类中的publish,内部会把"人民出版社"的id赋值给外键关联的publish_id 29 30 # 查询: 31 # 需求:查询python书籍对应的出版社名字和城市都拿到(正向查询) 32 # 方式一(双下划线正向跨表): 33 r = models.Book.objects.filter(name='python').values('publish__name', 'publish__city') 34 35 # 补充(双下划线反向跨表查询,这里不是对象的反向跨表,不用_set方式) 36 a = models.Publish.objects.filter(book__name="python").values("name") 37 print(a) # <QuerySet [{'name': '人民出版社'}]> 38 39 # 方式二(通过对象方式): 40 book_obj = models.Book.objects.get(name='python') 41 name = models.Book.objects.get(name='python').publish # 一对多的查询,models.Book.objects.get(name='python').publish 结果是一定是一个对象 42 city = models.Book.objects.get(name='python').publish.city 43 print(name, city) # 输出 Publish object (3) 南京 44 45 46 47 # 需求:查询人民出版社出了哪些书(反向查询) 48 # 方式一(双下划线跨表): 49 obj = models.Book.objects.filter(publish__name='人民出版社').values("name", "price") 50 print(obj) # <QuerySet [{'price': 89, 'name': 'python'}, {'price': 77, 'name': 'JAVA'}]> 51 # 方式二(通过对象方式): 52 pub_obj = models.Publish.objects.filter(name="人民出版社")[0] 53 ret = models.Book.objects.filter(publish=pub_obj).values("name", "price") 54 55 # 方式三(反向查找,通过表名_set): 56 a = models.Publish.objects.filter(name="人民出版社")[0].book_set.values("name", "price") 57 a = models.Publish.objects.get(name="人民出版社").book_set.values("name", "price") # 注意对别get和filter查询写法上的区别 58 print(a) # <QuerySet [{'price': 89, 'name': 'python'}, {'price': 77, 'name': 'JAVA'}]> 59 # 小结:三种方式内部都是利用ForeignKey来实现的 60 61 62 63 # 需求:查询在北京的出版社出版的所有书 64 # 错误思路:a = models.Publish.objects.filter(city="北京").values("id") 65 # b = models.Book.objects.filter(publish_id=a).values("name") 66 # 正确思路: 67 # 方式一(正向查询,双下滑线跨表): 68 a = models.Book.objects.filter(publish__city="北京").values("name") 69 print(a) # <QuerySet [{'name': 'python'}, {'name': 'Linux'}, {'name': 'GO'}]> 70 71 # 方式二(反向查询,双下滑线跨表) 72 b = models.Publish.objects.filter(city="北京").values("book__name") 73 print(b) # <QuerySet [{'book__name': 'python'}, {'book__name': 'Linux'}, {'book__name': 'GO'}]> 74 75 # 方式二(反向查询,_set跨表) 76 c = models.Publish.objects.filter(city="北京") 77 d = [] 78 for i in c: 79 d.append(i.book_set.values("name")) 80 print(d) # [<QuerySet [{'name': 'python'}]>, <QuerySet [{'name': 'Linux'}, {'name': 'GO'}]>]
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 class Book(models.Model): 2 name = models.CharField(max_length=20) 3 price = models.IntegerField() 4 pub_date = models.DateField() 5 publish = models.ForeignKey("Publish", on_delete=models.CASCADE) 6 # publish1 = models.ForeignKey(Publish, on_delete=models.CASCADE) # Publish不加引号,Publish要放在Book类上面 7 authors = models.ManyToManyField('Author') 8 9 def __str__(self): 10 return self.name 11 12 13 class Author(models.Model): 14 name = models.CharField(max_length=32) 15 age = models.IntegerField(default=20) 16 17 def __str__(self): 18 return self.name 19 20 # 21 # class Book_Author(models.Model): 22 # book = models.ForeignKey('Book', on_delete=models.CASCADE) 23 # author = models.ForeignKey('Author', on_delete=models.CASCADE)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 # 多对多: 2 # 查询: 3 # 需求:查询书名是Linux的作者名字 4 a = models.Book.objects.get(name='Linux').authors.all() 5 print(a) # <QuerySet [<Author: alex>, <Author: yuan>]> , all()结果models类中需要加上__str__方法 6 7 8 # 需求:找出yuan作者写过的书: 9 # 错误写法:a = models.Author.objects.filter(name='yuan').book_set.values("name") # filter反向查询_set跨表时不能用在filter后面,filter结果是多个QuerySet对象 10 # 方式一:_set反向跨表查询 11 a = models.Author.objects.get(name='yuan').book_set.values("name") 12 print(a) 13 # 方式二:双下划线正向跨表查询 14 a = models.Book.objects.filter(authors__name='yuan').values("name") 15 # print(a) 16 17 # 添加: 18 # 需求:添加一个作者yuan到书名为GO的书 19 autho_obj = models.Author.objects.get(name="yuan") 20 models.Book.objects.get(name="GO").authors.add(autho_obj) 21 22 # 需求:多对多操作更改书名为GO的书的作者为所有人: 23 b = models.Author.objects.all() 24 a = models.Book.objects.get(name="GO").authors.add(*b) 25 print(a) 26 27 # 删除 28 # 需求:删除GO书的所有作者: 29 author_obj = models.Author.objects.all() 30 models.Book.objects.get(name="GO").authors.remove(*author_obj) 31 print(author_obj) 32 33 # 需求:删除GO书的yuan作者 34 方式1:models.Book.objects.get(name="GO").authors.remove(models.Author.objects.get(name="yuan")) # 通过对象绑定来建立关系 35 方式二:models.Book.objects.get(name="GO").authors.remove(2) 36 37 38 39 # 自行创建第三张表 40 # 添加数据:models.Book_Author.objects.create(book_id=2, author_id=3) 41 # 需求:查询书名为python的作者,通过对象方式,反向跨表_set 42 book_obj = models.Book.objects.get(name='python') 43 a = book_obj.book_author_set.values("author__name") 44 45 # 需求:查找作者alex出过的书籍和价格 46 # 方式一: 47 a = models.Author.objects.get(name='alex').book_author_set.values("book__name", "book__price") 48 print(a) # <QuerySet [{'book__price': 77, 'book__name': 'Linux运维'}, {'book__price': 77, 'book__name': 'GO'}]> 49 # 方式二:通过自建第三张表查询(不推荐) 50 a = models.Book.objects.filter(book_author__author__name="alex").values('name', 'price') 51 print(a) # <QuerySet [{'price': 77, 'name': 'Linux运维'}, {'price': 77, 'name': 'GO'}]> 52 53 # 方式三:利用ManyToManyField字段跨表查询 54 a = models.Book.objects.filter(authors__name='alex').values('name', 'price') 55 print(a)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 # 分组聚合查询 2 models: 3 class Publish(models.Model): 4 name = models.CharField(max_length=32) 5 city = models.CharField(max_length=32) 6 7 def __str__(self): 8 return self.name 9 10 11 class Book(models.Model): 12 name = models.CharField(max_length=20) 13 price = models.IntegerField() 14 pub_date = models.DateField() 15 publish = models.ForeignKey("Publish", on_delete=models.CASCADE) 16 # publish1 = models.ForeignKey(Publish, on_delete=models.CASCADE) # Publish不加引号,Publish要放在Book类上面 17 authors = models.ManyToManyField('Author') 18 19 def __str__(self): 20 return self.name 21 22 23 class Author(models.Model): 24 name = models.CharField(max_length=32) 25 age = models.IntegerField(default=20) 26 27 def __str__(self): 28 return self.name 29 30 #聚合查询 31 # 求出所有书籍的平均价格 32 a = models.Book.objects.all().aggregate(Avg('price')) 33 b = models.Book.objects.all().aggregate(Sum('price')) 34 # 求出alex写的书籍的总价格 35 c = models.Book.objects.filter(authors__name='alex').aggregate(alex_money=Sum('price')) # alex_money设置查询结果字段的名字 36 print(c) 37 38 #分组查询: 39 # 求出每一个作者出的全部书的价格和 40 ret = models.Book.objects.values("authors__name").annotate(Sum('price')) 41 values:按照authors__name进行分组;annotate用来分组,Sum对分组后每一个组进行求和 42 print(ret) 43 44 # 查询各个出版社最便宜的书籍价格 45 a = models.Publish.objects.values("name").annotate(Min('book__price')) 46 print(a)
调试orm打印SQL:
1 LOGGING = { 2 'version': 1, 3 'disable_existing_loggers': False, 4 'handlers': { 5 'console':{ 6 'level':'DEBUG', 7 'class':'logging.StreamHandler', 8 }, 9 }, 10 'loggers': { 11 'django.db.backends': { 12 'handlers': ['console'], 13 'propagate': True, 14 'level':'DEBUG', 15 }, 16 } 17 }
函数导入:
from django.db.models import Count, Avg, Max, Min, Sum
1 F查询: 2 一般构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢? 3 4 Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。 5 6 7 # 1、查看评论数大于阅读数的书 8 from django.db.models import F,Q 9 print(models.Book.objects.filter(commentNum__gt=F("readNum"))) 10 11 # 2、修改操作也可以使用F函数,比如将id大于1的所有的书的价格涨价100元 12 print(models.Book.objects.filter(nid__gt=1).update(price=F("price")+100)) 13 14 15 3、Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。 16 # 查询评论数大于收藏数2倍的书籍 17 models.Book.objects.filter(commnetNum__lt=F('keepNum')*2) 18 22 23 Q查询: 24 filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。 25 26 # 1、查询id大于1并且评论数大于100的书 27 print(models.Book.objects.filter(nid__gt=1,commentNum__gt=100)) 28 print(models.Book.objects.filter(nid__gt=1).filter(commentNum__gt=100)) 29 print(models.Book.objects.filter(Q(nid__gt=1)&Q(commentNum__gt=100))) 30 31 32 # 2、查询评论数大于100或者阅读数小于200的书 33 print(models.Book.objects.filter(Q(commentNum__gt=100)|Q(readNum__lt=200))) 34 Q 对象可以使用& 和| 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。 35 36 37 # 3、查询年份等于2017年或者价格大于200的书 38 print(models.Book.objects.filter(Q(publishDdata__year=2017)|Q(price__gt=200))) 39 40 41 # 4、查询年份不是2017年且价格大于200的书 42 print(models.Book.objects.filter(~Q(publishDdata__year=2017)&Q(price__gt=200))) 43 44 注意: 45 46 查询函数可以混合使用Q 对象和关键字参数。所有提供给查询函数的参数(关键字参数或Q 对象)都将"AND”在一起。但是,如果出现Q 对象,它必须位于所有关键字参数的前面。例如: 47 bookList=models.Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), 48 title__icontains="python" 49 )
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 https://www.cnblogs.com/huchong/p/8027962.html 2 3 # 首先还是需要导入模块: 4 from django.db.models import Q 5 6 7 # 传入条件进行查询: 8 q1 = Q() 9 q1.connector = 'OR' 10 q1.children.append(('id', 1)) 11 q1.children.append(('id', 2)) 12 q1.children.append(('id', 3)) 13 14 models.Tb1.objects.filter(q1) 15 16 17 # 合并条件进行查询: 18 19 con = Q() 20 21 q1 = Q() 22 q1.connector = 'OR' 23 q1.children.append(('id', 1)) 24 q1.children.append(('id', 2)) 25 q1.children.append(('id', 3)) 26 27 q2 = Q() 28 q2.connector = 'OR' 29 q2.children.append(('status', '在线')) 30 31 con.add(q1, 'AND') 32 con.add(q2, 'AND') 33 34 models.Tb1.objects.filter(con)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 # F 使用查询条件的值,专门取对象中某列值的操作 2 3 # from django.db.models import F 4 # models.Tb1.objects.update(num=F('num')+1) 5 6 7 # Q 构建搜索条件 8 from django.db.models import Q 9 10 #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 11 q1=models.Book.objects.filter(Q(title__startswith='P')).all() 12 print(q1)#[<Book: Python>, <Book: Perl>] 13 14 # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。 15 Q(title__startswith='P') | Q(title__startswith='J') 16 17 # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合 18 Q(title__startswith='P') | ~Q(pub_date__year=2005) 19 20 # 4、应用范围: 21 22 # Each lookup function that takes keyword-arguments (e.g. filter(), 23 # exclude(), get()) can also be passed one or more Q objects as 24 # positional (not-named) arguments. If you provide multiple Q object 25 # arguments to a lookup function, the arguments will be “AND”ed 26 # together. For example: 27 28 Book.objects.get( 29 Q(title__startswith='P'), 30 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) 31 ) 32 33 #sql: 34 # SELECT * from polls WHERE question LIKE 'P%' 35 # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') 36 37 # import datetime 38 # e=datetime.date(2005,5,6) #2005-05-06 39 40 # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。 41 # 正确: 42 Book.objects.get( 43 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), 44 title__startswith='P') 45 # 错误: 46 Book.objects.get( 47 question__startswith='P', 48 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))) 49 50 51 52 53 练习: 54 # F 使用查询条件的值,专门取对象中某列值的操作 55 from django.db.models import F,Q 56 # 所有书籍价格+10 57 # models.Book.objects.all().update(price=F('price')+10) 58 59 # Q 构建搜索条件 60 61 a = models.Book.objects.filter(Q(price=97) | Q(name='GO')) # 或 62 b = models.Book.objects.filter(~Q(name='GO')) # 非 63 c = models.Book.objects.filter(Q(name__contains="G")) 64 65 # Q查询结合关键字查询,Q查询要放在前 66 d = models.Book.objects.filter(Q(name='GO'), price=97) 67 print(d)
小结:跨表的两种方式:1.对象方式直接跨表(利用对象中封装的信息) 2.非对象方式(字符串、字段方式跨表)
1 对象方式: 2 正向跨表: . 3 反向跨表: _set 4 5 字符串/models字段field 等形式进行跨表操作,正向/反向跨表: __ 6 7 注意:jango在写跨表时,关键在于ForeignKey和ManyToManyField所在字段名字,用这个名字后面加双下划线写跨表操作
一句话总结:对象跨表-> 用点和_set, 非对象跨表->用双下滑线
反向查找时,根据对象来取值用 表名_set ,其他条件(如跨表查询)用 “表名__字段”即可,前提必须有ForeignKey存在
QuerySet特点:
<1> 可迭代的
<2> 可切片
1 #objs=models.Book.objects.all()#[obj1,obj2,ob3...] 2 3 #QuerySet: 可迭代 4 5 # for obj in objs:#每一obj就是一个行对象 6 # print("obj:",obj) 7 # QuerySet: 可切片 8 9 # print(objs[1]) 10 # print(objs[1:4]) 11 # print(objs[::-1])
惰性机制:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 <1>Django的queryset是惰性的 2 3 Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得 4 到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave") 5 上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 6 这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 7 8 <2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 9 为了验证这些,需要在settings里加入 LOGGING(验证方式) 10 obj=models.Book.objects.filter(id=3) 11 # for i in obj: 12 # print(i) 13 14 # if obj: 15 # print("ok") 16 17 <3>queryset是具有cache的 18 当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行 19 (evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset, 20 你不需要重复运行通用的查询。 21 obj=models.Book.objects.filter(id=3) 22 23 # for i in obj: 24 # print(i) 25 ## models.Book.objects.filter(id=3).update(title="GO") 26 ## obj_new=models.Book.objects.filter(id=3) 27 # for i in obj: 28 # print(i) #LOGGING只会打印一次 29 30 <4> 31 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些 32 数据!为了避免这个,可以用exists()方法来检查是否有数据: 33 34 obj = Book.objects.filter(id=4) 35 # exists()的检查可以避免数据放入queryset的cache。 36 if obj.exists(): 37 print("hello world!") 38 39 <5>当queryset非常巨大时,cache会成为问题 40 41 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 42 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 43 来获取数据,处理完数据就将其丢弃。 44 objs = Book.objects.all().iterator() 45 # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 46 for obj in objs: 47 print(obj.name) 48 #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 49 for obj in objs: 50 print(obj.name) 51 52 #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使 53 #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 54 55 总结: 56 查询结果非常大时用iterator 57 查询结果数据量不大但使用频率高,用cache 58 59 queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 60 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 61 会造成额外的数据库查询。
对于一对一补充说明:
#先看一个小案例: #_*_coding:utf-8_*_ from django.db import models # Create your models here. class Colors(models.Model): colors=models.CharField(max_length=10) #蓝色 def __str__(self): return self.colors class Ball(models.Model): color=models.OneToOneField("Colors") #与颜色表为一对一,颜色表为母表 description=models.CharField(max_length=10) #描述 def __str__(self): return self.description class Clothes(models.Model): color=models.ForeignKey("Colors") #与颜色表为外键,颜色表为母表 description=models.CharField(max_length=10) #描述 def __str__(self): return self.description class Child(models.Model): name=models.CharField(max_length=10) #姓名 favor=models.ManyToManyField('Colors') #与颜色表为多对多 # 先来区分一下什么是一对一、多对多: 一对一:子表从母表中选出一条数据一一对应,母表中选出来一条就少一条,子表不可以再选择母表中已被选择的那条数据 一对多:子表从母表中选出一条数据一一对应,但母表的这条数据还可以被其他子表数据选择 共同点是在admin中添加数据的话,都会出现一个select选框,但只能单选,因为不论一对一还是一对多,自己都是“一” 多对多总结: 比如有多个孩子,和多种颜色、 每个孩子可以喜欢多种颜色,一种颜色可以被多个孩子喜欢,对于双向均是可以有多个选择 # 应用场景 一对一:一般用于某张表的补充,比如用户基本信息是一张表,但并非每一个用户都需要有登录的权限,不需要记录用户名和密码,此时,合理的做法就是新建一张记录登录信息的表,与用户信息进行一对一的关联,可以方便的从子表查询母表信息或反向查询 外键:有很多的应用场景,比如每个员工归属于一个部门,那么就可以让员工表的部门字段与部门表进行一对多关联,可以查询到一个员工归属于哪个部门,也可反向查出某一部门有哪些员工 多对多:如很多公司,一台服务器可能会有多种用途,归属于多个产品线当中,那么服务器与产品线之间就可以做成对多对,多对多在A表添加manytomany字段或者从B表添加,效果一致
1 #### 对于一对一 ###### 2 1. 一对一其实就是 一对多 + 唯一索引 3 2.当两个类之间有继承关系时,默认会创建一个一对一字段 4 如下会在A表中额外增加一个c_ptr_id列且唯一: 5 class C(models.Model): 6 nid = models.AutoField(primary_key=True) 7 part = models.CharField(max_length=12) 8 9 class A(C): 10 id = models.AutoField(primary_key=True) 11 code = models.CharField(max_length=1) 12 13 14 # 由两种方式实现表一对一的关系 15 models.OneToOneField() 16 models.ForeignKey(str_table, unique=True)
中介模型:(多对多关系中自建第三张关系表就是中介模型)
自己创建的第三张表就属于是中介模型。一般就Django会给我们自动创建第三张表,Django自己创建的只是有关系字段,无法增加其他的字段了, 如果根据需求添加其他字段,这就需要我们自己去创建第三张表。第三张表就可以在里面添加其他你需要的字段。
1 # 使用中介模型管理多对多关系模型 2 http://www.ziawang.com/article/189/ 3 在我们使用ManyToMany来设置多对多字段的时候,如果我们创建数据表,会在数据库中自动为我们创建一个第三张表,存放多对多中两个关联主键的关系。但是Django为我们创建的这张第三方表有时候并不能满足我们新的需求,比如我们想要在这张第三方表中添加其他字段或者自定义关联的字段。 4 在ManyToMany字段中,设置through属性和through_fields属性指定第三方表及关联的字段 5 注意through_fields的顺序 6 在下面例子中,要创建Article与Tag的多对多关系,如果要将多对多的tags键在Article中创建,那么就要将through_fields中与Article建立外键的字段放在第一个 7 8 class Article(models.Model): 9 """ 文章表 10 普通字段: id, title, summary, create_time, update_time 11 关联字段: tags(多对多) 12 """ 13 id = models.BigAutoField(primary_key=True) 14 title = models.CharField(max_length=64, verbose_name='文章标题') 15 summary = models.CharField(max_length=512, verbose_name='文章概要') 16 create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True) 17 update_time = models.DateTimeField(verbose_name='修改时间', auto_now_add=True) 18 19 tags = models.ManyToManyField(verbose_name='文章标签', to='Tag', through='Tag2Article', 20 through_fields=('article', 'tag')) # 这里through_fileds的顺序不能错,在数据库中Article表不会有tags这个字段! 21 22 type_choices = [ 23 (1, '技术'), 24 (2, '音乐'), 25 (3, '生活'), 26 (4, '经济'), 27 (5, '时政'), 28 (6, '其他'), 29 ] 30 type_id = models.IntegerField(choices=type_choices, verbose_name='文章类型') 31 32 def __str__(self): 33 return self.title 34 35 class Meta: 36 verbose_name_plural = '文章表' 37 38 39 class Tag(models.Model): 40 id = models.BigAutoField(primary_key=True) 41 title = models.CharField(max_length=16) 42 43 blog = models.ForeignKey(to='Blog', to_field='id', verbose_name='所属博客') 44 45 class Meta: 46 verbose_name_plural = '标签表' 47 48 49 class Tag2Article(models.Model): # Tag2Article是中介模型 50 id = models.BigAutoField(primary_key=True) 51 tag = models.ForeignKey(to='Article', to_field='id') 52 article = models.ForeignKey(to='Tag', to_field='id') 53 54 class Meta: 55 unique_together = (('tag', 'article'),) 56 verbose_name_plural = '标签文章表' 57 58 59 60 对于中介模型,不能通过article.tags.add()方法添加多对多关系 61 62 因为add方法只能用来绑定关联字段,不能对多对多关系表额外的其他字段进行绑定。要想创建多对多关系,只能直接通过创建Tag2Article实例对象的方法创建和其他操作 63 除了add方法外,set,remove,create方法都不能使用。但是clear方法可以继续使用 64 65 66 67 68 69 70 中介模型例2: 71 72 class Person(models.Model): 73 name = models.CharField(max_length=50) 74 75 76 class Group(models.Model): 77 name = models.CharField(max_length=128) 78 members = models.ManyToManyField( 79 Person, 80 through='Membership', 81 through_fields=('group', 'person'), #第一个字段要和所在表名一致(即:group 一定要在前面) 82 ) 83 84 85 class Membership(models.Model): 86 group = models.ForeignKey(Group, on_delete=models.CASCADE) 87 person = models.ForeignKey(Person, on_delete=models.CASCADE) 88 inviter = models.ForeignKey( 89 Person, 90 on_delete=models.CASCADE, 91 related_name="membership_invites", 92 ) 93 invite_reason = models.CharField(max_length=64) 94 95
1 应用场景: 2 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择) 3 例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。 4 多对多:在某表中创建一行数据是,有一个可以多选的下拉框 5 例如:创建用户信息,需要为用户指定多个爱好 6 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了 7 例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据
性能优化之:select_related和prefetch_related
测试数据:
1 class Province(models.Model): 2 name = models.CharField(max_length=10) 3 4 def __unicode__(self): 5 return self.name 6 7 8 class City(models.Model): 9 name = models.CharField(max_length=5) 10 province = models.ForeignKey(Province, on_delete=models.CASCADE) 11 12 def __unicode__(self): 13 return self.name 14 15 16 class Person(models.Model): 17 firstname = models.CharField(max_length=10) 18 lastname = models.CharField(max_length=10) 19 visitation = models.ManyToManyField(City, related_name="visitor") 20 hometown = models.ForeignKey(City, related_name="birth", on_delete=models.CASCADE) 21 living = models.ForeignKey(City, related_name="citizen", on_delete=models.CASCADE) 22 23 def __unicode__(self): 24 return self.firstname + self.lastname 25 26 注1:创建的app名为“app03”, person表:firstname: 张 lastname:三 hometown_id:3 living_id:1 27 注2:为了简化起见,`app03_province` 表中只有2条数据:湖北省和广东省,`app03_city`表中只有三条数据:武汉市、十堰市和广州市
1 对于: 2 citys = City.objects.all() 3 for c in citys: 4 print(c.province) 5 # 执行的SQL查询: 6 SELECT `app03_city`.`id`, `app03_city`.`name`, `app03_city`.`province_id` FROM `app03_city 7 SELECT `app03_province`.`id`, `app03_province`.`name` FROM `app03_province` WHERE `app03_province`.`id` = 1 8 SELECT `app03_province`.`id`, `app03_province`.`name` FROM `app03_province` WHERE `app03_province`.`id` = 1 9 SELECT `app03_province`.`id`, `app03_province`.`name` FROM `app03_province` WHERE `app03_province`.`id` = 2 10 11 12 # 如果使用select_related()函数: 13 citys_select_related = City.objects.select_related().all() 14 for i in citys_select_related: 15 print(i.province) 16 17 # 执行的SQL查询: 18 SELECT 19 `app03_city`.`id`, 20 `app03_city`.`name`, 21 `app03_city`.`province_id`, 22 `app03_province`.`id`, 23 `app03_province`.`name` 24 FROM 25 `app03_city` 26 INNER JOIN `app03_province` ON ( 27 `app03_city`.`province_id` = `app03_province`.`id` 28 ) 29 30 # 就只有一次SQL查询,显然大大减少了SQL查询的次数
1 *fields 参数:指定查询外键,未指定的外键则不会被查询添加到结果中。 2 获取张三所在省份: 3 obj = Person.objects.select_related('living__province').get(firstname=u"张", lastname=u"三") 4 print(obj.living.province) 5 # 执行的SQL查询: 6 SELECT 7 `app03_person`.`id`, 8 `app03_person`.`firstname`, 9 `app03_person`.`lastname`, 10 `app03_person`.`hometown_id`, 11 `app03_person`.`living_id`, 12 `app03_city`.`id`, 13 `app03_city`.`name`, 14 `app03_city`.`province_id`, 15 `app03_province`.`id`, 16 `app03_province`.`name` 17 FROM 18 `app03_person` 19 INNER JOIN `app03_city` ON ( 20 `app03_person`.`living_id` = `app03_city`.`id` 21 ) 22 INNER JOIN `app03_province` ON ( 23 `app03_city`.`province_id` = `app03_province`.`id` 24 ) 25 WHERE 26 ( 27 `app03_person`.`firstname` = '张' 28 AND `app03_person`.`lastname` = '三' 29 ); 30 31 # 不使用select_related共发起3次查询请求: 32 obj = Person.objects.get(firstname=u"张", lastname=u"三") 33 print(obj.living.province) 34 35 SELECT 36 `app03_person`.`id`, 37 `app03_person`.`firstname`, 38 `app03_person`.`lastname`, 39 `app03_person`.`hometown_id`, 40 `app03_person`.`living_id` 41 FROM 42 `app03_person` 43 WHERE 44 ( 45 `app03_person`.`firstname` = '张' 46 AND `app03_person`.`lastname` = '三' 47 ); 48 49 SELECT 50 `app03_city`.`id`, 51 `app03_city`.`name`, 52 `app03_city`.`province_id` 53 FROM 54 `app03_city` 55 WHERE 56 `app03_city`.`id` = 1; 57 58 59 SELECT 60 `app03_province`.`id`, 61 `app03_province`.`name` 62 FROM 63 `app03_province` 64 WHERE 65 `app03_province`.`id` = 1; 66 67 #fields 参数从Django 1.7开始,select_related()函数的作用方式改变了。在本例中,如果要同时获得张三的故乡和现居地的省份,在1.7以前你只能这样做 68 obj3 = Person.objects.select_related('living__province', 'hometown__province').get(firstname=u"张", lastname=u"三") 69 print(obj3.living.province.name) 70 print(obj3.hometown.province.name) 71 72 # 1.7及以上版本,你可以像和queryset的其他函数一样进行链式操作,也可像上面一样操作 73 obj4 = Person.objects.select_related('living__province').select_related('hometown__province').get(firstname=u"张", lastname=u"三") 74 print(obj4.living.province.name) 75 print(obj4.hometown.province.name) 76 77 78 79 depth 参数(在1.7版本及以后已经移除) 80 81 无参数 82 select_related() 也可以不加参数,这样表示要求Django尽可能深的select_related。例如:zhangs = Person.objects.select_related().get(firstname=u"张",lastname=u"三")。但要注意两点: 83 84 1.Django本身内置一个上限,对于特别复杂的表关系,Django可能在你不知道的某处跳出递归,从而与你想的做法不一样。具体限制是怎么工作的我表示不清楚。 85 2.Django并不知道你实际要用的字段有哪些,所以会把所有的字段都抓进来,从而会造成不必要的浪费而影响性能。
1 小结 2 1.select_related主要针一对一和多对一关系进行优化。 3 2.select_related使用SQL的JOIN语句(INNER JOIN)进行优化,通过减少SQL查询的次数来进行优化、提高性能。 4 3.可以通过可变长参数指定需要select_related的字段名。也可以通过使用双下划线“__”连接字段名来实现指定的递归查询。没有指定的字段不会缓存,如果要访问的话Django会再次进行SQL查询。 5 4.也接受无参数的调用,Django会尽可能深的递归查询所有的字段。但注意有Django递归的限制和性能的浪费。 6 5.Django >= 1.7,链式调用的select_related相当于使用可变长参数。Django < 1.7,链式调用会导致前边的select_related失效,只保留最后一个。
1 select_related()的效率要高于prefetch_related()。 2 因此,最好在能用select_related()的地方尽量使用它,也就是说,对于ForeignKey字段,避免使用prefetch_related() 3 4 5 prefetch_related()和select_related()的设计目的很相似,都是为了减少SQL查询的数量, 6 但是实现的方式不一样。 7 select_related是通过JOIN语句,在SQL查询内解决问题。 8 但是对于多对多关系,使用SQL语句解决就显得有些不太明智,因为JOIN得到的表将会很长,会导致SQL语句运行时间的增加和内存占用的增加。 9 若有n个对象,每个对象的多对多字段对应Mi条,就会生成Σ(n)Mi 行的结果表。 10 11 prefetch_related()的解决方法是,分别查询每个表,然后用Python处理他们之间的关系; 12 prefetch使用的是 IN 语句实现的,主要针一对多和多对多关系进行优化 13 14 15 16 如果我们想要获得所有家乡是湖北的人,最无脑的做法是先获得湖北省,再获得湖北的所有城市,最后获得故乡是这个城市的人。就像这样: 17 >>> hb = Province.objects.get(name__iexact=u"湖北省") 18 >>> people = [] 19 >>> for city in hb.city_set.all(): 20 ... people.extend(city.birth.all()) 21 ... 这样做会导致1+(湖北省城市数)次SQL查询。 22 prefetch_related() 或许是一个好的解决方法,让我们来看看。 23 >>> hb = Province.objects.prefetch_related("city_set__birth").objects.get(name__iexact=u"湖北省") 24 >>> people = [] 25 >>> for city in hb.city_set.all(): 26 ... people.extend(city.birth.all()) 27 ... 28 29 因为是一个深度为2的prefetch,所以会导致3次SQL查询: 30 SELECT `QSOptimize_province`.`id`, `QSOptimize_province`.`name` 31 FROM `QSOptimize_province` 32 WHERE `QSOptimize_province`.`name` LIKE '湖北省' ; 33 34 SELECT `QSOptimize_city`.`id`, `QSOptimize_city`.`name`, `QSOptimize_city`.`province_id` 35 FROM `QSOptimize_city` 36 WHERE `QSOptimize_city`.`province_id` IN (1); 37 38 SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`, 39 `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id` 40 FROM `QSOptimize_person` 41 WHERE `QSOptimize_person`.`hometown_id` IN (1, 3); 42 43 44 用select_related倒过来查询: 45 people = list(Person.objects.select_related("hometown__province").filter(hometown__province__name__iexact=u"湖北省")) 46 47 SELECT `QSOptimize_person`.`id`, `QSOptimize_person`.`firstname`, `QSOptimize_person`.`lastname`, 48 `QSOptimize_person`.`hometown_id`, `QSOptimize_person`.`living_id`, `QSOptimize_city`.`id`, 49 `QSOptimize_city`.`name`, `QSOptimize_city`.`province_id`, `QSOptimize_province`.`id`, `QSOptimize_province`.`name` 50 FROM `QSOptimize_person` 51 INNER JOIN `QSOptimize_city` ON (`QSOptimize_person`.`hometown_id` = `QSOptimize_city`.`id`) 52 INNER JOIN `QSOptimize_province` ON (`QSOptimize_city`.`province_id` = `QSOptimize_province`.`id`) 53 WHERE `QSOptimize_province`.`name` LIKE '湖北省'; 54 不仅SQL查询的数量减少了,python程序上也精简了.select_related()的效率要高于prefetch_related()。因此,最好在能用select_related()的地方尽量使用它,也就是说,对于ForeignKey字段,避免使用prefetch_related()。 55 56 57 58 59 小结 60 prefetch_related主要针一对多和多对多关系进行优化。 61 prefetch_related通过分别获取各个表的内容,然后用Python处理他们之间的关系来进行优化。 62 可以通过可变长参数指定需要select_related的字段名。指定方式和特征与select_related是相同的。 63 可以通过传入None来清空之前的prefetch_related。 64 https://blog.csdn.net/cugbabybear/article/details/38460877
总结:
对SQL优化时,多对多优先考虑prefetch_related;一对多考虑select_related