• Django 08. django框架模型之增删改查进阶


    简介django 框架模型之增改查进阶操作。

    创建完Model之后, Django 自动为你提供一套数据库抽象层的API,利用它可以完成创建,提取,更新,删除对象的操作。

    以下面的Model为例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()
     
        # On Python 3: def __str__(self):
        def __unicode__(self):
            return self.name
     
    class Author(models.Model):
        name = models.CharField(max_length=50)
        email = models.EmailField()
     
        # On Python 3: def __str__(self):
        def __unicode__(self):
            return self.name
     
    class Entry(models.Model):
        blog = models.ForeignKey(Blog)
        headline = models.CharField(max_length=255)
        body_text = models.TextField()
        pub_date = models.DateField()
        mod_date = models.DateField()
        authors = models.ManyToManyField(Author)
        n_comments = models.IntegerField()
        n_pingbacks = models.IntegerField()
        rating = models.IntegerField()
     
        # On Python 3: def __str__(self):
        def __unicode__(self):
            return self.headline

    创建Creating objects

    1
    2
    >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
    >>> b.save()

    Django 用一种很直观的表达方式将Python对象和数据表对应起来:一个model类对应一张数据表,一个model实例对应表中的某一行记录。

    以创建对象为例:只要将关键字参数传递给model类,然后调用save()保存到数据库即可

    这段代码就会在幕后执行一条INSERT SQL语句。除非你显式地调用save()方法,否则Django不会保存到数据库中。

    你可以使用create()方法一次完成新建并保存对象的操作。

    修改Saving changes to objects

    1
    2
    >> b5.name = 'New name'
    >> b5.save()

    Saving ForeignKey and ManyToManyField fields

    更新ForeignKey字段和保存普通字段没什么差别;只是在给字段分配对象时要注意对象类型一定要正确:

    1
    2
    3
    4
    >>> entry = Entry.objects.get(pk=1)
    >>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
    >>> entry.blog = cheese_blog
    >>> entry.save()

    更新ManyToManyField就有些不同;要在字段上使用add()方法来添加关系:

    1
    2
    >>> joe = Author.objects.create(name="Joe")
    >>> entry.authors.add(joe)

    一次添加多个:

    1
    2
    3
    4
    5
    >>> john = Author.objects.create(name="John")
    >>> paul = Author.objects.create(name="Paul")
    >>> george = Author.objects.create(name="George")
    >>> ringo = Author.objects.create(name="Ringo")
    >>> entry.authors.add(john, paul, george, ringo)

    检索对象 (Retrieving objects)

    要检索数据库中的对象,就要为你的model 类构造一个查询集QuerySet。一个QuerySet就代表数据库中的一组数据。它可以有一个或很多个,也可以通过filter根据给定的参数对数据集做进一步筛选。在SQL术语中,QuerySet相当于SELECT语句,filter相当于WHERE或LIMIT这样的限定从句。

    检索所有的对象

    1
    all_entries = Entry.objects.all()

    使用过滤器filter检索特定的对象

    filter(**kwargs)

        返回满足筛选条件的新QuerySet

    exclude(**kwargs)

        返回不满足筛选条件的新QuerySet

    1
    Entry.objects.filter(pub_date__year=2006)

    链式过滤:

    1
    2
    3
    4
    5
    6
    7
    >>> Entry.objects.filter(
    ...     headline__startswith='What'
    ... ).exclude(
    ...     pub_date__gte=datetime.date.today()
    ... ).filter(
    ...     pub_date__gte=datetime(2005130)
    ... )

    查询一个单独的对象

    1
    >>> one_entry = Entry.objects.get(pk=1)

    注意当使用get查询时若对象不存在会产生DoesNotExist异常。

    QuerySet是惰性的

    QuerySets是惰性的。创建 QuerySet 的动作不涉及任何数据库操作。你可以一直添加过滤器,在这个过程中,Django不会执行任何数据库查询,除非QuerySet被执行。

    切片

    可以用python的数组切片语法来限制你的QuerySet以得到一部分结果。它等价于SQL中的LIMIT和OFFSET。

    例如,下面的这个例子返回前五个对象:

    1
    >>> Entry.objects.all()[:5]

    Django 不支持对查询集做负数索引。

    跨关系查询

    Django 提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用双下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。

    1
    Blog.objects.filter(entry__authors__name='Lennon')

    要注意的是如果跨关系对象有多个符合条件,被查询对象会返回多次:

    1
    Blog.objects.filter(entry__headline='note')

    上面如果有个Blog链接多个headline为note的Entry对象,此Blog会返回多次。

    跨一对多/多对多关系

    对于包含在同一个filter()中的筛选条件,查询集要同时满足所有筛选条件。而对于连续的filter(),查询集的范围是依次限定的。但对于跨一对多/多对多关系查询来说,在第二种情况下,筛选条件针对的是主model所有的关联对象,而不是被前面的filter()过滤后的关联对象。

    这听起来会让人迷糊,举个例子会讲得更清楚。要检索这样的 blog:它要关系一个大标题中含有 "Lennon" 并且在2008年出版的entry(这个entry同时满足这两个条件),可以这样写:

    1
    2
    Blog.objects.filter(entry__headline__contains='Lennon',
            entry__pub_date__year=2008)

    要检索另外一种 blog:它关联一个大标题含有"Lennon"的 entry ,又关联一个在2008年出版的 entry (一个entry的大标题含有Lennon,同一个或另一个entry是在2008年出版的)。可以这样写:

    1
    2
    Blog.objects.filter(entry__headline__contains='Lennon').filter(
            entry__pub_date__year=2008)

    上述原则同样适用于exclude():一个单独exclude()中的所有筛选条件都是作用于同一个实例 (如果这些条件都是针对同一个一对多/多对多的关系)。连续的filter()或exclude()却根据同样的筛选条件,作用于不同的关联对象。

    Filters can reference fields on the model

    class F

    在上面所有的例子中,我们构造的过滤器都只是将字段值与某个常量做比较。如果我们要对两个字段的值做比较,那该怎么做呢?

    Django 提供F()来做这样的比较。F()的实例可以在查询中引用字段,来比较同一个model实例中两个不同字段的值。

    例如:要查询回复数(comments)大于广播数(pingbacks)的博文(blog entries),可以构造一个 F() 对象在查询中引用评论数量:

    1
    2
    >>> from django.db.models import F
    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

    Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。例如,要找到广播数等于评论数两倍的博文,可以这样修改查询语句:

    1
    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'* 2)

    要查找阅读数量小于评论数与广播数之和的博文,查询如下:

    1
    >>> Entry.objects.filter(rating__lt=F('n_comments'+ F('n_pingbacks'))

    你也可以在 F() 对象中使用双下划线做跨关系查询。F() 对象使用双下划线引入必要的关联对象。例如,要查询博客(blog)名称与作者(author)名称相同的博文(entry),查询就可以这样写:

    1
    >>> Entry.objects.filter(authors__name=F('blog__name'))

    对于Date和datetime域,可以加减timedelta对象。

    返回出版3天以后修改的博文:

    1
    2
    >>> from datetime import timedelta
    >>> Entry.objects.filter(mod_date__gt=F('pub_date'+ timedelta(days=3))

    F对象现在也支持位运算(bitand(),bitor()):

    1
    >>> F('somefield').bitand(16)

    主键查询的简捷方式 (The pk lookup shortcut)

    为使用方便考虑,Django 用pk代表主键"primary key"。

    以Blog为例, 主键是id字段,所以下面三个语句都是等价的:

    1
    2
    3
    >>> Blog.objects.get(id__exact=14# Explicit form
    >>> Blog.objects.get(id=14# __exact is implied
    >>> Blog.objects.get(pk=14# pk implies id__exact

    在LIKE语句中转义百分号%和下划线_ (Escaping percent signs and underscores in LIKE statements)

    字段筛选条件相当于LIKE SQL语句 (iexactcontainsicontainsstartswithistartswithendswith 和 iendswith) ,它会自动转义两个特殊符号,百分号(%)和下划线(_)。(在LIKE语句中,百分号%表示多字符匹配,而下划线_表示单字符匹配。)

    这就意味着我们可以直接使用这两个字符,而不用考虑他们的SQL语义。例如,要查询大标题中含有一个百分号%的entry:

    1
    >>> Entry.objects.filter(headline__contains='%')

    Django会处理转义,下划线_和百分号%的处理方式相同,Django都会自动转义。

    缓存和查询

    每个QuerySet都包含一个缓存,以减少对数据库的访问。要编写高效代码,就要理解缓存是如何工作的。

    一个QuerySet时刚刚创建的时候,缓存是空的。 QuerySet 第一次运行时,会执行数据库查询,接下Django就在QuerySet的缓存中保存查询的结果,并根据请求返回这些结果(比如,后面再次调用这个 QuerySet 的时候)。再次运行 QuerySet 时就会重用这些缓存结果。

    要牢住上面所说的缓存行为,否则在使用 QuerySet 时可能会给你造成不小的麻烦。例如,创建下面两个 QuerySet ,并对它们求值,然后释放:

    1
    2
    >>> print([e.headline for in Entry.objects.all()])
    >>> print([e.pub_date for in Entry.objects.all()])

    这就意味着相同的数据库查询将执行两次,事实上读取了两次数据库。而且,这两次读出来的列表可能并不完全相同,因为存在这种可能:在两次读取之间,某个 Entry 被添加到数据库中,或是被删除了。

    要避免这个问题,只要简单地保存QuerySet然后重用即可:

    1
    2
    3
    >>> queryset = Entry.objects.all()
    >>> print([p.headline for in queryset]) # 对查询集求值.
    >>> print([p.pub_date for in queryset]) # 使用缓存.

    When querysets are not cached

    查询集并不总是缓存结果,当查询集执行部分查询时,会先检查缓存,如果它没有被填充,部分查询返回的结果不会被缓存。这意味着,使用切片查询不会填充缓存。

    例如,重复的切片查询每次都会访问数据库:

    1
    2
    3
    >>> queryset = Entry.objects.all()
    >>> print queryset[5# 访问数据库
    >>> print queryset[5# 再次访问数据库

    但是,如果整个查询集已经被求值,切片查询会使用缓存:

    1
    2
    3
    4
    >>> queryset = Entry.objects.all()
    >>> [entry for entry in queryset] # 查询数据库
    >>> print queryset[5# 使用缓存
    >>> print queryset[5# 使用缓存

    下面是一些会填充缓存的操作:

    1
    2
    3
    4
    >>> [entry for entry in queryset]
    >>> bool(queryset)
    >>> entry in queryset
    >>> list(queryset) 

    用Q对象实现复杂查找 (Complex lookups with Q objects)

    在filter() 等函式中关键字参数彼此之间都是 "AND" 关系。如果你要执行更复杂的查询(比如,实现筛选条件的OR关系),可以使用Q对象。

    Q对象(django.db.models.Q)是用来封装一组查询关键字的对象。

    例如,下面这个Q对象封装了一个单独的 LIKE 查询:

    1
    2
    from django.db.models import Q
    Q(question__startswith='What')

    Q 对象可以用 & 和 | 运算符进行连接。当某个操作连接两个 Q 对象时,就会产生一个新的等价的 Q 对象。

    例如,下面这段语句就产生了一个Q,这是用 "OR" 关系连接的两个"question__startswith" 查询:

    1
    Q(question__startswith='Who') | Q(question__startswith='What')

    上面的例子等价于下面的 SQL WHERE 从句:

    1
    WHERE question LIKE 'Who%' OR question LIKE 'What%'

    你可以用 & 和 | 连接任意多的Q对象,而且可以用括号分组。Q 对象也可以用 ~ 操作取反,而且普通查询和取反查询(NOT)可以连接在一起使用:

    1
    Q(question__startswith='Who') | ~Q(pub_date__year=2005)

    每种查询函式(比如 filter(), exclude(), get()) 除了能接收关键字参数以外,也能以位置参数的形式接受一个或多个 Q 对象。如果你给查询函式传递了多个 Q 对象,那么它们彼此间都是 "AND" 关系。例如:

    1
    2
    3
    4
    Poll.objects.get(
        Q(question__startswith='Who'),
        Q(pub_date=date(200552)) | Q(pub_date=date(200556))
    )

    ... 大体可以翻译为下面的 SQL:

    1
    2
    SELECT * from polls WHERE question LIKE 'Who%'
        AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')

    查找函式可以混用 Q 对象和关键字参数。查询函式的所有参数(Q 关系和关键字参数) 都是 "AND" 关系。但是,如果参数中有 Q 对象,它必须排在所有的关键字参数之前。例如:

    1
    2
    3
    Poll.objects.get(
        Q(pub_date=date(200552)) | Q(pub_date=date(200556)),
        question__startswith='Who')

    是一个有效的查询。但下面这个查询虽然看上去和前者等价:

    1
    2
    3
    4
    # 无效查询
    Poll.objects.get(
        question__startswith='Who',
        Q(pub_date=date(200552)) | Q(pub_date=date(200556)))

    但这个查询却是无效的。

    对象比较 (Comparing objects)

    要比较两个对象,就和Python 一样,使用双等号运算符:==。实际上比较的是两个model的主键值。

    以上面的Entry为例,下面两个语句是等价的:

    1
    2
    >>> some_entry == other_entry
    >>> some_entry.id == other_entry.id

    如果model的主键名称不是id,也没关系。Django会自动比较主键的值,而不管他们的名称是什么。例如,如果一个model的主键字段名称是name,那么下面两个语句是等价的:

    1
    2
    >>> some_obj == other_obj
    >>> some_obj.name == other_obj.name

    对象删除(Deleting objects)

    删除方法就是delete()。它运行时立即删除对象而不返回任何值。例如:

    1
    e.delete()

    你也可以一次性删除多个对象。每个QuerySet 都有一个delete() 方法,它一次性删除 QuerySet 中所有的对象。

    例如,下面的代码将删除 pub_date 是2005年的 Entry 对象:

    1
    Entry.objects.filter(pub_date__year=2005).delete()

    要牢记这一点:无论在什么情况下,QuerySet 中的 delete() 方法都只使用一条 SQL 语句一次性删除所有对象,而并不是分别删除每个对象。如果你想使用在model中自定义的delete() 方法,就要自行调用每个对象的delete方法。(例如,遍历 QuerySet,在每个对象上调用 delete()方法),而不是使用QuerySe 中的 delete()方法。

    在Django删除对象时,会模仿SQL约束ON DELETE CASCADE的行为,换句话说,删除一个对象时也会删除与它相关联的外键对象。例如:

    1
    2
    3
    = Blog.objects.get(pk=1)
    # This will delete the Blog and all of its Entry objects.
    b.delete()

    要注意的是: delete() 方法是QuerySet上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

    1
    Entry.objects.all().delete()

    复制Copying model instances

    复制对象并没有内置的函数,最简单的情况,将pk设为None。

    1
    2
    3
    4
    5
    blog = Blog(name='My blog', tagline='Blogging is easy')
    blog.save() # blog.pk == 1
     
    blog.pk = None
    blog.save() # blog.pk == 2

    如果使用继承的话,情况会复杂一点:

    1
    2
    3
    4
    5
    class ThemeBlog(Blog):
        theme = models.CharField(max_length=200)
     
    django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
    django_blog.save() # django_blog.pk == 3

    需要将pk和id都设为None:

    1
    2
    3
    django_blog.pk = None
    django_blog.id = None
    django_blog.save() # django_blog.pk == 4

    这样写是不会复制关系对象的,要复制关系对象,还需要一点代码:

    1
    2
    3
    4
    5
    entry = Entry.objects.all()[0# some previous entry
    old_authors = entry.authors.all()
    entry.pk = None
    entry.save()
    entry.authors = old_authors # saves new many2many relations

    一次更新多个对象 (Updating multiple objects at once)

    有时你想对QuerySet中的所有对象,一次更新某个字段的值。这个要求可以用 update() 方法完成。例如:

    1
    2
    # Update all the headlines with pub_date in 2007.
    Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')

    这种方法仅适用于非关系字段和ForeignKey外键字段。更新非关系字段时,传入的值应该是一个常量。更新ForeignKey字段时,传入的值应该是你想关联的那个类的某个实例。例如:

    1
    2
    3
    >>> b = Blog.objects.get(pk=1)
    # Change every Entry so that it belongs to this Blog.
    >>> Entry.objects.all().update(blog=b)

     update() 方法也是即时生效,返回与查询匹配的行数(可能不等于被更新的行数,因为有些行已经是新值,不需要被更新)。 在QuerySet进行更新时,唯一的限制就是一次只能更新一个数据表,就是当前 model 的主表。你可以尝试更新关联表和与此类似的操作,但是只有主表的条目被更新:

    1
    2
    3
    >>> b = Blog.objects.get(pk=1)
    # Update all the headlines belonging to this Blog.
    >>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')

    要小心的是:update() 方法是直接翻译成一条SQL语句的。因此它是直接地一次完成所有更新。它不会调用你的 model 中的save() 方法,也不会发出pre_save 和post_save信号(这些信号在调用 save() 方法时产生)。如果你想保存QuerySet中的每个对象,并且调用每个对象各自的save() 方法,那么你不必另外多写一个函式。只要遍历这些对象,依次调用 save() 方法即可:

    1
    2
    for item in my_queryset:
        item.save()

    在调用update时可以使用 F() 对象来把某个字段的值更新为另一个字段的值。这对于自增记数器是非常有用的。例如,给所有的博文 (entry) 的广播数 (pingback) 加一:

    1
    >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks'+ 1)

    但是,与 F() 对象在查询时所不同的是,在filter 和 exclude子句中,你不能在 F() 对象中引入关联关系(NO-Join),你只能引用当前 model 中要更新的字段。如果你在 F() 对象引入了Join 关系object,就会抛出 FieldError 异常:

    1
    2
    # THIS WILL RAISE A FieldError
    >>> Entry.objects.update(headline=F('blog__name'))

    对象关联(Related objects)

    当你定义在 model 定义关系时 (例如,ForeignKey, OneToOneField, 或 ManyToManyField),model 的实例自带一套很方便的API以获取关联的对象。

    以最上面的 models 为例,一个 Entry 对象e能通过blog属性获得相关联的Blog对象:e.blog。

    Django也提供反向获取关联对象的API,就是由从被关联的对象得到其定义关系的主对象。例如,一个Blog类的实例b对象通过entry_set属性得到所有相关联的Entry对象列表: 

    1
    b.entry_set.all()

    一对多关系(One-to-many relationships)

    正向(Forward)

    如果一个model有一个ForeignKey字段,我们只要通过使用关联model的名称就可以得到相关联的外键对象。

    1
    2
    >>> e = Entry.objects.get(id=2)
    >>> e.blog # Returns the related Blog object.

    你可以设置和获得外键属性。正如你所期望的,改变外键的行为并不引发数据库操作,直到你调用 save()方法时,才会保存到数据库。例如:

    1
    2
    3
    >>> e = Entry.objects.get(id=2)
    >>> e.blog = some_blog
    >>> e.save()

    如果外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。例如:

    1
    2
    3
    >>> e = Entry.objects.get(id=2)
    >>> e.blog = None
    >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ..

    在一对多关系中,第一次正向获取关联对象时,关联对象会被缓存。其后根据外键访问时这个实例,就会从缓存中获得它。例如:

    1
    2
    3
    >>> e = Entry.objects.get(id=2)
    >>> print(e.blog)  # 访问数据库
    >>> print(e.blog)  # 使用缓存

    要注意的是,QuerySet 的select_related() 方法提前将所有的一对多关系放入缓存中。例如:

    1
    2
    3
    >>> e = Entry.objects.select_related().get(id=2)
    >>> print(e.blog)  # 使用缓存
    >>> print(e.blog)  # 使用缓存

    逆向关联(Following relationships "backward")

    如果model有一个ForeignKey外键字段,那么外联model的实例可以通过访问Manager来得到所有相关联的源model的实例。默认情况下,这个Manager被命名为 FOO_set, 这里面的 FOO 就是源 model 的小写名称。这个Manager返回QuerySets,它是可过滤和可操作的,在上面 "对象获取(Retrieving objects)" 有提及。

    1
    2
    3
    4
    5
    6
    >>> b = Blog.objects.get(id=1)
    >>> b.entry_set.all() # Returns all Entry objects related to Blog.
     
    # b.entry_set is a Manager that returns QuerySets.
    >>> b.entry_set.filter(headline__contains='Lennon')
    >>> b.entry_set.count()

     你可以通过在ForeignKey() 的定义中设置related_name的值来覆写FOO_set 的名称。例如,如果 Entry model 中做一下更改: blog = ForeignKey(Blog, related_name='entries'),那么接下来就会如我们看到这般:

    1
    2
    3
    4
    5
    >>> b = Blog.objects.get(id=1)
    >>> b.entries.all() # Returns all Entry objects related to Blog.
    # b.entries is a Manager that returns QuerySets.
    >>> b.entries.filter(headline__contains='Lennon')
    >>> b.entries.count()

     ForeignKey Manager 还有如下一些方法。下面仅仅对它们做一个简短介绍:

    add(obj1, obj2, ...)

    将某个特定的 model 对象添加到被关联对象集合中。

    create(**kwargs)

    创建并保存一个新对象,然后将这个对象加被关联对象的集合中,然后返回这个新对象。

    remove(obj1, obj2, ...)

    将某个特定的对象从被关联对象集合中去除。

    clear()

    清空被关联对象集合。

    想一次指定关联集合的成员,那么只要给关联集合分配一个可迭代的对象即可。它可以包含对象的实例,也可以只包含主键的值。例如:

    1
    2
    = Blog.objects.get(id=1)
    b.entry_set = [e1, e2]

    在这个例子中,e1 和 e2 可以是完整的 Entry 实例,也可以是整型的主键值。

    如果 clear() 方法是可用的,在迭代器(上例中就是一个列表)中的对象加入到 entry_set 之前,已存在于关联集合中的所有对象将被清空。如果 clear() 方法 不可用,原有的关联集合中的对象就不受影响,继续存在。

    这一节提到的每一个 "reverse" 操作都是实时操作数据库的,每一个添加,创建,删除操作都会及时保存将结果保存到数据库中。

    多对多关系(Many-to-many relationships)

    在多对多关系的任何一方都可以使用 API 访问相关联的另一方。多对多的 API 用起来和上面提到的 "逆向" 一对多关系关系非常相象。

    唯一的差虽就在于属性的命名: ManyToManyField 所在的 model (为了方便,我称之为源model A) 使用字段本身的名称来访问关联对象;而被关联的另一方则使用 A 的小写名称加上 '_set' 后缀(这与逆向的一对多关系非常相象)。

    下面这个例子会让人更容易理解:

    1
    2
    3
    4
    5
    6
    7
    = Entry.objects.get(id=3)
    e.authors.all() # Returns all Author objects for this Entry.
    e.authors.count()
    e.authors.filter(name__contains='John')
     
    = Author.objects.get(id=5)
    a.entry_set.all() # Returns all Entry objects for this Author.

    与 ForeignKey 一样, ManyToManyField 也可以指定 related_name。在上面的例子中,如果 Entry 中的 ManyToManyField 指定 related_name='entries',那么接下来每个 Author 实例的 entry_set 属性都被 entries 所代替。

    一对一关系(One-to-one relationships)

    相对于多对一关系而言,一对一关系是非常简单的。如果你在 model 中定义了一个 OneToOneField 关系,那么你就可以用这个字段的名称做为属性来访问其所关联的对象。

    1
    2
    3
    4
    5
    6
    class EntryDetail(models.Model):
        entry = models.OneToOneField(Entry)
        details = models.TextField()
     
    ed = EntryDetail.objects.get(id=2)
    ed.entry # Returns the related Entry object.

    与 "reverse" 查询不同的是,一对一关系的关联对象也可以访问Manager对象,但是这个Manager表现一个单独的对象,而不是一个列表:

    1
    2
    = Entry.objects.get(id=2)
    e.entrydetail # returns the related EntryDetail object

    如果一个空对象被赋予关联关系,Django就会抛出一个DoesNotExist 异常。

    和你定义正向关联所用的方式一样,类的实例也可以赋予逆向关联关系:

    1
    e.entrydetail = ed

    关系中的反向连接是如何做到的?

    其他对象关系的映射(ORM)需要你在关联双方都定义关系。而 Django 的开发者则认为这违背了 DRY 原则 (Don't Repeat Yourself),所以Django只需要你在一方定义关系即可。

    但仅由一个model类并不能知道其他model 类是如何与它关联的,除非是其他model也被载入,那么这是如何办到的?

    答案就在于 INSTALLED_APPS 设置中。任何一个 model 在第一次调用时,Django 就会遍历所有的 INSTALLED_APPS 的所有models,并且在内存中创建中必要的反向连接。本质上来说,INSTALLED_APPS 的作用之一就是确认 Django 完整的model范围。

    在关联对象上的查询(Queries over related objects)

    包含关联对象的查询与包含普通字段值的查询都遵循相同的规则。为某个查询指定某个值的时候,你可以使用一个类实例,也可以使用对象的主键值。

    例如,如果你有一个 Blog 对象 b ,它的 id=5, 下面三个查询是一样的:

    1
    2
    3
    Entry.objects.filter(blog=b) # Query using object instance
    Entry.objects.filter(blog=b.id# Query using id from instance
    Entry.objects.filter(blog=5# Query using id directly

    直接使用SQL(Falling back to raw SQL)

    如果你发现某个 SQL 查询用 Django 的数据库映射来处理会非常复杂的话,你可以使用直接写 SQL 来完成。

    建议的方式是在你的model自定义方法或是自定义model的manager方法来运行查询。虽然Django不要求数据操作必须在model层中执行。但是把你的商业逻辑代码放在一个地方,从代码组织的角度来看,也是十分明智的。

    最后,要注意的是,Django的数据操作层仅仅是访问数据库的一个接口。你可以用其他的工具,编程语言,数据库框架来访问数据库。对你的数据库而言,没什么是非用 Django 不可的。





  • 相关阅读:
    Spring源码情操陶冶-自定义节点的解析
    SpringMVC源码情操陶冶-DispatcherServlet
    springcloud config配置读取优先级
    Maven-常用插件
    springcloud情操陶冶-springcloud config server(三)
    springcloud情操陶冶-springcloud config server(二)
    springcloud情操陶冶-bootstrapContext(三)
    springcloud情操陶冶-springcloud config server(一)
    springcloud情操陶冶-bootstrapContext(二)
    springcloud情操陶冶-bootstrapContext(一)
  • 原文地址:https://www.cnblogs.com/PythonHomePage/p/7634400.html
Copyright © 2020-2023  润新知