Django学习笔记:Models03 关系类型字段

多对一(ForeignKey)

多对一关系由外键实现,外键要定义在“多”的一方,如:

models.ForeignKey(<主表>, ...)


如果要关联的主表在另一个app中,要显式的指出:

models.ForeignKey(’<app名称>.<主表>‘, ...)

 

可创建自己关联自己的外键——比如,在评论系统中,一条评论可被多次引用自身并继续评论:

class Comment(models.Model):

    title = models.Charfield(max_length=128)

    text = models.TextField()

    parent_comment = models.ForeignKey(’self’, on_delete=models.CASCADE)

    ...


如果在子模型中存在多个外键指向同一个主模型,必须给他们加上不同的related_name,用于反向查询。

外键字段在数据库中的列名是<外键名>_id,用来储存主表对象的id,因此,在得到主表id的情况下可以不调用外键而直接筛选:

QuerySet = <外键表>.filter(<外键名>_id=<主表id>)

#获得与“<主表id>=xxx的主对象”相关联的子对象


多对一的字段参数

on_delete=models.XXX

当主对象被删除时,关联的子对象的操作(因为主键不能随意删除,外键可以直接删除)

models.CASCADE:同时删除关联的子对象,Django默认

models.PROTECT:阻止CASCADE删除关联的子对象,并弹出ProtectedError异常

models.SET_NULL:将子对象的外键字段设为null(外键字段需设置null=True)

models.SET_DEFAULT:将子对象的外键字段设为默认值(外键字段需设置default=’xxx’)

models.DO_NOTHING:什么也不做

models.SET():设置为一个传递给SET()的值或一个回调函数的返回值

注:on_delete是Django2.0之后新增的必选项。


related_name= 

通过主对象反向引用子对象的通道名称,默认名称是<小写子模型名>_set。

如:

class Car(models.Model):

    factory = models.ForeignKey(

        Factory,      

        on_delete=models.CASCADE,

        related_name='car_by_factory',

    )

此时Factory主表取出所有关联的Car对象:factory.car_by_factory.all()

如果不想给外键设置反向关联名称,就用related_name=’+’或以+号结尾


related_query_name= 

主对象反向关联查询的名称,默认查询名称是<外键列>__<主表的某列>。

如:

class Tag(models.Model):

    article = models.ForeignKey(

        Article,

        on_delete=models.CASCADE,

        related_query_name="tag",

    )

    name = models.CharField(max_length=255)

此时用tag作为查询名称:Article.objects.filter(tag__name="A")


limit_choices_to={}

限制外键能关联的对象,用于Django的表单模块和admin后台,可以传入字典、Q对象、一个返回字典或Q对象的函数。

如:

staff_member = models.ForeignKey(

    User,

    on_delete=models.CASCADE,

    limit_choices_to={'is_staff': True},

)

此时,表单模块的staff_member字段列表中只会出现is_staff=True的Users对象,对admin后台非常有用。


to_field= 

让外键关联到指定的字段上(默认关联到主键),该字段必须有unique=True属性。


多对一的引用

子对象引用主对象:

<子对象>.<外键列>


主对象引用子对象:

<主对象>.<子表小写>_set

如:

<主对象>.<子表小写>_set.all()                          #所有关联子对象的集合

<主对象>.<子表小写>_set.create(<子表字段>=’xxx’, ...)     #创建关联的子对象

<主对象>.<子表小写>_set.count()                       #查询关联子对象的数量


添加多对一关系:

<子对象>.<外键列> = <主对象>

<子对象>.save()


删除多对一关系:

<子对象>.<外键列> = None     #外键列要设为null=True


多对一的查询

查询子对象的集合:

<子表>.objects.filter(<外键列>__<主表的某列>=’xxx’)

#获得与“<主表的某列>=xxx的主对象”相关联的子对象


查询主对象的集合:

<主表>.objects.filter(<子表小写> __<子表的某列>=’xxx’)

#获得与“<子表的某列>=xxx的子对象”相关联的主对象



多对多(ManyToManyField)

多对多字段可定义在任意一方,如:

models.ManyToManyField(‘<主表>’)

多对多字段不支持Django内置的validators验证功能。

null参数对ManyToManyField多对多字段无效,设置null=True毫无意义。

如果在子模型中存在多个多对多键指向同一个主模型,必须给他们加上不同的related_name,用于反向查询。


采用默认中间表的数据库结构:

<中间表id>  <多对多表>_id   <主表>_id

默认中间表通过保存两张表的id进行关联


采用自定义中间表并添加新字段的数据库结构:

<中间表id>  <新字段1>  <新字段2>  <多对多表>_id   <主表>_id


多对多字段的参数

related_name= 

主对象反向引用多对象时的名称,和多对一相同。


related_query_name= 

主对象反向关联查询的名称,和多对一相同。


through= 

自定义中间表,用于保存两表关系的附加数据,中间表要有两个外键字段分别指向关联的两个模型。


through_fields=(<字段1>, <字段2>)

当中间表有多个外键指向一个表的时候,用through_firlds指定中间表的两个连接字段,如:

class Person(models.Model):

    name = models.CharField(max_length=32)


class Group(models.Model):

    name = models.CharField(max_length=64)

    members = models.ManyToManyField(

        Person,

        through='Membership',

        through_fields=('group', 'person' ),     #指定两个连接字段

    )


class Membership(models.Model):

    group = models.ForeignKey(Group, on_delete=models.CASCADE)

    person = models.ForeignKey(Person, on_delete=models.CASCADE)

    inviter = models.ForeignKey(                       #另一个指向Person表的外键

        Person,

        on_delete=models.CASCADE,

        related_name="membership_invites",

    )

    invite_reason = models.CharField(max_length=128)


db_table= 

设置中间表的名称,默认为<多对多字段名>_<主表名>_<一串哈希码>


limit_choices_to={ }

限制多对多键关联的对象,和多对一相同,对于用through定义中间表的字段无效。

 

多对多的引用

多对象引用主对象:

<多对象>.<多键列>


主对象引用多对象:

<主对象>.<多表小写>_set


添加多对多关系:(默认中间表)

<多对象>.<多键列>.add(<主对象>)     #可同时添加多个主对象


添加多对多关系:(自定义中间表)

a = <主对象>

b = <多对象>

membership = <中间表>(<主表外键列>=a, <多表外键列>=b, <字段1>=’xxx’, <字段2>=’xxx’, ...)

membership.save()

#先创建主表和多表的实例,再创建中间表的连接关系

#自定义中间表不能用add()、create()、remove()和set()操作对象的关系


删除多对多关系:

<多对象>.<多键列>.clear()


获取中间表的附加数据:

a = <主对象>

b = <多对象>

membership = <中间表>.objects.get(<主表外键列>=a, <多表外键列>=b)

membership.<字段1>     #获取字段信息



一对一(OneToOneField)

反向关联的对象只有一个,多数用于从一个模型扩展出另一个模型的情况。

一对一通过子对象访问关联的主对象:

<子对象>.<一对一键>


一对一通过主对象访问关联的子对象:

<主对象>.<小写子模型名>

注意返回的是单个对象而不是集合。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容