本文主要给大家介绍关于Django外键赋值的相关内容,分享出来供大家参考学习,在开始之前,我们先来看一段代码:
1
2
3
4
5
6
7
8
9
|
class Article(models.Model):
title = models.CharField(max_length = 1024 , default = '')
...
def __str__( self ):
return 'Article pk:%d %s' % ( self .pk, self .title[: 30 ])
class ArticleContent(models.Model):
article = cached_fields.OneToOneField(Article)
...
|
写代码的的时候,发现了一个很奇怪的现象,当我给一个instance的外键(以_id结尾)赋值(数字)的时候 ,这个外键对应的instance的值并不会改变。
1
2
3
4
5
6
7
8
9
|
In [ 44 ]: ac = ArticleContent.objects.get(article_id = 14269 )
In [ 45 ]: ac.article_id
Out[ 45 ]: 14269
In [ 46 ]: ac.article_id = 14266
In [ 47 ]: ac.save()
In [ 48 ]: ac.article
Out[ 48 ]: <Article: Article pk: 14266 EC: Russia, Ukraine to Meet in >
In [ 49 ]: ac.article.pk
Out[ 49 ]: 14266
|
如上面的代码所示,为了找到答案,我翻了一下Django的源码:
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
31
32
33
34
35
36
37
38
39
40
41
|
django / db / models / fields / related_descriptors.py
def __get__( self , instance, cls = None ):
"""
Get the related instance through the forward relation.
With the example above, when getting ``child.parent``:
- ``self`` is the descriptor managing the ``parent`` attribute
- ``instance`` is the ``child`` instance
- ``cls`` is the ``Child`` class (we don't need it)
"""
if instance is None :
return self
# The related instance is loaded from the database and then cached in
# the attribute defined in self.cache_name. It can also be pre-cached
# by the reverse accessor (ReverseOneToOneDescriptor).
try :
rel_obj = getattr (instance, self .cache_name)
except AttributeError:
val = self .field.get_local_related_value(instance)
if None in val:
rel_obj = None
else :
qs = self .get_queryset(instance = instance)
qs = qs. filter ( self .field.get_reverse_related_filter(instance))
# Assuming the database enforces foreign keys, this won't fail.
rel_obj = qs.get()
# If this is a one-to-one relation, set the reverse accessor
# cache on the related object to the current instance to avoid
# an extra SQL query if it's accessed later on.
if not self .field.remote_field.multiple:
setattr (rel_obj, self .field.remote_field.get_cache_name(), instance)
setattr (instance, self .cache_name, rel_obj)
if rel_obj is None and not self .field.null:
raise self .RelatedObjectDoesNotExist(
"%s has no %s." % ( self .field.model.__name__, self .field.name)
)
else :
return rel_obj
|
注释得非常到位,当我们请求ac.article
的时候,会先去检查对应的cache(在这里是_article_cache
,感兴趣可以去看cache_name
的生成规则,就是外键名前面加下划线,后面加cache)存不存在,如果不存在那么就进行数据库请求,请求完之后会保存到cache中。
我们再看看__set__
,代码太长就不贴了(就在__get__
方法下面)。除了给外键字段(article
)赋值外,还会将pk字段(article_id
,是lh_field.attname
的值)设置为None,这样下次请求的时候就能拿到正确的值。
以上都是ForeignKey的Magic,而当我们给article_id
赋值的时候,只是在给一个普通的attribute赋值而已,没有任何magic,不会清理对应外键的cache,这时候拿到的instance仍然是cache中原来的那个instance。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。
原文链接:http://michaelyou.github.io/2016/12/19/Django外键赋值/