Django 的 ORM 有多种关系:一对一,多对一,多对多。

各自定义的方式为 :
       一对一: OneToOneField
       多对一: ForeignKey
       多对多: ManyToManyField
上边的描述太过数据而缺乏人性化,我们来更人性化一些:
       多个属于一个,即 belong to :  ForeignKey,多个属于一个
       一个有一个,即 has one: OneToOneField
       一个有很多个,即 has many:  lots of A belong to B 与 B has many A,在建立 ForeignKey 时,另一个表会自动建立对应的关系
       一个既有很多个,又属于很多个,即 has many and belong to : ManyToManyField,同样只能在一个model类中说明,关联表会自动建立。

操作数据表总的来说就是:

一对一和一对多
1、查找数据使用 __ 连接
2、获取数据时使用 . 连接

前提条件-正向和反向的定义:

正向:基于存在ForeignKey或ManyToManyField 字段的表查找为正向

反向:基于不存在ForeignKey或ManyToManyField表查找为反向

#反向查询
_set
注意:xx_set中的xx为小写的表名

例子如下:

#-*- coding:utf-8 -*-
from __future__ import unicode_literals from django.db import models # Create your models here. from django.db import models class UserType(models.Model):
caption = models.CharField(max_length=32) class UserInfo(models.Model):
user_type = models.ForeignKey(UserType)# user_type对象中封装id,caption
username = models.CharField(max_length=32)
age = models.IntegerField() class MyUser(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=64) def __unicode__(self):
return self.username class News(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=32)
def __unicode__(self):
return self.title class Favor(models.Model):
user_obj = models.ForeignKey(MyUser)
new_obj = models.ForeignKey(News) def __unicode__(self):
return "%s -> %s" %(self.user_obj.username, self.new_obj.title) # ########################### 多对多
class Host(models.Model):
hostname = models.CharField(max_length=32)
port = models.IntegerField() class HostAdmin(models.Model):
username = models.CharField(max_length=32)
email = models.CharField(max_length=32)
host = models.ManyToManyField(Host) #=========================== 方式二,自定义关联表 class Host1(models.Model):
hostname = models.CharField(max_length=32)
port = models.IntegerField() class HostAdmin1(models.Model):
username = models.CharField(max_length=32)
email = models.CharField(max_length=32)
host = models.ManyToManyField(Host1, through='HostRelation') class HostRelation(models.Model):
c1 = models.ForeignKey(Host1)
c2 = models.ForeignKey(HostAdmin1)

models.py

#-*- coding:utf-8 -*-
from django.shortcuts import render,HttpResponse # Create your views here. from app01 import models def user_type(request):
# dic = {'caption':'COO'}
#
# models.UserType.objects.create(**dic) return HttpResponse("OK") def user_info(request):
#添加
# dic = {"username":'xx',"age":88,'user_type_id':1}
# models.UserInfo.objects.create(**dic)
# result = models.UserInfo.objects.all() # for item in result:
# print item.username.item.age,item.user_type.caption #models.UserType.objects #反向查找
user_type_obj = models.UserType.objects.get(userinfo__username='alex')
print user_type_obj.caption
print user_type_obj.userinfo_set.all().count() return HttpResponse("OK") 2 def zan(request):
#女神实例
#new_list = models.News.objects.all() #大力赞的
new_list = models.News.objects.filter(favor__user_obj__username='dali') for item in new_list:
# item =>一条新闻
print '==========>'
print item.title
print item.content
print item.favor_set.all().count() return HttpResponse("OK") def host(request):
# models.HostAdmin.objects.create(username='dali',email='11@11.com')
# models.HostAdmin.objects.create(username='alex',email='22@11.com')
# models.HostAdmin.objects.create(username='root',email='33@11.com') #正向添加
"""
admin_obj = models.HostAdmin.objects.get(username='dali')
host_list = models.Host.objects.filter(id__lt=3)
admin_obj.host.add(*host_list)
"""
#反向添加
"""
host_obj = models.Host.objects.get(id=3)
admin_list = models.HostAdmin.objects.filter(id__gt=1)
host_obj.hostadmin_set.add(*admin_list)
""" """
#关联表
#一 方式增加
models.HostRelation.objects.create(
c1 = models.Host1.objects.get(id=1),
c2 = models.HostAdmin1.objects.get(id=2) ) #二 方式增加
models.HostRelation.objects.create(
c1_id = 2,
c2_id = 1,
)
"""
################################################
"""
#第一种 方式查询(区分正反向)
#正向查
admin_obj = models.HostAdmin.objects.get(id=1)
admin_obj.host.all() #反向查
host_obj = models.Host.objects.get(id=1)
print host_obj.hostadmin_set.all() """ #第二种 方式查询
#relation_list = models.HostRelation.objects.all()
relation_list = models.HostRelation.objects.filter(c2__username='dali')
for item in relation_list:
print item.c1.hostname
print item.c2.username return HttpResponse("OK")

views.py

一对多

增加
    #一 以对象的方式增加
    #二 以_id方式增加

查询:

#正向查(双下划线__

#反向查 (  _set)

多对多

增加

#正向添加
    admin_obj = models.HostAdmin.objects.get(username='dali')
    host_list = models.Host.objects.filter(id__lt=3)
    admin_obj.host.add(*host_list)

#反向添加
    host_obj = models.Host.objects.get(id=3)
    admin_list = models.HostAdmin.objects.filter(id__gt=1)
    host_obj.hostadmin_set.add(*admin_list)

查询

#正向查
    admin_obj = models.HostAdmin.objects.get(id=1)
    admin_obj.host.all()

#反向查
    host_obj = models.Host.objects.get(id=1)
    print host_obj.hostadmin_set.all()

---------------------------------------------------------------------------------------------

关联关系字段 (Relationship fields)

例如,一本书由一家出版社出版,一家出版社可以出版很多书。一本书由多个作者合写,一个作者可以写很多书。

1
2
3
4
5
6
7
8
class Author(models.Model):
    name=models.CharField(max_length=20)
class Publisher(models.Model):
    name=models.CharField(max_length=20)
class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey(Publisher)
    authors=models.ManyToManyField(Author)

1.关联尚未定义的Model

如果你要与某个尚未定义的 model 建立关联 ,就使用 model 的名称,而不是使用 model 对象本身。

例子中,如果Publisher与Author在Book后面定义,需要写成下面的形式:

1
2
3
4
class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey('Publisher')
    authors=models.ManyToManyField('Author')

2.Model关联自身

Model可以与自身做多对一关系

1
2
3
class People(models.Model):
    name=models.CharField(max_length=20)
    leader=models.ForeignKey('self',blank=True,null=True)

Model也可以与自身做多对多关系

1
2
class Person(models.Model):
    friends = models.ManyToManyField("self")

默认情况下,这种关联关系是对称的,如果Person1是Person2的朋友,那么Person2也是Person1的朋友

1
2
3
4
5
6
7
p1=Person()
p1.save()
p2=Person()
p2.save()
p3=Person()
p3.save()
p1.friends.add(p2,p3)

上述情况下,要查找p3的朋友,不用p3.person_set.all(),而直接用p3.friends.all()就可以了

如果想取消这种对称关系,将symmetrical设为False

1
2
class Person2(models.Model):
    friends=(models.ManyToManyField("self",symmetrical=False)

这样查询p3的朋友,就需要p3.person_set.all()了

3.反向名称related_name

反向名称,用来从被关联字段指向关联字段。

注意,在你定义 抽象 model (abstract models) 时,你必须显式指定反向名称; 只有在你这么做了之后, 某些特别语法 (some special syntax) 才能正常使用。

#当关联同一个模型的字段大于一个时,要使用related_name参数来指定表名

1
2
3
4
class Book(models.Model):
    name=models.CharField(max_length=20)
    pub=models.ForeignKey(Publisher,related_name='pub')
    authors=models.ManyToManyField(Author,related_name='author')

这样用Publisher或者Author反向查询Book时可以用related_name了:publisher1.pub.all()或者author1.author.all()。

如果不想设置反向关系,设置related_name为'+'或者以'+'结束。

1
user = models.ForeignKey(User, related_name='+')

如果有多个ManyToManyField指向同一个Model,这样反向查询FOO_set的时候就无法弄清是哪个ManyToManyField字段了,可以禁止反向关系:

1
2
users = models.ManyToManyField(User, related_name='u+')
referents = models.ManyToManyField(User, related_name='ref+')

4.数据库表现 (Database Representation)

多对一:Django 使用ForeignKey字段名称+ "_id" 做为数据库中的列名称。在上面的例子中,BOOK model 对应的数据表中会有 一个 publisher_id 列。

你可以通过显式地指定 db_column 来改变该字段的列名称,不过,除非你想自定 义 SQL ,否则没必要更改数据库的列名称。

多对多:Django 创建一个中间表来表示ManyToManyField关系。默认情况下,中间表的名称由两个关系表名结合而成。

由于某些数据库对表名的长度有限制,所以中间表的名称会自动限制在64个字符以内,并包含一个不重复的哈希字符串。这

意味着,你可能看到类似 book_authors_9cdf4 这样的表名称。你可以使用 db_table 选项手动指定中间表名称。

但是,如果你想手动指定中间表,你可以用 through 选项来指定model 使用另外某个 model 来管理多对多关系。而这个 model 就是中间表所对应的 model :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person(models.Model):
    name = models.CharField(max_length=128)
    def __unicode__(self):
        return self.name
class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')
    def __unicode__(self):
        return self.name
class Membership(models.Model):
    person = models.ForeignKey(Person)
    group = models.ForeignKey(Group)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

这样,就可以记录某个person何时加入group了。

要建立Person与Group的关系就不能用add,create,remove了,而是需要通过Membership进行。

1
2
3
4
5
6
7
>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles,
...     date_joined=date(1962816),
...     invite_reason= "Needed a new drummer.")
>>> m1.save()

clear()还是可以使用的

1
>>> beatles.members.clear()

当多对多关系关联自身时,中间表的ForeignKey是可以指向同一个Model的,但是它们必须被看做ManyToManyField的两边,而不是对称的,需要设置 symmetrical=False。

5.其它参数 (Arguments)

5.1 ForeignKey 接受下列这些可选参数,这些参数定义了关系是如何运行的。

ForeignKey.limit_choices_to

它是一个包含筛选条件和对应值的字典,用来在 Django 管理后台筛选 关联对象。例如,利用 Python 的 datetime 模块,过滤掉不符合筛选条件关联对象:

limit_choices_to = {'pub_date__lte': datetime.date.today}

只有 pub_date 在当前日期之前的关联对象才允许被选。

也可以使用 Q 对象来代替字典,从而实现更复杂的筛选。当limit_choices_to为Q对象时,如果把此外键字段放在ModelAdmin的raw_id_fields时是不可用的。

ForeignKey.to_field

指定当前关系与被关联对象中的哪个字段关联。默认情况下,to_field 指向被关联对象的主键。

ForeignKey.on_delete

当一个model对象的ForeignKey关联的对象被删除时,默认情况下此对象也会一起被级联删除的。

1
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.CASCADE)

CASCADE:默认值,model对象会和ForeignKey关联对象一起被删除

SET_NULL:将model对象的ForeignKey字段设为null。当然需要将null设为True。

SET_DEFAULT:将model对象的ForeignKey字段设为默认值。

Protect:删除ForeignKey关联对象时会生成一个ProtectedError,这样ForeignKey关联对象就不会被删除了。

SET():将model对象的ForeignKey字段设为传递给SET()的值。

1
2
3
4
5
def get_sentinel_user():
    return User.objects.get_or_create(username='deleted')[0]
 
class MyModel(models.Model):
    user = models.ForeignKey(User, on_delete=models.SET(get_sentinel_user))

DO_NOTHING:啥也不做。

5.2 ManyToManyField 接受下列可选参数,这些参数定义了关系是如何运行的。

ManyToManyField.limit_choices_to

和 ForeignKey.limit_choices_to 用法一样。

limit_choices_to 对于通过 through 参数指定了中介表的 ManyToManyField 不起作用。

ManyToManyField.symmetrical

只要定义递归的多对多关系时起作用。

ManyToManyField.through

手动指定中间表

ManyToManyField.db_table

指定数据库中保存多对多关系数据的表名称。如果没有提供该选项,Django 就会根据两个关系表的名称生成一个新的表名,做为中间表的名称。

6.OneToOneField

class OneToOneField(othermodel[, parent_link=False, **options])

用来定义一对一关系。笼统地讲,它与声明了 unique=True 的 ForeignKey 非常相似,不同的是使用反向关联的时候,得到的不是一个对象列表,而是一个单独的对象。

在某个 model 扩展自另一个 model 时,这个字段是非常有用的;例如: 多表继承 (Multi-tableinheritance) 就是通过在子 model 中添加一个指向父 model 的一对一关联而实现的。

必须给该字段一个参数:被关联的 model 类。工作方式和 ForeignKey 一样,连递归关联 (recursive) 和 延后关联 (lazy) 都一样。

此外,OneToOneField 接受 ForeignKey 可接受的参数,只有一个参数是 OnetoOneField 专有的:OneToOneField.parent_link

如果为 True ,并且作用于继承自某个父 model 的子 model 上(这里不能是延后继承,父 model 必须真实存在 ),那么该字段就会变成指向父类实例的引用(或者叫链接),

而不是象其他OneToOneField 那样用于扩展父类并继承父类属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from django.db import models, transaction, IntegrityError
 
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
 
    def __unicode__(self):
        return u"%s the place" % self.name
 
class Restaurant(models.Model):
    place = models.OneToOneField(Place, primary_key=True)
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()
 
    def __unicode__(self):
        return u"%s the restaurant" % self.place.name
 
class Waiter(models.Model):
    restaurant = models.ForeignKey(Restaurant)
    name = models.CharField(max_length=50)
 
    def __unicode__(self):
        return u"%s the waiter at %s" % (self.name, self.restaurant)

使用反向关联的时候,得到的不是一个对象列表,而是一个单独的对象:

1
2
3
4
5
6
7
8
9
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
<Place: Demon Dogs the place>
>>> Waiter.objects.filter(restaurant__place__name__startswith="Demon")
 

7.自定义多对多关系表

Django除了能自动创建多对多的第三张表,同样也可以自定义创建多对多的第三张表,而且操作和管理扩展等难易程度要比自动创建的好许多。所以,在之后的models表结构中,推荐使用自定义的方式。仅仅在创建时添加一个字段即可:through。

#=========================== 方式二,自定义关联表

class Host1(models.Model):
hostname = models.CharField(max_length=32)
port = models.IntegerField() class HostAdmin1(models.Model):
username = models.CharField(max_length=32)
email = models.CharField(max_length=32)
host = models.ManyToManyField(Host1, through='HostRelation') class HostRelation(models.Model):
c1 = models.ForeignKey(Host1)
c2 = models.ForeignKey(HostAdmin1) #自定义创建第三张表,其中的外键都为一对多的关系 增加数据(直接以id的方式向第三张表中添加数据)
models.HostRelation.objects.create(c1_id=2, c2_id=1,) 查询数据(双__)
relation_list = models.HostRelation.objects.filter(c2__username='dali')
for item in relation_list:
print item.c1.hostname
print item.c2.username

python--第二十三天总结(一对多和多对多)的更多相关文章

  1. 孤荷凌寒自学python第二十三天python类的封装

    孤荷凌寒自学python第二十三天python类的封装 (完整学习过程屏幕记录视频地址在文末,手写笔记在文末) 一.从怎么样访问类的内部代码块中定义的私有属性说起 类中定义的私有属性和私有方法是无法被 ...

  2. 小甲鱼Python第二十三讲课后习题--025,字典

    笔记: 1.字典是Python中唯一的映射类型 2.字典包含两个要素:键(key)和值(value)他们是成对出现的,用大括号括起来,多对存在时用逗号隔开. 3.可以用dict()直接创建字典,如di ...

  3. python第二十三天-----作业中

    #!usr/bin/env python #-*-coding:utf-8-*- # Author calmyan import os ,sys,time from core import trans ...

  4. python第二十三课——dict中的函数

    dic1 = {...} dic2 = {...} dic1.update(dic2) 1.update(dict):dic1调用update传入dic2,如果dic2中的内容在dic1中不存在,那么 ...

  5. python第二十三天-----Tornado

    Tornado是一个轻量级完整的web框架,在Linux系统下它会使用epoll,是一个异步非阻塞的web服务器框架,对于实时应用来说很理想,想想同是异步非阻塞的nginx的残暴程度就知道了 1.路由 ...

  6. Python开发【第二十三篇】:持续更新中...

    Python开发[第二十三篇]:持续更新中...

  7. python自动开发之第二十三天(Django)

    一.一大波model操作 1. 创建数据库表 # 单表 # app01_user ==> tb1 # users class User(models.Model): name = models. ...

  8. 简学Python第二章__巧学数据结构文件操作

    #cnblogs_post_body h2 { background: linear-gradient(to bottom, #18c0ff 0%,#0c7eff 100%); color: #fff ...

  9. Python第十三天 django 1.6 导入模板 定义数据模型 访问数据库 GET和POST方法 SimpleCMDB项目 urllib模块 urllib2模块 httplib模块 django和web服务器整合 wsgi模块 gunicorn模块

    Python第十三天   django 1.6   导入模板   定义数据模型   访问数据库   GET和POST方法    SimpleCMDB项目   urllib模块   urllib2模块 ...

  10. Python第二天 变量 运算符与表达式 input()与raw_input()区别 字符编码 python转义符 字符串格式化 format函数字符串格式化 帮助

    Python第二天  变量  运算符与表达式  input()与raw_input()区别  字符编码  python转义符  字符串格式化  format函数字符串格式化  帮助 目录 Pychar ...

随机推荐

  1. VMware 打开虚拟机的时候提示 internal error 内部错误 遇到这个问题时我的解决方法

    任务栏右键,启动任务管理器,选择“服务”选项卡 找到这个服务 启动这个服务后,再次尝试打开虚拟机,就OK了.

  2. [ZZ]新手学 appium-合集第一季度

    原文地址: https://testerhome.com/topics/2599 新手学appium-合集第一季度地址如下: 1.新手学 appium-GUI 端搞起来:http://testerho ...

  3. Java程序的第一次作业

  4. select、poll、epoll之间的区别总结[转载]

    转载:https://www.cnblogs.com/Anker/p/3265058.html select,poll,epoll都是IO多路复用的机制.I/O多路复用就通过一种机制,可以监视多个描述 ...

  5. python生成linux命令行工具

    您是否也曾一直想生成类似cd, cat等小巧/迷人/实用的小工具作为系统命令或者将python程序打包为exe进行分发?ok,机会来了.利用python 的argparse 和 pyinstaller ...

  6. NPOI helper

    using NPOI.HSSF.UserModel; using NPOI.HSSF.Util; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; ...

  7. wine install

    # yum -y groupinstall 'Development Tools' # yum -y install libX11-devel libxml2-devel libxslt-devel ...

  8. 集成学习(ensemble learning)

    集成学习,又称为“多分类器系统”(multi-classifier system).“基于委员会的学习”(committee-based learning)等.基本的想法是结合多个学习器,获得比单一学 ...

  9. element-ui-verify使用

    element-ui-verify是对ElementUI原本的校验封装之后的插件,并不会影响使用ElementUI的原生校验. 使用环境为vue+element-ui+webpack模块环境,首先使用 ...

  10. ie9 placeholder兼容代码方法

    function guid() { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r ...