-
Notifications
You must be signed in to change notification settings - Fork 0
/
django-modelyuan-ma-jie-xi.html
414 lines (345 loc) · 43 KB
/
django-modelyuan-ma-jie-xi.html
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="CatsAction" />
<meta property="og:type" content="article" />
<meta name="twitter:card" content="summary">
<meta name="keywords" content="Django, Orm, Python, " />
<meta property="og:title" content="Django Model源码解析 "/>
<meta property="og:url" content="./django-modelyuan-ma-jie-xi.html" />
<meta property="og:description" content="Django是Python最流行的Web框架,功能强大。虽然学习入门门槛较高,但是后期使用起来,避免了很多不必要的造轮子。 其中ORM是Django最主要的特点之一,它将数据库表定义映射为Python对象,避免了SQL语句的字符串编码,使代码变得清晰且易于维护。 Django的ORM代码,是Python元类编程的很好应用,下面一起看看。 from django.db import models class Student(models.Model): name = models.CharField(max_length=20) age = models.SmallIntegerField() 上面代码是定义表的基本例子。我们继承models.Model对象,models.Model对象部分源码如下 class Model(metaclass=ModelBase): def __init__(self, *args, **kwargs): # Alias some things as …" />
<meta property="og:site_name" content="CatsAction's blog" />
<meta property="og:article:author" content="CatsAction" />
<meta property="og:article:published_time" content="2018-04-08T00:00:00+08:00" />
<meta name="twitter:title" content="Django Model源码解析 ">
<meta name="twitter:description" content="Django是Python最流行的Web框架,功能强大。虽然学习入门门槛较高,但是后期使用起来,避免了很多不必要的造轮子。 其中ORM是Django最主要的特点之一,它将数据库表定义映射为Python对象,避免了SQL语句的字符串编码,使代码变得清晰且易于维护。 Django的ORM代码,是Python元类编程的很好应用,下面一起看看。 from django.db import models class Student(models.Model): name = models.CharField(max_length=20) age = models.SmallIntegerField() 上面代码是定义表的基本例子。我们继承models.Model对象,models.Model对象部分源码如下 class Model(metaclass=ModelBase): def __init__(self, *args, **kwargs): # Alias some things as …">
<title>Django Model源码解析 · CatsAction's blog
</title>
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet">
<link rel="icon" type="image/x-icon" href="./theme/image/logo.ico" />
<link rel="stylesheet" type="text/css" href="./theme/css/elegant.prod.css" media="screen">
<link rel="stylesheet" type="text/css" href="./theme/css/custom.css" media="screen">
</head>
<body>
<div id="content">
<div class="navbar navbar-static-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="./"><span class=site-name>CatsAction's blog</span></a>
<div class="nav-collapse collapse">
<ul class="nav pull-right top-menu">
<li >
<a href=
.
>Home</a>
</li>
<li ><a href="./categories.html">Categories</a></li>
<li ><a href="./tags.html">Tags</a></li>
<li ><a href="./archives.html">Archives</a></li>
<li><form class="navbar-search" action="./search.html" onsubmit="return validateForm(this.elements['q'].value);"> <input type="text" class="search-query" placeholder="Search" name="q" id="tipue_search_input"></form></li>
</ul>
</div>
</div>
</div>
</div>
<div class="container-fluid">
<div class="row-fluid">
<div class="span1"></div>
<div class="span10">
<article itemscope>
<div class="row-fluid">
<header class="page-header span10 offset2">
<h1>
<a href="./django-modelyuan-ma-jie-xi.html">
Django Model源码解析
</a>
</h1>
</header>
</div>
<div class="row-fluid">
<div class="span8 offset2 article-content">
<p>Django是Python最流行的Web框架,功能强大。虽然学习入门门槛较高,但是后期使用起来,避免了很多不必要的造轮子。
其中ORM是Django最主要的特点之一,它将数据库表定义映射为Python对象,避免了SQL语句的字符串编码,使代码变得清晰且易于维护。
Django的ORM代码,是Python元类编程的很好应用,下面一起看看。</p>
<div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db</span> <span class="kn">import</span> <span class="n">models</span>
<span class="k">class</span> <span class="nc">Student</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span>
<span class="n">name</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">CharField</span><span class="p">(</span><span class="n">max_length</span><span class="o">=</span><span class="mi">20</span><span class="p">)</span>
<span class="n">age</span> <span class="o">=</span> <span class="n">models</span><span class="o">.</span><span class="n">SmallIntegerField</span><span class="p">()</span>
</pre></div>
<p>上面代码是定义表的基本例子。我们继承<code>models.Model</code>对象,<code>models.Model</code>对象部分源码如下</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Model</span><span class="p">(</span><span class="n">metaclass</span><span class="o">=</span><span class="n">ModelBase</span><span class="p">):</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="c1"># Alias some things as locals to avoid repeat global lookups</span>
<span class="bp">cls</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span>
<span class="n">opts</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_meta</span>
<span class="n">_setattr</span> <span class="o">=</span> <span class="nb">setattr</span>
<span class="n">_DEFERRED</span> <span class="o">=</span> <span class="n">DEFERRED</span>
<span class="n">pre_init</span><span class="o">.</span><span class="n">send</span><span class="p">(</span><span class="n">sender</span><span class="o">=</span><span class="bp">cls</span><span class="p">,</span> <span class="n">args</span><span class="o">=</span><span class="n">args</span><span class="p">,</span> <span class="n">kwargs</span><span class="o">=</span><span class="n">kwargs</span><span class="p">)</span>
<span class="c1"># Set up the storage for instance state</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_state</span> <span class="o">=</span> <span class="n">ModelState</span><span class="p">()</span>
<span class="c1"># There is a rather weird disparity here; if kwargs, it's set, then args</span>
<span class="c1"># overrides it. It should be one or the other; don't duplicate the work</span>
<span class="c1"># The reason for the kwargs check is that standard iterator passes in by</span>
<span class="c1"># args, and instantiation for iteration is 33% faster.</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">args</span><span class="p">)</span> <span class="o">></span> <span class="nb">len</span><span class="p">(</span><span class="n">opts</span><span class="o">.</span><span class="n">concrete_fields</span><span class="p">):</span>
<span class="c1"># Daft, but matches old exception sans the err msg.</span>
<span class="k">raise</span> <span class="ne">IndexError</span><span class="p">(</span><span class="s2">"Number of args exceeds number of fields"</span><span class="p">)</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">kwargs</span><span class="p">:</span>
<span class="n">fields_iter</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">opts</span><span class="o">.</span><span class="n">concrete_fields</span><span class="p">)</span>
<span class="c1"># The ordering of the zip calls matter - zip throws StopIteration</span>
<span class="c1"># when an iter throws it. So if the first iter throws it, the second</span>
<span class="c1"># is *not* consumed. We rely on this, so don't change the order</span>
<span class="c1"># without changing the logic.</span>
<span class="k">for</span> <span class="n">val</span><span class="p">,</span> <span class="n">field</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">fields_iter</span><span class="p">):</span>
<span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="n">_DEFERRED</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">_setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1"># Slower, kwargs-ready version. fields_iter = iter(opts.fields)</span>
<span class="k">for</span> <span class="n">val</span><span class="p">,</span> <span class="n">field</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">args</span><span class="p">,</span> <span class="n">fields_iter</span><span class="p">):</span>
<span class="k">if</span> <span class="n">val</span> <span class="ow">is</span> <span class="n">_DEFERRED</span><span class="p">:</span>
<span class="k">continue</span>
<span class="n">_setattr</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">attname</span><span class="p">,</span> <span class="n">val</span><span class="p">)</span>
<span class="n">kwargs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="n">field</span><span class="o">.</span><span class="n">name</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
</pre></div>
<p>可以看到<code>Model</code>的定义使用了元类<code>ModelBase</code>。<code>ModelBase</code>主要处理表映射的Fields字段,具体怎么处理,后面再具体分析。所谓元类,就是普通类的类,也就是普通类是元类的实例。
可以用<code>类名.__class__</code>查看普通类的元类,默认元类是<code>type</code>。《流畅的Python》第21章很透彻的分析了元类,下面这幅图是里面的一张插图,很好的表示了元类与普通类的关系。</p>
<p><img alt="Python元类图" src="./images/meta_class.jpg"></p>
<p>我们回来,继续看<code>models.Model</code>源码。
我们看到<code>__init__</code>初始化方法第二行<code>opts=self._meta</code>。这个<code>_meta</code>属性,也是通过元类<code>ModelBase</code>附加给<code>Model</code>类的。它是一个<code>options.Options</code>对象,是管理ORM的核心。
<code>__init__</code>方法剩余的代码主要是处理域和值的映射关系,也是依赖<code>_meta</code>属性管理。完整的代码这里就不粘贴了。
<code>Model</code>对象除了<code>__init__</code>方法外,还有负责验证的<code>clean_fields</code>, <code>clean</code>, <code>validate_unique</code>以及负责保存对象到库的<code>save_base</code>方法,也都依赖<code>_meta</code>属性。
下面是<code>save_base</code>的代码,它通过<code>_meta</code>处理proxy,parents,auto_created,db_name然后创建或更新数据库表。</p>
<div class="highlight"><pre><span></span> <span class="k">def</span> <span class="nf">save_base</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">raw</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">force_insert</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span>
<span class="n">force_update</span><span class="o">=</span><span class="kc">False</span><span class="p">,</span> <span class="n">using</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="n">update_fields</span><span class="o">=</span><span class="kc">None</span><span class="p">):</span>
<span class="sd">"""</span>
<span class="sd"> Handle the parts of saving which should be done only once per save,</span>
<span class="sd"> yet need to be done in raw saves, too. This includes some sanity</span>
<span class="sd"> checks and signal sending.</span>
<span class="sd"> The 'raw' argument is telling save_base not to save any parent</span>
<span class="sd"> models and not to do any changes to the values before save. This</span>
<span class="sd"> is used by fixture loading.</span>
<span class="sd"> """</span>
<span class="n">using</span> <span class="o">=</span> <span class="n">using</span> <span class="ow">or</span> <span class="n">router</span><span class="o">.</span><span class="n">db_for_write</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span><span class="p">,</span> <span class="n">instance</span><span class="o">=</span><span class="bp">self</span><span class="p">)</span>
<span class="k">assert</span> <span class="ow">not</span> <span class="p">(</span><span class="n">force_insert</span> <span class="ow">and</span> <span class="p">(</span><span class="n">force_update</span> <span class="ow">or</span> <span class="n">update_fields</span><span class="p">))</span>
<span class="k">assert</span> <span class="n">update_fields</span> <span class="ow">is</span> <span class="kc">None</span> <span class="ow">or</span> <span class="n">update_fields</span>
<span class="bp">cls</span> <span class="o">=</span> <span class="n">origin</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="vm">__class__</span>
<span class="c1"># Skip proxies, but keep the origin as the proxy model.</span>
<span class="k">if</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">proxy</span><span class="p">:</span>
<span class="bp">cls</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">concrete_model</span>
<span class="n">meta</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">_meta</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">meta</span><span class="o">.</span><span class="n">auto_created</span><span class="p">:</span>
<span class="n">pre_save</span><span class="o">.</span><span class="n">send</span><span class="p">(</span>
<span class="n">sender</span><span class="o">=</span><span class="n">origin</span><span class="p">,</span> <span class="n">instance</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">raw</span><span class="o">=</span><span class="n">raw</span><span class="p">,</span> <span class="n">using</span><span class="o">=</span><span class="n">using</span><span class="p">,</span>
<span class="n">update_fields</span><span class="o">=</span><span class="n">update_fields</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># A transaction isn't needed if one query is issued.</span>
<span class="k">if</span> <span class="n">meta</span><span class="o">.</span><span class="n">parents</span><span class="p">:</span>
<span class="n">context_manager</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">atomic</span><span class="p">(</span><span class="n">using</span><span class="o">=</span><span class="n">using</span><span class="p">,</span> <span class="n">savepoint</span><span class="o">=</span><span class="kc">False</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">context_manager</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="n">mark_for_rollback_on_error</span><span class="p">(</span><span class="n">using</span><span class="o">=</span><span class="n">using</span><span class="p">)</span>
<span class="k">with</span> <span class="n">context_manager</span><span class="p">:</span>
<span class="n">parent_inserted</span> <span class="o">=</span> <span class="kc">False</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">raw</span><span class="p">:</span>
<span class="n">parent_inserted</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_save_parents</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">using</span><span class="p">,</span> <span class="n">update_fields</span><span class="p">)</span>
<span class="n">updated</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">_save_table</span><span class="p">(</span>
<span class="n">raw</span><span class="p">,</span> <span class="bp">cls</span><span class="p">,</span> <span class="n">force_insert</span> <span class="ow">or</span> <span class="n">parent_inserted</span><span class="p">,</span>
<span class="n">force_update</span><span class="p">,</span> <span class="n">using</span><span class="p">,</span> <span class="n">update_fields</span><span class="p">,</span>
<span class="p">)</span>
<span class="c1"># Store the database on which the object was saved</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_state</span><span class="o">.</span><span class="n">db</span> <span class="o">=</span> <span class="n">using</span>
<span class="c1"># Once saved, this is no longer a to-be-added instance.</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_state</span><span class="o">.</span><span class="n">adding</span> <span class="o">=</span> <span class="kc">False</span>
<span class="c1"># Signal that the save is complete</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">meta</span><span class="o">.</span><span class="n">auto_created</span><span class="p">:</span>
<span class="n">post_save</span><span class="o">.</span><span class="n">send</span><span class="p">(</span>
<span class="n">sender</span><span class="o">=</span><span class="n">origin</span><span class="p">,</span> <span class="n">instance</span><span class="o">=</span><span class="bp">self</span><span class="p">,</span> <span class="n">created</span><span class="o">=</span><span class="p">(</span><span class="ow">not</span> <span class="n">updated</span><span class="p">),</span>
<span class="n">update_fields</span><span class="o">=</span><span class="n">update_fields</span><span class="p">,</span> <span class="n">raw</span><span class="o">=</span><span class="n">raw</span><span class="p">,</span> <span class="n">using</span><span class="o">=</span><span class="n">using</span><span class="p">,</span>
<span class="p">)</span>
</pre></div>
<p>简单看了<code>model.Model</code>的源码后,我们接下来简单分析下<code>ModelBase</code>代码。</p>
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ModelBase</span><span class="p">(</span><span class="nb">type</span><span class="p">):</span>
<span class="sd">"""Metaclass for all models."""</span>
<span class="k">def</span> <span class="fm">__new__</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
<span class="n">super_new</span> <span class="o">=</span> <span class="nb">super</span><span class="p">()</span><span class="o">.</span><span class="fm">__new__</span>
<span class="c1"># Also ensure initialization is only performed for subclasses of Model</span>
<span class="c1"># (excluding Model class itself).</span>
<span class="n">parents</span> <span class="o">=</span> <span class="p">[</span><span class="n">b</span> <span class="k">for</span> <span class="n">b</span> <span class="ow">in</span> <span class="n">bases</span> <span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">b</span><span class="p">,</span> <span class="n">ModelBase</span><span class="p">)]</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">parents</span><span class="p">:</span>
<span class="k">return</span> <span class="n">super_new</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">attrs</span><span class="p">)</span>
<span class="c1"># Create the class.</span>
<span class="n">module</span> <span class="o">=</span> <span class="n">attrs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s1">'__module__'</span><span class="p">)</span>
<span class="n">new_attrs</span> <span class="o">=</span> <span class="p">{</span><span class="s1">'__module__'</span><span class="p">:</span> <span class="n">module</span><span class="p">}</span>
<span class="n">classcell</span> <span class="o">=</span> <span class="n">attrs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s1">'__classcell__'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="k">if</span> <span class="n">classcell</span> <span class="ow">is</span> <span class="ow">not</span> <span class="kc">None</span><span class="p">:</span>
<span class="n">new_attrs</span><span class="p">[</span><span class="s1">'__classcell__'</span><span class="p">]</span> <span class="o">=</span> <span class="n">classcell</span>
<span class="n">attr_meta</span> <span class="o">=</span> <span class="n">attrs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s1">'Meta'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="c1"># Pass all attrs without a (Django-specific) contribute_to_class()</span>
<span class="c1"># method to type.__new__() so that they're properly initialized</span>
<span class="c1"># (i.e. __set_name__()).</span>
<span class="n">contributable_attrs</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">obj_name</span><span class="p">,</span> <span class="n">obj</span> <span class="ow">in</span> <span class="nb">list</span><span class="p">(</span><span class="n">attrs</span><span class="o">.</span><span class="n">items</span><span class="p">()):</span>
<span class="k">if</span> <span class="n">_has_contribute_to_class</span><span class="p">(</span><span class="n">obj</span><span class="p">):</span>
<span class="n">contributable_attrs</span><span class="p">[</span><span class="n">obj_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">new_attrs</span><span class="p">[</span><span class="n">obj_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">obj</span>
<span class="n">new_class</span> <span class="o">=</span> <span class="n">super_new</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">new_attrs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="n">abstract</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">attr_meta</span><span class="p">,</span> <span class="s1">'abstract'</span><span class="p">,</span> <span class="kc">False</span><span class="p">)</span>
<span class="n">meta</span> <span class="o">=</span> <span class="n">attr_meta</span> <span class="ow">or</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">new_class</span><span class="p">,</span> <span class="s1">'Meta'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">base_meta</span> <span class="o">=</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">new_class</span><span class="p">,</span> <span class="s1">'_meta'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="n">app_label</span> <span class="o">=</span> <span class="kc">None</span>
<span class="c1"># Look for an application configuration to attach the model to.</span>
<span class="n">app_config</span> <span class="o">=</span> <span class="n">apps</span><span class="o">.</span><span class="n">get_containing_app_config</span><span class="p">(</span><span class="n">module</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">getattr</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="s1">'app_label'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="n">app_config</span> <span class="ow">is</span> <span class="kc">None</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">abstract</span><span class="p">:</span>
<span class="k">raise</span> <span class="ne">RuntimeError</span><span class="p">(</span>
<span class="s2">"Model class </span><span class="si">%s</span><span class="s2">.</span><span class="si">%s</span><span class="s2"> doesn't declare an explicit "</span>
<span class="s2">"app_label and isn't in an application in "</span>
<span class="s2">"INSTALLED_APPS."</span> <span class="o">%</span> <span class="p">(</span><span class="n">module</span><span class="p">,</span> <span class="n">name</span><span class="p">)</span>
<span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">app_label</span> <span class="o">=</span> <span class="n">app_config</span><span class="o">.</span><span class="n">label</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">add_to_class</span><span class="p">(</span><span class="s1">'_meta'</span><span class="p">,</span> <span class="n">Options</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="n">app_label</span><span class="p">))</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">abstract</span><span class="p">:</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">add_to_class</span><span class="p">(</span>
<span class="s1">'DoesNotExist'</span><span class="p">,</span>
<span class="n">subclass_exception</span><span class="p">(</span>
<span class="s1">'DoesNotExist'</span><span class="p">,</span>
<span class="nb">tuple</span><span class="p">(</span>
<span class="n">x</span><span class="o">.</span><span class="n">DoesNotExist</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">parents</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s1">'_meta'</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">x</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">abstract</span>
<span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">ObjectDoesNotExist</span><span class="p">,),</span>
<span class="n">module</span><span class="p">,</span>
<span class="n">attached_to</span><span class="o">=</span><span class="n">new_class</span><span class="p">))</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">add_to_class</span><span class="p">(</span>
<span class="s1">'MultipleObjectsReturned'</span><span class="p">,</span>
<span class="n">subclass_exception</span><span class="p">(</span>
<span class="s1">'MultipleObjectsReturned'</span><span class="p">,</span>
<span class="nb">tuple</span><span class="p">(</span>
<span class="n">x</span><span class="o">.</span><span class="n">MultipleObjectsReturned</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">parents</span> <span class="k">if</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="s1">'_meta'</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">x</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">abstract</span>
<span class="p">)</span> <span class="ow">or</span> <span class="p">(</span><span class="n">MultipleObjectsReturned</span><span class="p">,),</span>
<span class="n">module</span><span class="p">,</span>
<span class="n">attached_to</span><span class="o">=</span><span class="n">new_class</span><span class="p">))</span>
<span class="k">if</span> <span class="n">base_meta</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">base_meta</span><span class="o">.</span><span class="n">abstract</span><span class="p">:</span>
<span class="c1"># Non-abstract child classes inherit some attributes from their</span>
<span class="c1"># non-abstract parent (unless an ABC comes before it in the</span>
<span class="c1"># method resolution order).</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="s1">'ordering'</span><span class="p">):</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">ordering</span> <span class="o">=</span> <span class="n">base_meta</span><span class="o">.</span><span class="n">ordering</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="s1">'get_latest_by'</span><span class="p">):</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">get_latest_by</span> <span class="o">=</span> <span class="n">base_meta</span><span class="o">.</span><span class="n">get_latest_by</span>
<span class="n">is_proxy</span> <span class="o">=</span> <span class="n">new_class</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">proxy</span>
</pre></div>
<p>首先<code>ModelBase</code>继承<code>type</code>,所有的元类都继承<code>type</code>。接下来<code>__new__</code>方法,它才是Python真正的构造函数。由于普通类是元类的实例,当定义<code>Model</code>对象时,<code>ModelBase</code>的<code>__new__</code>方法将被调用。
注意是定义时,而不是调用时。就是类体本身属性和方法的初始化,都会有对应元类的<code>__new__</code>方法构建。<code>__new__</code>是一个类方法,接受的三个位置参数分别是要创建的类类名,继承的基类对象元组,以及类所属属性的映射。
下面是<code>__new__</code>部分代码注释。</p>
<div class="highlight"><pre><span></span><span class="c1"># Meta属性会再赋值给__meta</span>
<span class="n">attr_meta</span> <span class="o">=</span> <span class="n">attrs</span><span class="o">.</span><span class="n">pop</span><span class="p">(</span><span class="s1">'Meta'</span><span class="p">,</span> <span class="kc">None</span><span class="p">)</span>
<span class="c1"># 调用父类的__new__创建类对象,也就是Model对象</span>
<span class="n">new_class</span> <span class="o">=</span> <span class="n">super_new</span><span class="p">(</span><span class="bp">cls</span><span class="p">,</span> <span class="n">name</span><span class="p">,</span> <span class="n">bases</span><span class="p">,</span> <span class="n">new_attrs</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
<span class="c1"># 给Model增加_meta属性, 转化成了Options对象</span>
<span class="n">new_class</span><span class="o">.</span><span class="n">add_to_class</span><span class="p">(</span><span class="s1">'_meta'</span><span class="p">,</span> <span class="n">Options</span><span class="p">(</span><span class="n">meta</span><span class="p">,</span> <span class="n">app_label</span><span class="p">))</span>
<span class="c1"># 处理抽象父Model对应的域</span>
<span class="k">for</span> <span class="n">base</span> <span class="ow">in</span> <span class="nb">reversed</span><span class="p">([</span><span class="n">new_class</span><span class="p">]</span> <span class="o">+</span> <span class="n">parents</span><span class="p">):</span>
<span class="c1"># Conceptually equivalent to `if base is Model`.</span>
<span class="k">if</span> <span class="ow">not</span> <span class="nb">hasattr</span><span class="p">(</span><span class="n">base</span><span class="p">,</span> <span class="s1">'_meta'</span><span class="p">):</span>
<span class="k">continue</span>
<span class="c1"># Skip concrete parent classes.</span>
<span class="k">if</span> <span class="n">base</span> <span class="o">!=</span> <span class="n">new_class</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">base</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">abstract</span><span class="p">:</span>
<span class="k">continue</span>
<span class="c1"># Locate OneToOneField instances.</span>
<span class="k">for</span> <span class="n">field</span> <span class="ow">in</span> <span class="n">base</span><span class="o">.</span><span class="n">_meta</span><span class="o">.</span><span class="n">local_fields</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">field</span><span class="p">,</span> <span class="n">OneToOneField</span><span class="p">):</span>
<span class="n">related</span> <span class="o">=</span> <span class="n">resolve_relation</span><span class="p">(</span><span class="n">new_class</span><span class="p">,</span> <span class="n">field</span><span class="o">.</span><span class="n">remote_field</span><span class="o">.</span><span class="n">model</span><span class="p">)</span>
<span class="n">parent_links</span><span class="p">[</span><span class="n">make_model_tuple</span><span class="p">(</span><span class="n">related</span><span class="p">)]</span> <span class="o">=</span> <span class="n">field</span>
<span class="c1"># 给Model对象追加objects作为manager</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">opts</span><span class="o">.</span><span class="n">managers</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">f</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s1">'objects'</span> <span class="k">for</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">opts</span><span class="o">.</span><span class="n">fields</span><span class="p">):</span>
<span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span>
<span class="s2">"Model </span><span class="si">%s</span><span class="s2"> must specify a custom Manager, because it has a "</span>
<span class="s2">"field named 'objects'."</span> <span class="o">%</span> <span class="bp">cls</span><span class="o">.</span><span class="vm">__name__</span>
<span class="p">)</span>
<span class="n">manager</span> <span class="o">=</span> <span class="n">Manager</span><span class="p">()</span>
<span class="n">manager</span><span class="o">.</span><span class="n">auto_created</span> <span class="o">=</span> <span class="kc">True</span>
<span class="bp">cls</span><span class="o">.</span><span class="n">add_to_class</span><span class="p">(</span><span class="s1">'objects'</span><span class="p">,</span> <span class="n">manager</span><span class="p">)</span>
</pre></div>
<p>可以看到<code>ModelBase</code>主要是给<code>Model</code>对象追加<code>_meta</code>属性及objects manager,并利用<code>Options</code>对象校验Model和父Model域定义。<code>Options</code>代码这里不再分析,明天写写<code>Model</code>的<code>Field</code>对象。</p>
<hr/>
</div>
<section id="article-sidebar" class="span2">
<h4>Published</h4>
<time itemprop="dateCreated" datetime="2018-04-08T00:00:00+08:00"> 4 8, 2018</time>
<!---->
<h4>Category</h4>
<a class="category-link" href="./categories.html#python-ref">Python</a>
<h4>Tags</h4>
<ul class="list-of-tags tags-in-article">
<li><a href="./tags.html#django-ref">Django
<span>1</span>
</a></li>
<li><a href="./tags.html#orm-ref">Orm
<span>1</span>
</a></li>
</ul>
<h4>Contact</h4>
<div id="sidebar-social-link">
<a href="https://github.com/boyaziqi" title="" target="_blank" rel="nofollow noopener noreferrer">
<svg xmlns="http://www.w3.org/2000/svg" aria-label="GitHub" role="img" viewBox="0 0 512 512"><rect width="512" height="512" rx="15%" fill="#1B1817"/><path fill="#fff" d="M335 499c14 0 12 17 12 17H165s-2-17 12-17c13 0 16-6 16-12l-1-50c-71 16-86-28-86-28-12-30-28-37-28-37-24-16 1-16 1-16 26 2 40 26 40 26 22 39 59 28 74 22 2-17 9-28 16-35-57-6-116-28-116-126 0-28 10-51 26-69-3-6-11-32 3-67 0 0 21-7 70 26 42-12 86-12 128 0 49-33 70-26 70-26 14 35 6 61 3 67 16 18 26 41 26 69 0 98-60 120-117 126 10 8 18 24 18 48l-1 70c0 6 3 12 16 12z"/></svg>
</a>
</div>
</section>
</div>
</article>
</div>
<div class="span1"></div>
</div>
</div>
</div>
<footer>
<div id="fpowered">
Powered by: <a href="http://getpelican.com/" title="Pelican Home Page" target="_blank" rel="nofollow noopener noreferrer">Pelican</a>
Theme: <a href="https://elegant.oncrashreboot.com/" title="Theme Elegant Home Page" target="_blank" rel="nofollow noopener noreferrer">Elegant</a>
</div>
</footer> <script src="//code.jquery.com/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js"></script>
<script>
function validateForm(query)
{
return (query.length > 0);
}
</script>
<script>
(function () {
if (window.location.hash.match(/^#comment-\d+$/)) {
$('#comment_thread').collapse('show');
}
})();
window.onhashchange=function(){
if (window.location.hash.match(/^#comment-\d+$/))
window.location.reload(true);
}
$('#comment_thread').on('shown', function () {
var link = document.getElementById('comment-accordion-toggle');
var old_innerHTML = link.innerHTML;
$(link).fadeOut(200, function() {
$(this).text('Click here to hide comments').fadeIn(200);
});
$('#comment_thread').on('hidden', function () {
$(link).fadeOut(200, function() {
$(this).text(old_innerHTML).fadeIn(200);
});
})
})
</script>
</body>
<!-- Theme: Elegant built for Pelican
License : MIT -->
</html>