Django ForeignKey 字段详解

ForeignKey 字段的定义如下:
`class ForeignKey(othermodel, on_delete, **options)’
第一个参数是关联的对象,第二个on_delete指示删除时的动作。

on_delete 参数值

  1. CASCADE: 级联删除
  2. PROTECT: 阻止删除并提示 ProtectedError
  3. SET_NULL: 设置为NULL值
  4. SET_DEFAUL: 设置默认值
  5. SET(): 将传入SET方法的值作为值,传入的可以是一个函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models

    def get_sentinel_user():
    return get_user_model().objects.get_or_create(username='deleted')[0]

    class MyModel(models.Model):
    user = models.ForeignKey(
    settings.AUTH_USER_MODEL,
    on_delete=models.SET(get_sentinel_user),
    )
  6. DO_NOTHING: 无动作。
    如果不写,默认是CASCADE

limit_choice_to

对choice进行过滤,可以是字典也可以是一个Q查询组件,或者一个函数:

1
2
3
4
5
staff_member = models.ForeignKey(
User,
on_delete=models.CASCADE,
limit_choices_to={'is_staff': True},
)

函数:

1
2
3
4
def limit_pub_date_choices():
return {'pub_date__lte': datetime.date.utcnow()}

limit_choices_to = limit_pub_date_choices

related_name 和 related_query_name

当你的model中有两个或多个字段ForeignKey到同一个model,那么related_name必填。

1
2
3
4
class Meal(models.Model):
name = models.CharField('Name',max_length=10)
fruit = models.ForeignKey('Fruit')
fruit2 = models.ForeignKey('Fruit')

生成migrations的时候就会报:

1
2
3
4
5
ERRORS:
person.Meal.fruit2: (fields.E304) Reverse accessor for 'Meal.fruit2' clashes with reverse accessor for 'Meal.fruit'.
HINT: Add or change a related_name argument to the definition for 'Meal.fruit2' or 'Meal.fruit'.
person.Meal.fruit: (fields.E304) Reverse accessor for 'Meal.fruit' clashes with reverse accessor for 'Meal.fruit2'.
HINT: Add or change a related_name argument to the definition for 'Meal.fruit' or 'Meal.fruit2'.

的错误,因为不指明related_name,django区别不了这两个字段。所以,我们加上related_name:

1
2
3
4
class Meal(models.Model):
name = models.CharField('Name',max_length=10)
fruit = models.ForeignKey('Fruit',related_name='frist_meal')
fruit2 = models.ForeignKey('Fruit',related_name="second_meal")

数据库中,django自动为我们这两个字段起的名字是字段名+_id的形式,这里分别是fruit_id和fruit2_id,如果想自定义这个字段名,使用参数db_column。

related_query_name 默认值同 related_name,用于外键对象反查本对象。例如,我们可以通过如下的方式查询Meal:

1
2
3
4
5
6
7
8
from person.models import Fruit,Meal

apple = Fruit.objects.create(name="apple")
banana = Fruit.objects.create(name="banana")
m = Meal.objects.create(name="Big Meal",fruit=apple,fruit2=banana)

Fruit.objects.get(MoringFruit=m).name #apple
Fruit.objects.get(AfternoonFruit=m).name #banana

一般情况下,relate_name和related_query_name保持一致即可。但是在继承中一需要保证抽象父类和子类的名称不一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Meal(models.Model):
name = models.CharField('Name',max_length=10)
fruit = models.ForeignKey('Fruit',related_name='%(app_label)s_%(class)s_first_meal')
fruit2 = models.ForeignKey('Fruit',related_name="%(app_label)s_%(class)s_second_meal")

class Meta:
abstract = True

class Fruit(models.Model):
name = models.CharField('Name',max_length=10)

def __str__(self):
return self.name

class Breakfast(Meal):
food = models.CharField('Food',max_length=10,null=True)

class Lunch(Meal):
pass

其中app_label是app的名字,class是类名,最后组合出来的名称均用小写。例如:
person_breakfast_first_meal=br

有时候会遇见related_name=+或者以+号结尾,意思是禁止反查。

to_field

外键对象的关联字段,默认为id,如果指定了别的字段,该字段必须设置unique=True.

db_constraint

指定是否添加数据库限制,默认为True,一般不需要设置为False

swappable

保持默认即可,如果想知道什么是swappable models,看这里

你的支持我的动力