• python--第二十三天总结(一对多和多对多)


    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(1962, 8, 16),
    ...     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
  • 相关阅读:
    [NOI2003],[AHOI2006]文本编辑器
    luogu P5151 HKE与他的小朋友
    [NOI2005]维护数列
    [HNOI2012]永无乡
    luogu P4146 序列终结者
    [SCOI2016]美味
    UVA1451 Average
    [JSOI2007]字符加密
    luogu P3809 【模板】后缀排序
    CentOS 7系统启动后怎么从命令行模式切换到图形界面模式
  • 原文地址:https://www.cnblogs.com/wjx1/p/5421911.html
Copyright © 2020-2023  润新知