others linux服务器运维 django3 监控 k8s golang 数据库 大数据 前端 devops 理论基础 java oracle 运维日志

django多表关联定义与查询

访问量:1345 创建时间:2020-01-15

通常在定义数据库模型是,不同的模型之间有关联关系,例如,user表与group表;员工与部门;书籍与作者等等。有些关系是一对一的关系,有的是一对多的关系,有的是多对多的关系,django通过下面的模型字段进行表关系的关联:

注意:如果你的model定义在不同的models.py文件中,你在定义关联关系前需要import到当前文件。

通过学习django的多表关联,了解3种关联关系的应用场景,和基本的查询语句(创建关联,正向查询,反向查询,删除关联等内容)。

django模型多对一关联

定义一个多对一的关联关系,使用 django.db.models.ForeignKey 类。就和其它 Field 字段类型一样,只需要在你模型中添加一个值为该类的属性。

学习案例:一个记者reporter有多篇文章:

from django.db import models
class Reporter(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField()
    def __str__(self):
        return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
    headline = models.CharField(max_length=100)
    pub_date = models.DateField()
    reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE) #定义多个车属于一个制造者
#on_delete=models.CASCADE  主外关系键中,级联删除,也就是当删除主表的数据时候从表中的数据也随着一起删除。
    def __str__(self):
        return self.headline
    class Meta:
        ordering = ['headline'] 

django模型多对一查询

迁移数据库后,可以在python3 manage.py shell中操作

#创建一个记者
>>> r = Reporter(first_name='John', last_name='Smith', email='john@example.com')
>>> r.save()
创建一个文章注意传参数reporter=r来关联上面的记者
>>> from datetime import date
>>> a = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
>>> a.save()
#查看文章关联的记者对象的id
>>> a.reporter.id
1
#查看文章关联的记者对象
>>> a.reporter
<Reporter: John Smith>
#通过 Reporter 对象来创建一个 Article
>>> new_article = r.article_set.create(headline="John's second story", pub_date=date(2005, 7, 29))
>>> new_article
<Article: John's second story>
>>> new_article.reporter
<Reporter: John Smith>
>>> new_article.reporter.id
1
#创建新的article
>>> new_article2 = Article.objects.create(headline="Paul's story", pub_date=date(2006, 1, 17), reporter=r)
>>> new_article2.reporter
<Reporter: John Smith>
>>> new_article2.reporter.id
1
#查看记者的所有文章
>>> r.article_set.all()
<QuerySet [<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]>
#根据记者查询,这个记者的文章,并且这些文章以This开头
>>> r.article_set.filter(headline__startswith='This')
<QuerySet [<Article: This is a test>]>
#查询文章,条件:作者的first_name以John开头的作者发布的文章
>>> Article.objects.filter(reporter__first_name='John')
<QuerySet [<Article: John's second story>, <Article: This is a test>]>
#多条件查询
>>> Article.objects.filter(reporter__first_name='John', reporter__last_name='Smith')
#根据记者的主键id查询相关联的文章
>>> Article.objects.filter(reporter__pk=1)

django模型多对多关联

定义一个多对多的关联关系,使用 django.db.models.ManyToManyField 类。就和其他 Field 字段类型一样,只需要在你模型中添加一个值为该类的属性。

学习案例:一篇“Article(报刊上的文章)”可能在多个“公开发行物(对象objects)”中发布,并且一个“公开发行物(对象objects)”也有多个具体发行的对象(Article)(像小视频既可以发布在b站,也可以发在头条一样):

from django.db import models

class Publication(models.Model):
    title = models.CharField(max_length=30)
    class Meta:
        ordering = ['title']
    def __str__(self):
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)
    class Meta:
        ordering = ['headline']
    def __str__(self):
        return self.headline

django模型多对多查询

迁移数据库后,可以在python3 manage.py shell中操作

#创建几个“公共出版物”:
>>> p1 = Publication(title='The Python Journal')
>>> p1.save()
>>> p2 = Publication(title='Science News')
>>> p2.save()
>>> p3 = Publication(title='Science Weekly')
>>> p3.save()

#创建一个“Article(报刊上的文章-具体的出版物)”:
>>> a1 = Article(headline='Django lets you build Web apps easily')

#“公开出版物”被保存前,你不能关联它。
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: "<Article: Django lets you build Web apps easily>" needs to have a value for field "id" before this many-to-many relationship can be used.

#保存
>>> a1.save()

#用一个 Publication``来关联 ``Article:
>>> a1.publications.add(p1)

#创建另一个``Article``, 并且设置它的``Publications``
>>> a2 = Article(headline='NASA uses Python')
>>> a2.save()
>>> a2.publications.add(p1, p2)
>>> a2.publications.add(p3)
#
>>> new_publication = a2.publications.create(title='Highlights for Children')

#Article 对象可以访问于它们相关的 Publication 对象:
>>> a1.publications.all()
<QuerySet [<Publication: The Python Journal>]>
>>> a2.publications.all()
<QuerySet [<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>]>

#Publication 对象可以访问于它们相关的 Article 对象:
>>> p2.article_set.all()
<QuerySet [<Article: NASA uses Python>]>
>>> p1.article_set.all()
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Publication.objects.get(id=4).article_set.all()
<QuerySet [<Article: NASA uses Python>]>

#可通过 跨关联查询 查询多对多关联:
>>> Article.objects.filter(publications__id=1)
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Article.objects.filter(publications__pk=1)
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Article.objects.filter(publications=1)
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Article.objects.filter(publications=p1)
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>

>>> Article.objects.filter(publications__title__startswith="Science")
<QuerySet [<Article: NASA uses Python>, <Article: NASA uses Python>]>

>>> Article.objects.filter(publications__title__startswith="Science").distinct()
<QuerySet [<Article: NASA uses Python>]>

#count()计算数据条数函数也可以配合 distinct()去重 。
>>> Article.objects.filter(publications__title__startswith="Science").count()
2
>>> Article.objects.filter(publications__title__startswith="Science").distinct().count()
1
>>> Article.objects.filter(publications__in=[1,2]).distinct()
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Article.objects.filter(publications__in=[p1,p2]).distinct()
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>

#支持反向m2m查询
>>> Publication.objects.filter(id=1)
>>> Publication.objects.filter(article__id=1)
>>> Publication.objects.filter(article__in=[1,2]).distinct()

#排除查询
>>> Article.objects.exclude(publications=p2)

#通过m2m的另一端添加:
>>> a4 = Article(headline='NASA finds intelligent life on Earth')
>>> a4.save()
>>> p2.article_set.add(a4)
>>> p2.article_set.all()
#使用关键字通过另一端添加:
>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders')
>>> p2.article_set.all()
<QuerySet [<Article: NASA finds intelligent life on Earth>, <Article: Oxygen-free diet works wonders>]>
>>> a5 = p2.article_set.all()[1]
>>> a5.publications.all()

#从 Article 中移除 Publication:
>>> a4.publications.remove(p2)
>>> p2.article_set.all()
<QuerySet [<Article: Oxygen-free diet works wonders>]>
>>> a4.publications.all()
<QuerySet []>

#从另一端移除
>>> p2.article_set.remove(a5)
>>> p2.article_set.all()
<QuerySet []>
>>> a5.publications.all()
<QuerySet []>

#可以set()设置关系
>>> a4.publications.all()
<QuerySet [<Publication: Science News>]>
>>> a4.publications.set([p3])
>>> a4.publications.all()
<QuerySet [<Publication: Science Weekly>]>

#关系可以被清除
>>> p2.article_set.clear()
>>> p2.article_set.all()

#批量删除 Publications - 引用已经删除的出版者应该是:
>>> Publication.objects.filter(title__startswith='Science').delete()

django模型一对一关联

使用 OneToOneField 来定义一对一关系。就像使用其他类型的 Field 一样:在模型属性中包含它.

学习案例: 一个街道的门牌号地址只对应一家餐厅

from django.db import models
#地址模型
class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)
    def __str__(self):
        return "%s the place" % self.name
#Restaurant餐厅模型
class Restaurant(models.Model):
    place = models.OneToOneField( Place,on_delete=models.CASCADE, primary_key=True,)
    serves_hot_dogs = models.BooleanField(default=False)
    serves_pizza = models.BooleanField(default=False)
    def __str__(self):
        return "%s the restaurant" % self.place.name

django模型一对一查询

创建几个 Place
>>> p1 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> p1.save()
>>> p2 = Place(name='Ace Hardware', address='1013 N. Ashland')
>>> p2.save()

#创建一个 Restaurant。将 “父” 对象的 ID 传递为该对象的 ID:

>>> r = Restaurant(place=p1, serves_hot_dogs=True, serves_pizza=False)
>>> r.save()

#餐厅可以获取所在地:
>>> r.place
<Place: Demon Dogs the place>

#地点可以访问关联的餐厅(如果有的话):
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>

#p2 没有关联餐厅:
>>> from django.core.exceptions import ObjectDoesNotExist
>>> try:
>>>     p2.restaurant
>>> except ObjectDoesNotExist:
>>>     print("There is no restaurant here.")
There is no restaurant here.

#您还可以使用 hasattr 来免除异常捕获:
>>> hasattr(p2, 'restaurant')
False

#使用赋值符号来设置地方。因为地方是餐厅的主键,保存将创建一个新的餐馆:
>>> r.place = p2
>>> r.save()
>>> p2.restaurant
<Restaurant: Ace Hardware the restaurant>
>>> r.place
<Place: Ace Hardware the place>

#再次设置地方,使用相反方向的赋值:
>>> p1.restaurant = r
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>

#你可以用 跨关联查询 查询这些模型:
>>> Restaurant.objects.get(place=p1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.get(place__pk=1)
<Restaurant: Demon Dogs the restaurant>
>>> Restaurant.objects.filter(place__name__startswith="Demon")
<QuerySet [<Restaurant: Demon Dogs the restaurant>]>
>>> Restaurant.objects.exclude(place__address__contains="Ashland")
<QuerySet [<Restaurant: Demon Dogs the restaurant>]>

#方向查询
>>> Place.objects.get(pk=1)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__place=p1)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant=r)
<Place: Demon Dogs the place>
>>> Place.objects.get(restaurant__place__name__startswith="Demon")
<Place: Demon Dogs the place>

总结:本章节主要介绍了django的3中关联关系,和关联关系的创建,删除,通过关联关系查询与反向查询等内容。

登陆评论: 使用GITHUB登陆