[model examples] Many-to-one relationships

2 views
Skip to first unread message

憨狗

unread,
Jun 26, 2006, 4:13:27 AM6/26/06
to djan...@googlegroups.com

多对一的关系

使用ForeignKey()来定义一个多对一的关系:即多篇文章可以是同一个记者写的,
反过来:一篇文章只能由一个记者来写。
实现的时候是在Article类中增加一个到Reporter的ForeignKey的列,而不是在
Reporter中增加一个到Article的 Foreignkey
的列——那样的话就相当于多个记者写一篇文章,他们怎么分稿费? :D


Model代码

from django.db import models

class Reporter(models.Model):
first_name = models.CharField(maxlength=30)
last_name = models.CharField(maxlength=30)
email = models.EmailField()

def __str__(self):
return "%s %s" % (self.first_name, self.last_name)

class Article(models.Model):
headline = models.CharField(maxlength=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter)

def __str__(self):
return self.headline

class Meta:
ordering = ('headline',)


API使用例子

上面的代码在 manyToOne/models.py中

>>> from manyToOne.models import Article, Reporter

# 新建几个个记者.
>>> r = Reporter(first_name='John', last_name='Smith', email='jo...@example.com')
>>> r.save()

>>> r2 = Reporter(first_name='Paul', last_name='Jones', email='pa...@example.com')
>>> r2.save()

# 创建一篇新的文章.
>>> from datetime import datetime
>>> a = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter=r)
>>> a.save()

>>> a.reporter.id
1

>>> a.reporter
<Reporter: John Smith>

# 文章对象可以直接访问它的记者:
>>> r = a.reporter
>>> r.first_name, r.last_name
('John', 'Smith')

# 直接使用记者对象来创建一篇文章
>>> new_article = r.article_set.create(headline="John's second story", pub_date=datetime(2005, 7, 29))
>>> new_article
<Article: John's second story>
>>> new_article.reporter.id
1

# 先创建一篇文章,然后署名成某个记者
>>> new_article2 = Article(headline="Paul's story", pub_date=datetime(2006, 1, 17))
>>> r.article_set.add(new_article2)
>>> new_article2.reporter.id
1
# John够勤快的,写了三篇文章了:
>>> r.article_set.all()
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]

# 算了,还是把nwe_article2重新署名成Paul吧:
>>> r2.article_set.add(new_article2)
>>> new_article2.reporter.id
2

# John现在只有两篇文章了!
>>> r.article_set.all()
[<Article: John's second story>, <Article: This is a test>]

# Paul终于有篇自己的文章了!
>>> r2.article_set.all()
[<Article: Paul's story>]

# 直接使用reporter可以给文章重新署名成John:
>>> new_article2.reporter = r
>>> new_article2.save()
>>> new_article2.reporter
<Reporter: John Smith>
>>> new_article2.reporter.id
1
>>> r.article_set.all()
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]

# 哦,Paul又成光杆司令了!一篇文章都没有
>>> r2.article_set.all()
[]

# 不行要多给Paul一些署名的机会,把那两篇文章都转到Paul的名下吧:
>>> r2.article_set = [new_article, new_article2]

# John只有一篇文章了!
>>> r.article_set.all()
[<Article: This is a test>]

# Paul再也不是光杆司令了!二是有两篇文章了!比John还多!
>>> r2.article_set.all()
[<Article: John's second story>, <Article: Paul's story>]

# 跳来跳去,文章始终要署名的,:D
# ForeignKey ,不能为空,必须给每篇文章分配一个记者,署上一个记者的名号
>>> r.article_set = [new_article]
>>> r.article_set.all()
[<Article: John's second story>, <Article: This is a test>]
>>> r2.article_set.all()
[<Article: Paul's story>]

# 文章的记者属性不能为空 - 即无clear 或者remove方法
>>> hasattr(r2.article_set, 'remove')
False
>>> hasattr(r2.article_set, 'clear')
False

# 记者对象可以访问和署有自己大名的文章对象,
# 自己肯定可以阅读自己发表的文章,
>>> r.article_set.all()
[<Article: John's second story>, <Article: This is a test>]

>>> r.article_set.filter(headline__startswith='This')
[<Article: This is a test>]

>>> r.article_set.count()
2

>>> r2.article_set.count()
1

# 通过id获取文章
>>> Article.objects.filter(id__exact=1)
[<Article: This is a test>]
>>> Article.objects.filter(pk=1)
[<Article: This is a test>]

# 查询文章属性
>>> Article.objects.filter(headline__startswith='This')
[<Article: This is a test>]

# 这些API会自动的把模型的层次关系关联起来,
# 使用双下划线来分隔这种层次关系,
# 层次的深度不限,完全取决于所需
# 查找所有署名记者是"John"的文章
>>> Article.objects.filter(reporter__first_name__exact='John')
[<Article: John's second story>, <Article: This is a test>]

# 通过关系进行两次查询
>>> Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
[<Article: John's second story>, <Article: This is a test>]

# 其实上面的查询,只对两个相关的表执行了一次join:
>>> query = Article.objects.filter(reporter__first_name__exact='John', reporter__last_name__exact='Smith')
>>> null, sql, null = query._get_sql_clause()
>>> sql.count('INNER JOIN')
1

# The automatically joined table has a predictable name.
>>> Article.objects.filter(reporter__first_name__exact='John').extra(where=["many_to_one_article__reporter.last_name='Smith'"])
[<Article: John's second story>, <Article: This is a test>]

# 找出所有记者id为1的文章:
>>> Article.objects.filter(reporter__id__exact=1)
[<Article: John's second story>, <Article: This is a test>]
>>> Article.objects.filter(reporter__pk=1)
[<Article: John's second story>, <Article: This is a test>]

# 在reporter和id之间是两个下划线,而不是一个:
>>> Article.objects.filter(reporter_id__exact=1)
Traceback (most recent call last):
...
TypeError: Cannot resolve keyword 'reporter_id' into field

# 必须正确指定比较的条件(id=1):
>>> Article.objects.filter(reporter_id=1)
Traceback (most recent call last):
...
TypeError: Cannot resolve keyword 'reporter_id' into field

# "pk" 这样的缩写语法也适用于关系型模型中.
>>> Article.objects.filter(reporter__pk=1)
[<Article: John's second story>, <Article: This is a test>]

# 除了可以通过Reporter实例化Article对象外,还可以通过传入Reporter id来实例化实例化一个Artice对象,
>>> a3 = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter_id=r.id)
>>> a3.save()
>>> a3.reporter.id
1
>>> a3.reporter
<Reporter: John Smith>

# 当然, reporter id也可以是一个字符串:
>>> a4 = Article(id=None, headline="This is a test", pub_date=datetime(2005, 7, 27), reporter_id="1")
>>> a4.save()
>>> a4.reporter
<Reporter: John Smith>

# 只翻箱倒柜的找了一天Article,其实Reporter也照样是可以查询的
>>> Reporter.objects.filter(id__exact=1)
[<Reporter: John Smith>]
>>> Reporter.objects.filter(pk=1)
[<Reporter: John Smith>]
>>> Reporter.objects.filter(first_name__startswith='John')
[<Reporter: John Smith>]

# Reporter也可以通过ForeignKey来反向查找对应的Article
# 接下来的几个例子有个技巧:如果你仅仅是本文开头的那个
# from manyToOne.models import Article, Reporter来导入模块的话,
# 会失败的,但是如果使用from app.manyToOne.models import Article, Reporter
# 来导入模块的话,就会成功
# 这方面的讨论在:
# http://groups.google.com/group/django-developers/browse_thread/thread/51ebb62cc40ff3f7
# http://groups.google.com/group/django-users/browse_thread/thread/210c4908fd9fe8ea
# 有讨论,也许以后会fix这个bug。
>>> Reporter.objects.filter(article__id__exact=1)
[<Reporter: John Smith>]
>>> Reporter.objects.filter(article__pk=1)
[<Reporter: John Smith>]
>>> Reporter.objects.filter(article__headline__startswith='This')
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
>>> Reporter.objects.filter(article__headline__startswith='This').distinct()
[<Reporter: John Smith>]

# 反向查询的时候联合使用distinct()和count()
>>> Reporter.objects.filter(article__headline__startswith='This').count()
3
>>> Reporter.objects.filter(article__headline__startswith='This').distinct().count()
1

# 查询中绕圈圈,小心头晕哦 :D:
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John')
[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>]
>>> Reporter.objects.filter(first_name__startswith='John')
[<Reporter: John Smith>]
>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
[<Reporter: John Smith>]

# 如果删除一个记者,他/她的文章也会被删除,
>>> Article.objects.all()
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>, <Article: This is a test>, <Article: This is a test>]
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>, <Reporter: Paul Jones>]
>>> r2.delete()
>>> Article.objects.all()
[<Article: John's second story>, <Article: This is a test>, <Article: This is a test>, <Article: This is a test>]
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>]

# 在使用的时候其实并不是这样的,需要先删除该记者的所有文章,
# 然后才能删除该记者,这是外键约束所致:

>>> r
<Reporter: John Smith>
>>> r2
<Reporter: Paul Jones>
>>> r2.delete()
Traceback (most recent call last):
File "<console>", line 1, in ?
raise errorclass, errorvalue
IntegrityError: (1217, 'Cannot delete or update a parent row: a foreign key constraint fails')
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>, <Reporter: Paul Jones>]
>>> r2.delete()
Traceback (most recent call last):
raise errorclass, errorvalue
IntegrityError: (1217, 'Cannot delete or update a parent row: a foreign key constraint fails')
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>, <Reporter: Paul Jones>]
>>> r2
<Reporter: Paul Jones>
>>> Article.objects.filter(reporter__first_name__startswith='Paul')
[<Article: Paul's story>]

# 通过在查询中使用join来删除该记者的所有文章,也很简单:
>>> Article.objects.filter(reporter__first_name__startswith='Paul').delete()
>>> Article.objects.filter(reporter__first_name__startswith='Paul')
[]
>>> r2.delete()
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>]
>>>

# 通过在查询中使用join来删除某些记录:
>>> Reporter.objects.filter(article__headline__startswith='This').delete()
>>> Reporter.objects.all()
[]
>>> Article.objects.all()
[]

Reply all
Reply to author
Forward
0 new messages