Python菜鸟之路:Django 路由、模板、Model(ORM)
Django路由系统
Django的路由系统让Django可以根据URI进行匹配,进而发送至特定的函数去处理用户请求。有点类似nginx的location功能。
Django的路由关系分为三种:普通关系、动态关系、分组分发。这三种关系都记录在urls.py中。
路由-普通关系
普通关系表示一个url对应一个函数,例如:
urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^login/', views.login), url(r'^detail/', views.detail), ]
路由-动态关系
动态关系表示通过正则的方式去实现动态匹配。例如:
urlpatterns = [ url(r'^detail/(\d+)/', views.detail), # 动态路由之:单参数的路由 url(r'^detail2/(\d+)/', views.detail2), # 动态路由之:多参数的路由 url(r'^detail3/(?P<p1>\d+)/(?P<x2>\d+)/', views.detail3), # 动态路由之:命名参数,两个参数分别被命名为p1和x2 url(r'^index/(\d+)/', views.index), # 分页显示 ]
动态路由,也是动态关系的一种,动态路由其实通过映射也可以实现,需要自己来实现
主要代码实现 1、url.py 配置url映射 2、activator.py 根据url中获取的参数,通过反射调用指定 app/view.py 中的函数 如: http://127.0.0.1:9000/app01/index/ ==》 执行app01/view.py文件中的index函数 http://127.0.0.1:9000/app02/index/ ==》 执行app02/view.py文件中的index函数 http://127.0.0.1:9000/app01/login/ ==》 执行app01/view.py文件中的login函数 # 具体代码: # 总入口urls.py from django.conf.urls import include, url from django.contrib import admin from DynamicRouter.activator import process urlpatterns = [ # Examples: # url(r'^$', 'DynamicRouter.views.home', name='home'), # url(r'^blog/', include('blog.urls')), url(r'^admin/', include(admin.site.urls)), ('^(?P<app>(\w+))/(?P<function>(\w+))/(?P<page>(\d+))/(?P<id>(\d+))/$',process), ('^(?P<app>(\w+))/(?P<function>(\w+))/(?P<id>(\d+))/$',process), ('^(?P<app>(\w+))/(?P<function>(\w+))/$',process), ('^(?P<app>(\w+))/$',process,{'function':'index'}), ] # activator.py代码 from django.shortcuts import render_to_response,HttpResponse,redirect def process(request,**kwargs): '''接收所有匹配url的请求,根据请求url中的参数,通过反射动态指定view中的方法''' app = kwargs.get('app',None) function = kwargs.get('function',None) try: appObj = __import__("%s.views" %app) viewObj = getattr(appObj, 'views') funcObj = getattr(viewObj, function) #执行view.py中的函数,并获取其返回值 result = funcObj(request,kwargs) except (ImportError,AttributeError) as e: #导入失败时,自定义404错误 return HttpResponse('404 Not Found') except Exception as e: #代码执行异常时,自动跳转到指定页面 return redirect('/app01/index/') return result
路由-分组分发
什么是分组分发?一个Django项目里,通常会有很多的app,比如一些bbs论坛为例,他的项目里就有前端部分,供普通用户使用,这类url通常写作http://hostname/common/form.php 。 也有后端admin管理员部分,用来进行用户管理、帖子管理等功能。这类url通常有个特别,一般是http://hostname/admin/XXX。可以明显看到,这个论坛项目中,就有2个分组:common和admin。Django中也有这样的功能。
Django分组写法:
步骤1:导入include函数
from django.conf.urls import include
步骤2:创建url映射
urlpatterns = [ ... url(r'^common/', include("frontweb.urls")), # 多app项目的分组匹配 url(r'^admin/', include("manager.urls")), # 多app项目的分组匹配 ]
上面代码中,定义了2个app,一个是frontweb,一个是manager。
步骤3:在app中创建对应的url
# manager\urls.py中 代码 from manager import views urlpatterns = [ url(r'^assets', views.assets), url(r'^userinfo', views.userinfo), ] # frontweb\urls.py中 代码 from frontweb import views urlpatterns = [ url(r'^index/(\d+)/', views.index), url(r'^detail/(\d+)/', views.detail), url(r'^userinfo', views.userinfo), ]
模板
模版的创建过程,对于模版,其实就是读取模版(其中嵌套着模版标签),然后将 Model 中获取的数据插入到模版中,最后将信息返回给用户。
模板的语法
1. 传普通字符串
# python中 return render(request, 'index.html', {'k1': 'v1'}) # html中引用k1的值 {{ k1 }}
2. 传入列表
# python中 return render(request, 'index.html', {'k2': [11,22,33,44]}) # html中引用k1的值 {{ k2 }} <!-- 取整个列表 --> {{ k2.0 }} <!-- 取索引0的位置元素的值 --> {{ k2.1 }} <!-- 取索引1的位置元素的值 --> {{ k2.2 }} <!-- 取索引2的位置元素的值 -->
3. 传入字典
# python中 return render(request, 'index.html', {'k3': {'name': 'Bob', 'nid': 18}}) # html中引用k1的值 {{ k3 }} <!-- 取整个字典 --> {{ k3.name }} <!-- 取键值为name的位置元素的值 --> {{ k3.nid}} <!-- 取键值为age的位置元素的值 -->
4. 模板中的for循环,还是以上边的传入列表k2为例
{% for item in k2 %} <p> {{ item }}, {{ forloop.counter }}, <!--循环至第几个元素,从索引+1 开始 --> {{ forloop.counter0 }}, <!--循环至第几个元素,从索引0 开始--> {{ forloop.first }}, <!--判断是否是第一个元素 --> {{ forloop.last }}, <!-- 判断是否是最后一个元素--> {{ forloop.revcounter }} <!-- 倒叙显示是第几个元素 --> </p> {% endfor %}
5. 模板中的if循环,以上边传入的k1为例
{% if k1 == 'v1' %} <h1>V1</h1> {% elif k1 == 'v2' %} <h1>V2</h1> {% else %} <h1>777777</h1> {% endif %}
6. 模板中的继承语法
母板:{% block title %}{% endblock %} 子板:{% extends "base.html" %} {% block title %}{% endblock %}
7. 模板内置的一些方法(可以理解为函数,而模板中的变量就是传入的值)
{{ item.event_start|date:"Y-m-d H:i:s"}} {{ bio|truncatewords:"30" }} {{ my_list|first|upper }} {{ name|lower }}
8. 自定义模板方法
自定义模板方法,有两种方式,一种是filter,另外一种是simple_tag
8.1 filter
步骤1:创建指定文件夹,名称必须为templatetags,文件夹放在对应的app目录下。
步骤2:创建任意的py文件,比如xx.py
from django import template from django.utils.safestring import mark_safe from django.template.base import Variable, Node, TemplateSyntaxError register = template.Library() #要点1: template.Library的返回值的变量,必须是register,不能是其他 #要点2:函数必须加装饰器register.filter,这样f1就是模板语言中的一个函数 @register.filter def f1(value): # 接收单个参数 return value + 'f111' @register.filter def f2(value, value2): # 接收两个参数 return value + value2
步骤3:使用函数,在html模板的头部,执行{% load xx %},xx表示创建的py文件的文件名
{% load xx %}
步骤4:使用函数
# html中代码 {{ k1|f1 }} {# filter自定义方法之:1个参数 #}
# 输出 v1f111 # 其中v1是k1本身的值,f111是函数后加的字符串
# html中代码 {{ k1|f2:'F2:XXX' }} <br /> {# filter自定义方法之:2个参数 #}
{% if k1|f1 == 'v1f111' %}
<h1>filter可以用作if语句的条件</h1>
{% endif %}
最终输出: v1F2:XXX 其中v1是k1的值, F2:XXX是前端传入的变量
Ps:从上边可以看出,filter方法构建的函数,默认不支持传入超过2个以上的参数,因此,如果需要传入多个参数,可以将"F2:XXX"写成 "F2:XXX,arg3,arg4",在python代码中,用split来拆分字符串,来获取参数。
限制:传参个数有限制,多个参数需要自行split进行切割
支持:可以作为模板中if语句的if条件
步骤5:注册app。最后一步非常重要,否则django会不知道去哪个路径下查找xx.py。
# 在settings.py中注册app INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', # 新注册的app目录名称 ]
8.2 用simple_tag 来自定义模板方法
创建simple_tag的步骤基本一致,唯独不同的有2个地方
不同点1: 函数的装饰器需要写为register.simple_tag
@register.simple_tag def f3(value): return value + 'f3333' @register.simple_tag def f4(value, arg1, arg2): return value + arg1 + arg2
不同点2:前端html传参的时候
# html中的代码 {% f3 'simple_tag' %}<br /> {# simple_tag自定义方法之:1个参数 #} {% f4 'simple_tag' 'arg2' 'arg3' %}<br /> {# simple_tag自定义方法之:多个参数 #} # 输出 simple_tagf3333 simple_tagarg2arg3
Ps:用simple_tag来创建函数,不支持作为if语句中的条件
模板的渲染过程
看一张图就明白,
模板继承
母版页面
母版页面通常用来定义网页的基本框架结构,同时空出部分内容块来供子页面来自定义式的填充
# 母版的代码 {% block css %}{% endblock %} # 空出名为css的代码块部分,供子版来填充 {% block body %}{% endblock %} # 同上,只是名称不同而已 {% block js %}{% endblock %} #同上,只是名称不同而已
完整代码示例如下图:
子页面
{% extends 'layout.html' %} # 表示继承layoout.html母版页面 {% block css %} #自定义样式 <style> ... </style> {% endblock %} {% block body %} # 填充html内容 <table> {% for item in assets_list %} <tr> <td>{{ item.hostname }}</td> <td>{{ item.port }}</td> </tr> {% endfor %} </table> {% endblock %} {% block js %} # 自定义js <script> ... </script> {% endblock %}
重复利用的小插件
一般情况下,在页面中,会有一些元素是相似度很高的,或许只有装饰文件不同而已。比如京东首页中,家电数码、个性美妆、手机通讯等部分。通常的做法是把这些相似的部分,做成类似小插件一样的html代码,在使用的时候引用即可。
{% include "xx.html" %}
Django中的Model
在Django的model中,提供了两类操作,一种是创建数据库表(CURD),一种是操作数据库的数据
Django数据库表间的三种关系
Django的数据库表中,有三种关系:一对一,一对多,多对多。
假设有两张表userinfo和user_type 。
class userinfo(models.Model): nid = models.AutoField(primary_key=True) name = models.CharField(max_length=30, verbose_name='用户名',editable=TabError) email = models.EmailField(db_index=True) memo = models.TextField() # 普通用户,超级用户 class UserType(models.Model): name = models.CharField(max_length=32) def __str__(self): return self.name
一对一
即 user_type = OneToOneField("另外一张表")
这种情况下,每次创建一条userinfo数据,就需要userType表中一条数据与之对应。即,usertype表中的数据,只能被对应一次。
一对多
即 user_type = models.ForeignKey("UserType")
这是很常见的一种对应关系。比如一个usertype有两种用户,超级用户和普通用户,那么在这两种用户组中,可以分别存在很多人。
多对多
即 user_type = models.ManyToManyField("UserType")。
比如,一个user可以属于多个usergroup,一个usergroup可以包含多个user,这就是典型的多对多关系。
创建数据库表
步骤1:在app的models.py中写类,来创建表的每个字段
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正则表达式,在1.10的django版本中,可能会被替换掉,统一使用models.GenericIPAddressField) 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
步骤2:执行python manager.py makemigrations && python manager.py migrate初始化数据库结构
操作数据库表-基本操作
基本数据访问
运行python manage.py shell并使用Django提供的高级Python API
# 导入student模型类,通过这个类我们可以与student数据表进行交互 >>> from app01.models import UserType # 创建一个student类的实例并设置了字段name的值 >>> data_1 = UserType(name='外围人员') # 调用该对象的save()方法,将对象保存到数据库中,Django会在后台执行一条INSERT语句 >>> s1.save() # 再插入一条数据 >>> data_2 = UserType(name='核心人员') >>> data_2 .save() # 使用UserType.objects属性从数据库取出UserType表的记录集,这个属性有许多方法,UserType.objects.all()方法获取数据库中UserType类的所有对象,实际上Django执行了一条SQL SELECT语句 >>> usertype_list = UserType.objects.all() >>> usertype_list Out[6]: <QuerySet [<UserType: 普通用户>, <UserType: 超级用户>, <UserType: 外围人员>]>
在代码中进行数据访问基本操作
增
方法1: models.Tb1.objects.create(c1='xx', c2='oo') 增加一条数据,可以接受字典类型数据 **kwargs 方法2: obj = models.Tb1(c1='xx', c2='oo') obj.save()
删
models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据
改
方法1: models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs 方法2: obj = models.Tb1.objects.get(id=1) obj.c1 = '111' obj.save() # 修改单条数据
查
models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议) models.Tb1.objects.all() # 获取全部 models.Tb1.objects.filter(name='seven') # 获取指定条件的数据 obj = model.UserInfo.objects.filter(name='alex').values('id','email') #sql: select id,email from userinfo where name = 'alex' # 结果是个字典:[{'id':1 , 'email':'xxq@qq.com'}, {'id':2 , 'email':'qqqq@qq.com'},...] obj = model.UserInfo.objects.filter(name='alex').value_list('id','email') #sql: select id,email from userinfo where name = 'alex' #结果是个元祖 [('1', 'xxq@qq.com'), ('2', 'qqq@qq.com'), (),....]
说明
执行models.Tb1.objects.all()等查询的结果,是一个queryset对象,而queryset是Django中定义的一个类
操作数据库单表-进阶操作
# 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于---gt lt语句 # # 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---like语句 # # models.Tb1.objects.filter(name__contains="ven") # contains就是like # models.Tb1.objects.filter(name__icontains="ven") # icontains 表示对大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range ---between and语句 # # 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')) # 这里的value表示要根据id 进行groupby, 获取num列的个数 # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
操作数据库连表-进阶操作
双下划线的主要功能:1)大于小于等操作,参照上面的单表进阶操作 2)跨表操作
数据库结构说明:
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
操作数据库联表-双下划线跨表查询
两种表的联表查询案例
#表结构 class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) user_type = models.ForignKey('UserType') # 创建UserInfo 1、UserInfo.objects.create(user='alex',pwd='123',user_type=UserType.objects.get(id=2)) # 这种方法不推荐,实际查询了2次 2、 UserInfo.objects.create(user='alex',pwd='123',user_type_id=2) #推荐这种方法!!!这种是进阶用法 # 普通查询: UserInfo.objects.filter(user='alex') 1、查询所有用户类型等于 普通用户 的所有用户名和密码(普通查询方法) uid = UserType.objects.get(caption='普通用户').id # uid是一个对象 UserInfo.objects.filter(user_type_id=uid) # 进阶跨表查询方法及取值方法 querset = UserInfo.objects.filter(user_type__caption='普通用户') querset = UserInfo.objects.filter(user_type__id__gt=2) 【UserInfo对象,UserInfo对象,UserInfo对象,】 row = querset[0] row.user row.pwd row.user_type.id row.user_type.caption #在进行models.object.xxx查询时,使用: objects __ #获取到某一样的对象后使用: row.外键字段.外键表的字段 # 使用values和value_list跨表查询方法及取值 querset = UserInfo.objects.filter(user_type__caption='普通用户').values('user','user_type__caption') 【{'user': 'alex','user_type__caption': '普通用户'},{'user': 'eric','user_type__caption': '普通用户'},】 结果是一个字典对象形成的列表 # 取出单值 row = querset[0] row['user'] row['user_type__caption']
三张表跨表操作 class Somthing(models.Model): name = models.CharField(max_length=32) class UserType(models.Model): catption = models.CharField(max_length=32) s = models.ForignKey('Somthing') # 超级管理员,普通用户,游客,黑河 class UserInfo(models.Model): user = models.CharField(max_length=32) pwd = models.CharField(max_length=32) user_type = models.ForignKey('UserType') # 通过Userinfo表,查询Something表中name是xx UserInfo.objects.filter(user_type__s__name='xx') # 多个下滑连通过外键__ 则可以
Model中的多对多操作补充
Django中创建多对多关系有两种方式:
方式1:手动创建第三张表,与其他两张表建立外键关系
class B2G(models.Model): b_id = models.ForeignKey('Boy') g_id = models.ForeignKey('Girl') class Boy(models.Model): username = models.CharField(max_length=16) def __str__(self): return str(self.id) class Girl(models.Model): name = models.CharField(max_length=16) def __str__(self): return str(self.id)
方式2:利用models.ManyToManyField来让django自动创建第三张表
class Boy(models.Model): username = models.CharField(max_length=16) def __str__(self): return str(self.id) class Girl(models.Model): name = models.CharField(max_length=16) b = models.ManyToManyField('Boy') def __str__(self): return str(self.id)
多对多基本操作
建立两张表的关系:即向第三张表中增加数据-获取两个对象,通过add方法建立关系
########### 增加数据 ########### # 获取一个女孩对象 g1 = models.Girl.objects.get(id=1) # 获取一个男孩对象 b1 = models.Boy.objects.get(id=1) # 利用多对多字段“b”将男孩和女孩建立关系--一对一建立关系 g1.b.add(models.Boy.objects.get(id=1)) g1.b.add(1) # 也可以传入数字或者 数字列表*【1,2,3】 # 添加和女孩1和 男孩1,2,3,4关联--一对多建立关系 g1 = models.Girl.objects.get(id=1) g1.b.add(*[1,2,3,4]) # 将多个boy与女孩1建立关联 # boys = models.Boy.objects.all() # g1.b.add(*boys)
查询数据
# 获取一个女孩对象 g1 = models.Girl.objects.get(id=1) # 获取和当前女孩有关系的所有男孩 boy_list = g1.b.all() print(boy_list)
删除数据
# 删除第三张表中和女孩1关联的所有关联信息 g1 = models.Girl.objects.get(id=1) g1.b.clear() # 删除女孩1和男孩2,3的关联 g1 = models.Girl.objects.get(id=1) g1.b.remove(*[2,3])
特别补充——反向添加:由于manytomany的存在,在Boy表中,还有一个隐含字段girl_set,他的作用与Gril中的b字段是一样的功能。
b1 = models.Boy.objects.get(id=1) b1.girl_set.add(1) b1.girl_set.add(models.Girl.objects.all()) b1.girl_set.add(*[1,2,3,4])
多对多跨表操作
首先要明白一点,通过第三张表进行跨表操作,是通过left outer join的形式进行查询的,on的条件就是A表的id与B表的id相等。
# 两张表关联查询 models.Girl.objects.all().values('id','name', 'b') # 查询 第三张表b的 girl.id==b.id的 b.boy_id列 SQL: SELECT "app01_girl"."id", "app01_girl"."name", "app01_girl_b"."boy_id" FROM "app01_girl" LEFT OUTER JOIN "app01_girl_b" ON ("app01_girl"."id" = "app01_girl_b"."girl_id") # 三张表关联查询-跨表查询 models.Girl.objects.all().values('id','name', 'b__username') SQL: SELECT "app01_girl"."id", "app01_girl"."name", "app01_boy"."username" FROM "app01_girl" LEFT OUTER JOIN "app01_girl_b" ON ("app01_girl"."id" = "app01_girl_b"."girl_id") LEFT OUTER JOIN "app01_boy" ON ("app01_girl_ b"."boy_id" = "app01_boy"."id")
r = models.Boy.objects.exclude(girl__name=None).values('id','username', 'girl__name') print(r) print(r.query) #SELECT "app01_boy"."id", "app01_boy"."username", "app01_girl"."name" FROM "app01_boy" LEFT OUTER JOIN "app01_girl_b" ON ("app01_boy"."id" = "app01_girl_b"."boy_id") LEFT OUTER JOIN "app01_girl" ON ("app01_girl_b". #"girl_id" = "app01_girl"."id") WHERE NOT ("app01_boy"."id" IN (SELECT U0."id" AS Col1 FROM "app01_boy" U0 LEFT OUTER JOIN "app01_girl_b" U1 ON (U0."id" = U1."boy_id") LEFT OUTER JOIN "app01_girl" U2 ON (U1."girl_i #d" = U2."id") WHERE U2."name" IS NULL))
原生SQL操作-更新
在Django的SQL ORM框架中,对于更新操作的支持并不好,因此需要借助原生的sql语句来完成更新操作
from django.db import connection cursor = connection.cursor() cursor.execute("""SELECT * from tb where name = %s""", ['Lennon']) row = cursor.fetchone()