forked from bconstantin/django_polymorphic
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DOCS.html
756 lines (679 loc) · 35.2 KB
/
DOCS.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
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.6: http://docutils.sourceforge.net/" />
<title></title>
<style type="text/css">
h1, h2, h3, h4,
#table-of-contents
{
color: #47c;
}
h1 { padding-top: 15px; }
h2 { padding-top: 10px; }
h3 { padding-top: 7px; }
a:hover { border-bottom: 1px solid #0066cc; }
a {color: #0066cc; text-decoration: none;}
li {
padding-top: 5px;
padding-bottom: 5px;
}
tt {
color: #080;
}
blockquote tt {
color: #000
}
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
/*
a.toc-backref {
text-decoration: none ;
color: black }
*/
dd {
margin-bottom: 0.5em }
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt, pre.literal-block, pre.doctest-block {
font-size: 115%;
line-height: 150% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<div class="section" id="polymorphic-models-for-django">
<h1><a class="toc-backref" href="#id1">Polymorphic Models for Django</a></h1>
<div class="contents topic" id="table-of-contents">
<p class="topic-title first">Table of Contents</p>
<ul class="simple">
<li><a class="reference internal" href="#polymorphic-models-for-django" id="id1">Polymorphic Models for Django</a></li>
<li><a class="reference internal" href="#quickstart" id="id2">Quickstart</a></li>
<li><a class="reference internal" href="#list-of-features" id="id3">List of Features</a></li>
<li><a class="reference internal" href="#more-about-installation-testing" id="id4">More about Installation / Testing</a></li>
<li><a class="reference internal" href="#more-polymorphic-functionality" id="id5">More Polymorphic Functionality</a></li>
<li><a class="reference internal" href="#custom-managers-querysets-manager-inheritance" id="id6">Custom Managers, Querysets & Manager Inheritance</a></li>
<li><a class="reference internal" href="#performance-considerations" id="id7">Performance Considerations</a></li>
<li><a class="reference internal" href="#restrictions-caveats" id="id8">Restrictions & Caveats</a></li>
<li><a class="reference internal" href="#project-status" id="id9">Project Status</a></li>
<li><a class="reference internal" href="#links" id="id10">Links</a></li>
</ul>
</div>
</div>
<div class="section" id="quickstart">
<h1><a class="toc-backref" href="#id2">Quickstart</a></h1>
<div class="section" id="install">
<h2>Install</h2>
<p>After uncompressing (if necessary), in the directory "...django_polymorphic",
execute (on Unix-like systems):</p>
<pre class="literal-block">
sudo python setup.py install
</pre>
</div>
<div class="section" id="make-your-models-polymorphic">
<h2>Make Your Models Polymorphic</h2>
<p>Use <tt class="docutils literal">PolymorphicModel</tt> instead of Django's <tt class="docutils literal">models.Model</tt>, like so:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel
class Project(PolymorphicModel):
topic = models.CharField(max_length=30)
class ArtProject(Project):
artist = models.CharField(max_length=30)
class ResearchProject(Project):
supervisor = models.CharField(max_length=30)
</pre>
<p>All models inheriting from your polymorphic models will be polymorphic as well.</p>
</div>
<div class="section" id="create-some-objects">
<h2>Create some objects</h2>
<pre class="doctest-block">
>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")
</pre>
</div>
<div class="section" id="get-polymorphic-query-results">
<h2>Get polymorphic query results</h2>
<pre class="doctest-block">
>>> Project.objects.all()
[ <Project: id 1, topic "Department Party">,
<ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
</pre>
<p>use <tt class="docutils literal">instance_of</tt> or <tt class="docutils literal">not_instance_of</tt> for narrowing the result to specific subtypes:</p>
<pre class="doctest-block">
>>> Project.objects.instance_of(ArtProject)
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner"> ]
</pre>
<pre class="doctest-block">
>>> Project.objects.instance_of(ArtProject) | Project.objects.instance_of(ResearchProject)
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]
</pre>
<p>Polymorphic filtering: Get all projects where Mr. Turner is involved as an artist
or supervisor (note the three underscores):</p>
<pre class="doctest-block">
>>> Project.objects.filter( Q(ArtProject___artist = 'T. Turner') | Q(ResearchProject___supervisor = 'T. Turner') )
[ <ArtProject: id 2, topic "Painting with Tim", artist "T. Turner">,
<ResearchProject: id 4, topic "Color Use in Late Cubism", supervisor "T. Turner"> ]
</pre>
<p>This is basically all you need to know, as django_polymorphic mostly
works fully automatic and just delivers the expected ("pythonic") results.</p>
<p>Note: In all example output, above and below, for a nicer and more informative
output the <tt class="docutils literal">ShowFieldType</tt> mixin has been used (documented below).</p>
</div>
</div>
<div class="section" id="list-of-features">
<h1><a class="toc-backref" href="#id3">List of Features</a></h1>
<ul class="simple">
<li>Fully automatic - generally makes sure that the same objects are
returned from the database that were stored there, regardless how
they are retrieved</li>
<li>Only on models that request polymorphic behaviour (and the
models inheriting from them)</li>
<li>Full support for ForeignKeys, ManyToManyFields and OneToToneFields</li>
<li>Filtering for classes, equivalent to python's isinstance():
<tt class="docutils literal"><span class="pre">instance_of(...)</span></tt> and <tt class="docutils literal"><span class="pre">not_instance_of(...)</span></tt></li>
<li>Polymorphic filtering/ordering etc., allowing the use of fields of
derived models ("ArtProject___artist")</li>
<li>Support for user-defined custom managers</li>
<li>Automatic inheritance of custom managers</li>
<li>Support for user-defined custom queryset classes</li>
<li>Non-polymorphic queries if needed, with no other change in
features/behaviour</li>
<li>Combining querysets of different types/models ("qs3 = qs1 | qs2")</li>
<li>Nice/informative display of polymorphic queryset results</li>
</ul>
</div>
<div class="section" id="more-about-installation-testing">
<h1><a class="toc-backref" href="#id4">More about Installation / Testing</a></h1>
<div class="section" id="requirements">
<h2>Requirements</h2>
<p>Django 1.1 (or later) and Python 2.4 or later. This code has been tested
on Django 1.1 / 1.2 / 1.3 and Python 2.4.6 / 2.5.4 / 2.6.4 on Linux.</p>
</div>
<div class="section" id="included-test-suite">
<h2>Included Test Suite</h2>
<p>The repository (or tar file) contains a complete Django project
that may be used for tests or experiments, without any installation needed.</p>
<p>To run the included test suite, in the directory "...django_polymorphic" execute:</p>
<pre class="literal-block">
./manage test polymorphic
</pre>
<p>The management command <tt class="docutils literal">pcmd.py</tt> in the app <tt class="docutils literal">pexp</tt> can be used
for quick tests or experiments - modify this file (pexp/management/commands/pcmd.py)
to your liking, then run:</p>
<pre class="literal-block">
./manage syncdb # db is created in /var/tmp/... (settings.py)
./manage pcmd
</pre>
</div>
<div class="section" id="installation">
<h2>Installation</h2>
<p>In the directory "...django_polymorphic", execute <tt class="docutils literal">sudo python setup.py install</tt>.</p>
<p>Alternatively you can simply copy the <tt class="docutils literal">polymorphic</tt> subdirectory
(under "django_polymorphic") into your Django project dir
(e.g. if you want to distribute your project with more 'batteries included').</p>
<p>If you want to run the test cases in <cite>polymorphic/tests.py</cite>, you need to add
<tt class="docutils literal">polymorphic</tt> to your INSTALLED_APPS setting.</p>
<p>Django's ContentType framework (<tt class="docutils literal">django.contrib.contenttypes</tt>)
needs to be listed in INSTALLED_APPS (usually it already is).</p>
</div>
</div>
<div class="section" id="more-polymorphic-functionality">
<h1><a class="toc-backref" href="#id5">More Polymorphic Functionality</a></h1>
<p>In the examples below, these models are being used:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel
class ModelA(PolymorphicModel):
field1 = models.CharField(max_length=10)
class ModelB(ModelA):
field2 = models.CharField(max_length=10)
class ModelC(ModelB):
field3 = models.CharField(max_length=10)
</pre>
<div class="section" id="filtering-for-classes-equivalent-to-python-s-isinstance">
<h2>Filtering for classes (equivalent to python's isinstance() ):</h2>
<pre class="doctest-block">
>>> ModelA.objects.instance_of(ModelB)
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
</pre>
<p>In general, including or excluding parts of the inheritance tree:</p>
<pre class="literal-block">
ModelA.objects.instance_of(ModelB [, ModelC ...])
ModelA.objects.not_instance_of(ModelB [, ModelC ...])
</pre>
<p>You can also use this feature in Q-objects (with the same result as above):</p>
<pre class="doctest-block">
>>> ModelA.objects.filter( Q(instance_of=ModelB) )
</pre>
</div>
<div class="section" id="polymorphic-filtering-for-fields-in-derived-classes">
<h2>Polymorphic filtering (for fields in derived classes)</h2>
<p>For example, cherrypicking objects from multiple derived classes
anywhere in the inheritance tree, using Q objects (with the
syntax: <tt class="docutils literal">exact model name + three _ + field name</tt>):</p>
<pre class="doctest-block">
>>> ModelA.objects.filter( Q(ModelB___field2 = 'B2') | Q(ModelC___field3 = 'C3') )
.
[ <ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
</pre>
</div>
<div class="section" id="combining-querysets">
<h2>Combining Querysets</h2>
<p>Querysets could now be regarded as object containers that allow the
aggregation of different object types, very similar to python
lists - as long as the objects are accessed through the manager of
a common base class:</p>
<pre class="doctest-block">
>>> Base.objects.instance_of(ModelX) | Base.objects.instance_of(ModelY)
.
[ <ModelX: id 1, field_x (CharField)>,
<ModelY: id 2, field_y (CharField)> ]
</pre>
</div>
<div class="section" id="manytomanyfield-foreignkey-onetoonefield">
<h2>ManyToManyField, ForeignKey, OneToOneField</h2>
<p>Relationship fields referring to polymorphic models work as
expected: like polymorphic querysets they now always return the
referred objects with the same type/class these were created and
saved as.</p>
<p>E.g., if in your model you define:</p>
<pre class="literal-block">
field1 = OneToOneField(ModelA)
</pre>
<p>then field1 may now also refer to objects of type <tt class="docutils literal">ModelB</tt> or <tt class="docutils literal">ModelC</tt>.</p>
<p>A ManyToManyField example:</p>
<pre class="literal-block">
# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
many2many = models.ManyToManyField('ModelA') # ManyToMany relation to a polymorphic model
>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))
>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
</pre>
</div>
<div class="section" id="using-third-party-models-without-modifying-them">
<h2>Using Third Party Models (without modifying them)</h2>
<p>Third party models can be used as polymorphic models without
restrictions by subclassing them. E.g. using a third party
model as the root of a polymorphic inheritance tree:</p>
<pre class="literal-block">
from thirdparty import ThirdPartyModel
class MyThirdPartyBaseModel(PolymorhpicModel, ThirdPartyModel):
pass # or add fields
</pre>
<p>Or instead integrating the third party model anywhere into an
existing polymorphic inheritance tree:</p>
<pre class="literal-block">
class MyBaseModel(SomePolymorphicModel):
my_field = models.CharField(max_length=10)
class MyModelWithThirdParty(MyBaseModel, ThirdPartyModel):
pass # or add fields
</pre>
</div>
<div class="section" id="non-polymorphic-queries">
<h2>Non-Polymorphic Queries</h2>
<p>If you insert <tt class="docutils literal">.non_polymorphic()</tt> anywhere into the query chain, then
django_polymorphic will simply leave out the final step of retrieving the
real objects, and the manager/queryset will return objects of the type of
the base class you used for the query, like vanilla Django would
(<tt class="docutils literal">ModelA</tt> in this example).</p>
<pre class="doctest-block">
>>> qs=ModelA.objects.non_polymorphic().all()
>>> qs
[ <ModelA: id 1, field1 (CharField)>,
<ModelA: id 2, field1 (CharField)>,
<ModelA: id 3, field1 (CharField)> ]
</pre>
<p>There are no other changes in the behaviour of the queryset. For example,
enhancements for <tt class="docutils literal">filter()</tt> or <tt class="docutils literal">instance_of()</tt> etc. still work as expected.
If you do the final step yourself, you get the usual polymorphic result:</p>
<pre class="doctest-block">
>>> ModelA.objects.get_real_instances(qs)
[ <ModelA: id 1, field1 (CharField)>,
<ModelB: id 2, field1 (CharField), field2 (CharField)>,
<ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]
</pre>
</div>
<div class="section" id="about-queryset-methods">
<h2>About Queryset Methods</h2>
<ul class="simple">
<li><tt class="docutils literal">annotate()</tt> and <tt class="docutils literal">aggregate()</tt> work just as usual, with the
addition that the <tt class="docutils literal">ModelX___field</tt> syntax can be used for the
keyword arguments (but not for the non-keyword arguments).</li>
<li><tt class="docutils literal">order_by()</tt> now similarly supports the <tt class="docutils literal">ModelX___field</tt> syntax
for specifying ordering through a field in a submodel.</li>
<li><tt class="docutils literal">distinct()</tt> works as expected. It only regards the fields of
the base class, but this should never make a difference.</li>
<li><tt class="docutils literal">select_related()</tt> works just as usual, but it can not (yet) be used
to select relations in derived models
(like <tt class="docutils literal"><span class="pre">ModelA.objects.select_related('ModelC___fieldxy')</span></tt> )</li>
<li><tt class="docutils literal">extra()</tt> works as expected (it returns polymorphic results) but
currently has one restriction: The resulting objects are required to have
a unique primary key within the result set - otherwise an error is thrown
(this case could be made to work, however it may be mostly unneeded)..
The keyword-argument "polymorphic" is no longer supported.
You can get back the old non-polymorphic behaviour (before V1.0)
by using <tt class="docutils literal"><span class="pre">ModelA.objects.non_polymorphic().extra(...)</span></tt>.</li>
<li><tt class="docutils literal">get_real_instances()</tt> allows you to turn a
queryset or list of base model objects efficiently into the real objects.
For example, you could do <tt class="docutils literal"><span class="pre">base_objects_queryset=ModelA.extra(...).non_polymorphic()</span></tt>
and then call <tt class="docutils literal">real_objects=base_objects_queryset.get_real_instances()</tt>.Or alternatively
.``real_objects=ModelA.objects..get_real_instances(base_objects_queryset_or_object_list)``</li>
<li><tt class="docutils literal">values()</tt> & <tt class="docutils literal">values_list()</tt> currently do not return polymorphic
results. This may change in the future however. If you want to use these
methods now, it's best if you use <tt class="docutils literal"><span class="pre">Model.base_objects.values...</span></tt> as
this is guaranteed to not change.</li>
<li><tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet supported (support will be added
in the future).</li>
</ul>
</div>
<div class="section" id="using-enhanced-q-objects-in-any-places">
<h2>Using enhanced Q-objects in any Places</h2>
<p>The queryset enhancements (e.g. <tt class="docutils literal">instance_of</tt>) only work as arguments
to the member functions of a polymorphic queryset. Occationally it may
be useful to be able to use Q objects with these enhancements in other places.
As Django doesn't understand these enhanced Q objects, you need to
transform them manually into normal Q objects before you can feed them
to a Django queryset or function:</p>
<pre class="literal-block">
normal_q_object = ModelA.translate_polymorphic_Q_object( Q(instance_of=Model2B) )
</pre>
<p>This function cannot be used at model creation time however (in models.py),
as it may need to access the ContentTypes database table.</p>
</div>
<div class="section" id="nicely-displaying-polymorphic-querysets">
<h2>Nicely Displaying Polymorphic Querysets</h2>
<p>In order to get the output as seen in all examples here, you need to use the
ShowFieldType class mixin:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, ShowFieldType
class ModelA(ShowFieldType, PolymorphicModel):
field1 = models.CharField(max_length=10)
</pre>
<p>You may also use ShowFieldContent or ShowFieldTypeAndContent to display
additional information when printing querysets (or converting them to text).</p>
<p>When showing field contents, they will be truncated to 20 characters. You can
modify this behaviour by setting a class variable in your model like this:</p>
<pre class="literal-block">
class ModelA(ShowFieldType, PolymorphicModel):
polymorphic_showfield_max_field_width = 20
...
</pre>
<p>Similarly, pre-V1.0 output formatting can be re-estated by using
<tt class="docutils literal">polymorphic_showfield_old_format = True</tt>.</p>
</div>
</div>
<div class="section" id="custom-managers-querysets-manager-inheritance">
<h1><a class="toc-backref" href="#id6">Custom Managers, Querysets & Manager Inheritance</a></h1>
<div class="section" id="using-a-custom-manager">
<h2>Using a Custom Manager</h2>
<p>A nice feature of Django is the possibility to define one's own custom object managers.
This is fully supported with django_polymorphic: For creating a custom polymorphic
manager class, just derive your manager from <tt class="docutils literal">PolymorphicManager</tt> instead of
<tt class="docutils literal">models.Manager</tt>. As with vanilla Django, in your model class, you should
explicitly add the default manager first, and then your custom manager:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, PolymorphicManager
class TimeOrderedManager(PolymorphicManager):
def get_query_set(self):
qs = super(TimeOrderedManager,self).get_query_set()
return qs.order_by('-start_date') # order the queryset
def most_recent(self):
qs = self.get_query_set() # get my ordered queryset
return qs[:10] # limit => get ten most recent entries
class Project(PolymorphicModel):
objects = PolymorphicManager() # add the default polymorphic manager first
objects_ordered = TimeOrderedManager() # then add your own manager
start_date = DateTimeField() # project start is this date/time
</pre>
<p>The first manager defined ('objects' in the example) is used by
Django as automatic manager for several purposes, including accessing
related objects. It must not filter objects and it's safest to use
the plain <tt class="docutils literal">PolymorphicManager</tt> here.</p>
</div>
<div class="section" id="manager-inheritance">
<h2>Manager Inheritance</h2>
<p>Polymorphic models inherit/propagate all managers from their
base models, as long as these are polymorphic. This means that all
managers defined in polymorphic base models continue to work as
expected in models inheriting from this base model:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, PolymorphicManager
class TimeOrderedManager(PolymorphicManager):
def get_query_set(self):
qs = super(TimeOrderedManager,self).get_query_set()
return qs.order_by('-start_date') # order the queryset
def most_recent(self):
qs = self.get_query_set() # get my ordered queryset
return qs[:10] # limit => get ten most recent entries
class Project(PolymorphicModel):
objects = PolymorphicManager() # add the default polymorphic manager first
objects_ordered = TimeOrderedManager() # then add your own manager
start_date = DateTimeField() # project start is this date/time
class ArtProject(Project): # inherit from Project, inheriting its fields and managers
artist = models.CharField(max_length=30)
</pre>
<p>ArtProject inherited the managers <tt class="docutils literal">objects</tt> and <tt class="docutils literal">objects_ordered</tt> from Project.</p>
<p><tt class="docutils literal">ArtProject.objects_ordered.all()</tt> will return all art projects ordered
regarding their start time and <tt class="docutils literal">ArtProject.objects_ordered.most_recent()</tt>
will return the ten most recent art projects.
.</p>
</div>
<div class="section" id="using-a-custom-queryset-class">
<h2>Using a Custom Queryset Class</h2>
<p>The <tt class="docutils literal">PolymorphicManager</tt> class accepts one initialization argument,
which is the queryset class the manager should use. Just as with vanilla Django,
you may define your own custom queryset classes. Just use PolymorphicQuerySet
instead of Django's QuerySet as the base class:</p>
<pre class="literal-block">
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
class MyQuerySet(PolymorphicQuerySet):
def my_queryset_method(...):
...
class MyModel(PolymorphicModel):
my_objects=PolymorphicManager(MyQuerySet)
...
</pre>
</div>
</div>
<div class="section" id="performance-considerations">
<h1><a class="toc-backref" href="#id7">Performance Considerations</a></h1>
<p>The current implementation is rather simple and does not use any
custom SQL or Django DB layer internals - it is purely based on the
standard Django ORM.</p>
<p>Specifically, the query:</p>
<pre class="literal-block">
result_objects = list( ModelA.objects.filter(...) )
</pre>
<p>performs one SQL query to retrieve <tt class="docutils literal">ModelA</tt> objects and one additional
query for each unique derived class occurring in result_objects.
The best case for retrieving 100 objects is 1 SQL query if all are
class <tt class="docutils literal">ModelA</tt>. If 50 objects are <tt class="docutils literal">ModelA</tt> and 50 are <tt class="docutils literal">ModelB</tt>, then
two queries are executed. The pathological worst case is 101 db queries if
result_objects contains 100 different object types (with all of them
subclasses of <tt class="docutils literal">ModelA</tt>).</p>
<p>Usually, when Django users create their own polymorphic ad-hoc solution
without a tool like django_polymorphic, this usually results in a variation of</p>
<pre class="literal-block">
result_objects = [ o.get_real_instance() for o in BaseModel.objects.filter(...) ]
</pre>
<p>which has very bad performance, as it introduces one additional
SQL query for every object in the result which is not of class <tt class="docutils literal">BaseModel</tt>.</p>
<p>Compared to these solutions, django_polymorphic has the advantage
that it only needs one sql request per <em>object type</em>, and not <em>per object</em>.</p>
<div class="section" id="performance-problems-with-postgresql-mysql-and-sqlite3">
<span id="performance"></span><h2>Performance Problems with PostgreSQL, MySQL and SQLite3</h2>
<p>Current relational DBM systems seem to have general problems with
the SQL queries produced by object relational mappers like the Django
ORM, if these use multi-table inheritance like Django's ORM does.
The "inner joins" in these queries can perform very badly.
This is independent of django_polymorphic and affects all uses of
multi table Model inheritance.</p>
<p>Concrete benchmark results are forthcoming (please see discussion forum).</p>
<p>Please also see this <a class="reference external" href="http://www.jacobian.org/writing/concrete-inheritance/">post (and comments) from Jacob Kaplan-Moss</a>.</p>
</div>
</div>
<div class="section" id="restrictions-caveats">
<span id="restrictions"></span><h1><a class="toc-backref" href="#id8">Restrictions & Caveats</a></h1>
<ul class="simple">
<li>Database Performance regarding concrete Model inheritance in general.
Please see "Performance Problems" above.</li>
<li>Queryset methods <tt class="docutils literal">values()</tt>, <tt class="docutils literal">values_list()</tt>, <tt class="docutils literal">select_related()</tt>,
<tt class="docutils literal">defer()</tt> and <tt class="docutils literal">only()</tt> are not yet fully supported (see above).
<tt class="docutils literal">extra()</tt> has one restriction: the resulting objects are required to have
a unique primary key within the result set.</li>
<li>Django Admin Integration: There currently is no specific admin integration,
but it would most likely make sense to have one.</li>
<li>Diamond shaped inheritance: There seems to be a general problem
with diamond shaped multiple model inheritance with Django models
(tested with V1.1 - V1.3).
An example is here: <a class="reference external" href="http://code.djangoproject.com/ticket/10808">http://code.djangoproject.com/ticket/10808</a>.
This problem is aggravated when trying to enhance models.Model
by subclassing it instead of modifying Django core (as we do here
with PolymorphicModel).</li>
<li>The enhanced filter-definitions/Q-objects only work as arguments
for the methods of the polymorphic querysets. Please see above
for <tt class="docutils literal">translate_polymorphic_Q_object</tt>.</li>
<li>A reference (<tt class="docutils literal">ContentType</tt>) to the real/leaf model is stored
in the base model (the base model directly inheriting from
PolymorphicModel). You need to be aware of this when using the
<tt class="docutils literal">dumpdata</tt> management command or any other low-level
database operations. E.g. if you rename models or apps or copy
objects from one database to another, then Django's ContentType
table needs to be corrected/copied too. This is of course generally
the case for any models using Django's ContentType.</li>
<li>Django 1.1 only - the names of polymorphic models must be unique
in the whole project, even if they are in two different apps.
This results from a restriction in the Django 1.1 "related_name"
option (fixed in Django 1.2).</li>
<li>Django 1.1 only - when ContentType is used in models, Django's
seralisation or fixtures cannot be used (all polymorphic models
use ContentType). This issue seems to be resolved for Django 1.2
(changeset 11863: Fixed #7052, Added support for natural keys in serialization).<ul>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/7052">http://code.djangoproject.com/ticket/7052</a></li>
<li><a class="reference external" href="http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django">http://stackoverflow.com/questions/853796/problems-with-contenttypes-when-loading-a-fixture-in-django</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="project-status">
<h1><a class="toc-backref" href="#id9">Project Status</a></h1>
<p>Django_polymorphic works well for a considerable number of users now,
and no major problems have shown up for many months.
The API can be considered stable beginning with the V1.0 release.</p>
</div>
<div class="section" id="links">
<h1><a class="toc-backref" href="#id10">Links</a></h1>
<ul class="simple">
<li><a class="reference external" href="http://code.djangoproject.com/wiki/ModelInheritance">http://code.djangoproject.com/wiki/ModelInheritance</a></li>
<li><a class="reference external" href="http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html">http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html</a></li>
<li><a class="reference external" href="http://www.djangosnippets.org/snippets/1031/">http://www.djangosnippets.org/snippets/1031/</a></li>
<li><a class="reference external" href="http://www.djangosnippets.org/snippets/1034/">http://www.djangosnippets.org/snippets/1034/</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&q=model+inheritance+CORBA#a20fabc661b7035d">http://groups.google.com/group/django-developers/browse_frm/thread/7d40ad373ebfa912/a20fabc661b7035d?lnk=gst&q=model+inheritance+CORBA#a20fabc661b7035d</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&q=inheritance#0b92971ffc0aa6f8">http://groups.google.com/group/django-developers/browse_thread/thread/9bc2aaec0796f4e0/0b92971ffc0aa6f8?lnk=gst&q=inheritance#0b92971ffc0aa6f8</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&q=inheritance#d8c0af3dacad412d">http://groups.google.com/group/django-developers/browse_thread/thread/3947c594100c4adb/d8c0af3dacad412d?lnk=gst&q=inheritance#d8c0af3dacad412d</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f">http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/b76c9d8c89a5574f</a></li>
<li><a class="reference external" href="http://peterbraden.co.uk/article/django-inheritance">http://peterbraden.co.uk/article/django-inheritance</a></li>
<li><a class="reference external" href="http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django">http://www.hopelessgeek.com/2009/11/25/a-hack-for-multi-table-inheritance-in-django</a></li>
<li><a class="reference external" href="http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982">http://stackoverflow.com/questions/929029/how-do-i-access-the-child-classes-of-an-object-in-django-without-knowing-the-name/929982#929982</a></li>
<li><a class="reference external" href="http://stackoverflow.com/questions/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses">http://stackoverflow.com/questions/1581024/django-inheritance-how-to-have-one-method-for-all-subclasses</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&q=polymorphic#e676a537d735d9ef">http://groups.google.com/group/django-users/browse_thread/thread/cbdaf2273781ccab/e676a537d735d9ef?lnk=gst&q=polymorphic#e676a537d735d9ef</a></li>
<li><a class="reference external" href="http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&q=model+inheritance#bc18c18b2e83881e">http://groups.google.com/group/django-users/browse_thread/thread/52f72cffebb705e/bc18c18b2e83881e?lnk=gst&q=model+inheritance#bc18c18b2e83881e</a></li>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/10808">http://code.djangoproject.com/ticket/10808</a></li>
<li><a class="reference external" href="http://code.djangoproject.com/ticket/7270">http://code.djangoproject.com/ticket/7270</a></li>
</ul>
</div>
</div>
</body>
</html>