显然,关系型数据库的强大之处在于,表与表之间存在关联,Django提供了定义常见的三种类型的数据库关系的方法:many-to-one, many-to-many 和one-to-one.。
1.many-to-one
为了定义一个many-to-one的关系,使用django.db.models.ForeignKey.其中ForeignKey需要一个位置参数:model关联的类。举个例子:
如果一个Car model有一个制造商Manufacturer,就是说,一个Manufacturer制造很多cars但是呢,一个Car只有一个Manufacturer,使用下面方法定义:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
# ...
同样,你也可以创建一个递归的关系(就是说,一个对象many-to-one关系它自身),如下: models.ForeignKey(’self’, on_delete=models.CASCADE).
你也可以创建一个关系,其model尚未定义,你可以使用它的model名,而不是model对象自身。如下:
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass
这种技巧在多种场合下都有使用到,比如:
products/models.py
from django.db import models
class AbstractCar(models.Model):
manufacturer = models.ForeignKey('Manufacturer', on_delete=models.CASCADE)
class Meta:
abstract = True
production/models.py
from django.db import models
from products.models import AbstractCar
class Manufacturer(models.Model):
pass
class Car(AbstractCar):
pass
# Car.manufacturer will point to `production.Manufacturer` here.
为了引用定义在其他application中的model,你可以明确指定带有application标签的model.比如,如上一个制作商Manufacturer在另一个application名为production中定义,你可以这样引用:
class Car(models.Model):
manufacturer = models.ForeignKey(
'production.Manufacturer',
on_delete=models.CASCADE,
)
这种引用类型在两个application有关联时非常有用。一个数据库的索引是由ForeignKey自动创建的,你可以设置db_index的值为False来关闭这项设置。接下来,具体地看看ForeignKey类其他的一些参数:
- ForeignKey.on_delete
当一个被ForeignKey引用的对象删除后,django将会通过指定on_delete参数来仿真sql约束的行为。举例,假如你有一个可以为空的ForeignKey并且当引用类被删除时,你希望置空:
user = models.ForeignKey(
User,
models.SET_NULL,
blank=True,
null=True,
)
所有on_delete可能的值如下:
CASCADE,PROTECT,SET_NULL,SET_DEFAULT,SET(),DO_NOTHING
- ForeignKey.limit_choices_to
对该字段可用的选择设置一个限制,当该字段通过ModelForm或者admin进行渲染的时候。值为一个字段,或者Q对象。
staff_member = models.ForeignKey(
User,
on_delete=models.CASCADE,
limit_choices_to={'is_staff': True},
)
- ForeignKey.related_name
通过被关联的类访问自己的名称。同时也是related_query_name的默认值。注意,当定义抽象models的关联时,该值是必须要设置的。如果你不想django创建一个逆向的关联,设置该参数为'+'或者以'+'结尾。举例:
user = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='+',
)
这将确保,User model不会有一个逆向的关联到当前的model.
- ForeignKey.related_query_name
目标model反向过滤的名称,默认为related_name参数的值。如果related_name的值和它都没设置,该参数的默认值为model的名称.
class Tag(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name="tags",
related_query_name="tag",)
name = models.CharField(max_length=255)
Article.objects.filter(tag__name="important")
Declare the ForeignKey with related_query_name,That's now the name of the reverse filter
- ForeignKey.db_constraint
控制是否在数据库中创建一个通外键的约束,默认值为True.设置为False,对数据库数据的完整性非常不利。如果设置为False,访问关联的类如果不存在就会报错。
基于上述描述,举一个many-to-one相关的完整的例子:
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): # __unicode__ on Python 2
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)
def __str__(self): # __unicode__ on Python 2
return self.headline
class Meta:
ordering = ('headline',)
具体python api接口使用的情景:
r = Reporter(first_name='John', last_name='Smith', email='[email protected]')
r.save()
r2 = Reporter(first_name='Paul', last_name='Jones', email='[email protected]')
r2.save()
from datetime import date
a = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
a.save()
a.reporter.id
1
a.reporter
r3 = Reporter(first_name='John', last_name='Smith', email='[email protected]')
Article.objects.create(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r3)
Traceback (most recent call last):
...
ValueError: save() prohibited to prevent data loss due to unsaved related object 'reporter'.
new_article
new_article.reporter
new_article.reporter.id
1```
new_article2 = Article(headline="Paul's story", pub_date=date(2006, 1, 17))
r.article_set.add(new_article2)
new_article2.reporter
new_article2.reporter.id
1
r.article_set.all()
```r2.article_set.add(new_article2)
new_article2.reporter.id
2
new_article2.reporter
```
r.article_set.add(r2)
Traceback (most recent call last):
...
TypeError: 'Article' instance expected
r.article_set.all()
r2.article_set.all()
r.article_set.count()
2
r2.article_set.count()
1
r.article_set.filter(headline__startswith='This')
Article.objects.filter(reporter__first_name='John')
Article.objects.filter(reporter__first_name='John')
Article.objects.filter(reporter__first_name='John', reporter__last_name='Smith')
Article.objects.filter(reporter__pk=1)
Article.objects.filter(reporter=1)
Article.objects.filter(reporter=r)
Article.objects.filter(reporter__in=[1,2]).distinct()
Article.objects.filter(reporter__in=[r,r2]).distinct()
```Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John')).distinct()
, ]>
Reporter.objects.filter(article__pk=1)
]>
Reporter.objects.filter(article=1)
]>
Reporter.objects.filter(article=a)
]>
Reporter.objects.filter(article__headline__startswith='This')
, , ]>
Reporter.objects.filter(article__headline__startswith='This').distinct()
]>
Reporter.objects.filter(article__headline__startswith='This').count()
3
Reporter.objects.filter(article__headline__startswith='This').distinct().count()
1
Reporter.objects.filter(article__reporter__first_name__startswith='John')
, , , ]>
Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct()
]>
Reporter.objects.filter(article__reporter=r).distinct()
]>
Article.objects.all()
, , ]>
Reporter.objects.order_by('first_name')
, ]>
r2.delete()
Article.objects.all()
, ]>
Reporter.objects.order_by('first_name')
]>
Reporter.objects.filter(article__headline__startswith='This').delete()
Reporter.objects.all()
Article.objects.all()