commit
999775c2d5
24
.travis.yml
24
.travis.yml
|
|
@ -5,25 +5,49 @@ python:
|
|||
- "3.2"
|
||||
- "3.3"
|
||||
- "3.4"
|
||||
- "3.5"
|
||||
env:
|
||||
- DJANGO=">=1.4,<1.5"
|
||||
- DJANGO=">=1.5,<1.6"
|
||||
- DJANGO=">=1.6,<1.7"
|
||||
- DJANGO=">=1.7,<1.8"
|
||||
- DJANGO=">=1.8,<1.9"
|
||||
- DJANGO=">=1.9a1,<1.10"
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- python: "3.5"
|
||||
env: DJANGO=">=1.4,<1.5"
|
||||
- python: "3.5"
|
||||
env: DJANGO=">=1.5,<1.6"
|
||||
- python: "3.5"
|
||||
env: DJANGO=">=1.6,<1.7"
|
||||
- python: "3.5"
|
||||
env: DJANGO=">=1.7,<1.8"
|
||||
- python: "3.5"
|
||||
env: DJANGO=">=1.8,<1.9"
|
||||
|
||||
- python: "3.4"
|
||||
env: DJANGO=">=1.4,<1.5"
|
||||
- python: "3.4"
|
||||
env: DJANGO=">=1.9a1,<1.10"
|
||||
|
||||
- python: "3.3"
|
||||
env: DJANGO=">=1.4,<1.5"
|
||||
- python: "3.3"
|
||||
env: DJANGO=">=1.9a1,<1.10"
|
||||
|
||||
- python: "3.2"
|
||||
env: DJANGO=">=1.4,<1.5"
|
||||
- python: "3.2"
|
||||
env: DJANGO=">=1.9a1,<1.10"
|
||||
|
||||
- python: "2.6"
|
||||
env: DJANGO=">=1.7,<1.8"
|
||||
- python: "2.6"
|
||||
env: DJANGO=">=1.8,<1.9"
|
||||
- python: "2.6"
|
||||
env: DJANGO=">=1.9a1,<1.10"
|
||||
|
||||
install:
|
||||
- pip install django$DJANGO coverage==3.6
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ Contributors
|
|||
* Evan Borgstrom
|
||||
* Gavin Wahl
|
||||
* Germán M. Bravo
|
||||
* Hugo Osvaldo Barrera
|
||||
* Jacob Rief
|
||||
* Jedediah Smith (proxy models support)
|
||||
* John Furr
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Advanced features
|
|||
In the examples below, these models are being used::
|
||||
|
||||
from django.db import models
|
||||
from polymorphic import PolymorphicModel
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
class ModelA(PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ manager class, just derive your manager from ``PolymorphicManager`` instead of
|
|||
``models.Manager``. As with vanilla Django, in your model class, you should
|
||||
explicitly add the default manager first, and then your custom manager::
|
||||
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.manager import PolymorphicManager
|
||||
|
||||
class TimeOrderedManager(PolymorphicManager):
|
||||
def get_queryset(self):
|
||||
|
|
@ -41,7 +42,8 @@ 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::
|
||||
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.manager import PolymorphicManager
|
||||
|
||||
class TimeOrderedManager(PolymorphicManager):
|
||||
def get_queryset(self):
|
||||
|
|
@ -77,7 +79,9 @@ 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::
|
||||
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.manager import PolymorphicManager
|
||||
from polymorphic.query import PolymorphicQuerySet
|
||||
|
||||
class MyQuerySet(PolymorphicQuerySet):
|
||||
def my_queryset_method(...):
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ Making Your Models Polymorphic
|
|||
|
||||
Use ``PolymorphicModel`` instead of Django's ``models.Model``, like so::
|
||||
|
||||
from polymorphic import PolymorphicModel
|
||||
from polymorphic.models import PolymorphicModel
|
||||
|
||||
class Project(PolymorphicModel):
|
||||
topic = models.CharField(max_length=30)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
from django.conf.urls import patterns, include, url
|
||||
from django.conf.urls import include, url
|
||||
from django.contrib import admin
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.views.generic import RedirectView
|
||||
|
||||
admin.autodiscover()
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = [
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^$', RedirectView.as_view(url=reverse_lazy('admin:index'), permanent=False)),
|
||||
)
|
||||
]
|
||||
|
|
|
|||
|
|
@ -42,11 +42,10 @@ class ModelAAdmin(PolymorphicParentModelAdmin):
|
|||
admin.site.register(ModelA, ModelAAdmin)
|
||||
|
||||
|
||||
if 'Model2A' in globals():
|
||||
class Model2AChildAdmin(PolymorphicChildModelAdmin):
|
||||
class Model2AChildAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = Model2A
|
||||
|
||||
class Model2AAdmin(PolymorphicParentModelAdmin):
|
||||
class Model2AAdmin(PolymorphicParentModelAdmin):
|
||||
base_model = Model2A
|
||||
list_filter = (PolymorphicChildModelFilter,)
|
||||
child_models = (
|
||||
|
|
@ -55,14 +54,13 @@ if 'Model2A' in globals():
|
|||
(Model2C, Model2AChildAdmin),
|
||||
)
|
||||
|
||||
admin.site.register(Model2A, Model2AAdmin)
|
||||
admin.site.register(Model2A, Model2AAdmin)
|
||||
|
||||
|
||||
if 'UUIDModelA' in globals():
|
||||
class UUIDModelAChildAdmin(PolymorphicChildModelAdmin):
|
||||
class UUIDModelAChildAdmin(PolymorphicChildModelAdmin):
|
||||
base_model = UUIDModelA
|
||||
|
||||
class UUIDModelAAdmin(PolymorphicParentModelAdmin):
|
||||
class UUIDModelAAdmin(PolymorphicParentModelAdmin):
|
||||
base_model = UUIDModelA
|
||||
list_filter = (PolymorphicChildModelFilter,)
|
||||
child_models = (
|
||||
|
|
@ -71,7 +69,7 @@ if 'UUIDModelA' in globals():
|
|||
(UUIDModelC, UUIDModelAChildAdmin),
|
||||
)
|
||||
|
||||
admin.site.register(UUIDModelA, UUIDModelAAdmin)
|
||||
admin.site.register(UUIDModelA, UUIDModelAAdmin)
|
||||
|
||||
|
||||
class ProxyChildAdmin(PolymorphicChildModelAdmin):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,222 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9b1 on 2015-10-23 22:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import polymorphic.showfields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('contenttypes', '0002_remove_content_type_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Model2A',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('field1', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ModelA',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('field1', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(polymorphic.showfields.ShowFieldTypeAndContent, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='nModelA',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('field1', models.CharField(max_length=10)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Project',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('topic', models.CharField(max_length=30)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(polymorphic.showfields.ShowFieldContent, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProxyBase',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=200)),
|
||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_pexp.proxybase_set+', to='contenttypes.ContentType')),
|
||||
],
|
||||
options={
|
||||
'ordering': ('title',),
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UUIDModelA',
|
||||
fields=[
|
||||
('uuid_primary_key', models.UUIDField(primary_key=True, serialize=False)),
|
||||
('field1', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=(polymorphic.showfields.ShowFieldTypeAndContent, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ArtProject',
|
||||
fields=[
|
||||
('project_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.Project')),
|
||||
('artist', models.CharField(max_length=30)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.project',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Model2B',
|
||||
fields=[
|
||||
('model2a_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.Model2A')),
|
||||
('field2', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.model2a',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ModelB',
|
||||
fields=[
|
||||
('modela_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.ModelA')),
|
||||
('field2', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.modela',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='nModelB',
|
||||
fields=[
|
||||
('nmodela_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.nModelA')),
|
||||
('field2', models.CharField(max_length=10)),
|
||||
],
|
||||
bases=('pexp.nmodela',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ResearchProject',
|
||||
fields=[
|
||||
('project_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.Project')),
|
||||
('supervisor', models.CharField(max_length=30)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.project',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UUIDModelB',
|
||||
fields=[
|
||||
('uuidmodela_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.UUIDModelA')),
|
||||
('field2', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.uuidmodela',),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='uuidmodela',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_pexp.uuidmodela_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='project',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_pexp.project_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='modela',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_pexp.modela_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='model2a',
|
||||
name='polymorphic_ctype',
|
||||
field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_pexp.model2a_set+', to='contenttypes.ContentType'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProxyA',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('pexp.proxybase',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ProxyB',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
},
|
||||
bases=('pexp.proxybase',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Model2C',
|
||||
fields=[
|
||||
('model2b_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.Model2B')),
|
||||
('field3', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.model2b',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ModelC',
|
||||
fields=[
|
||||
('modelb_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.ModelB')),
|
||||
('field3', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.modelb',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='nModelC',
|
||||
fields=[
|
||||
('nmodelb_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.nModelB')),
|
||||
('field3', models.CharField(max_length=10)),
|
||||
],
|
||||
bases=('pexp.nmodelb',),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UUIDModelC',
|
||||
fields=[
|
||||
('uuidmodelb_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='pexp.UUIDModelB')),
|
||||
('field3', models.CharField(max_length=10)),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
bases=('pexp.uuidmodelb',),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.9b1 on 2015-10-24 01:42
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pexp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='modelc',
|
||||
name='field4',
|
||||
field=models.ManyToManyField(related_name='related_c', to='pexp.ModelB'),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import django
|
||||
from django.db import models
|
||||
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||
|
||||
class Project(ShowFieldContent, PolymorphicModel):
|
||||
|
|
@ -18,6 +19,7 @@ class ModelB(ModelA):
|
|||
field2 = models.CharField(max_length=10)
|
||||
class ModelC(ModelB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
field4 = models.ManyToManyField(ModelB, related_name='related_c')
|
||||
|
||||
class nModelA(models.Model):
|
||||
field1 = models.CharField(max_length=10)
|
||||
|
|
@ -26,26 +28,24 @@ class nModelB(nModelA):
|
|||
class nModelC(nModelB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
|
||||
# for Django 1.2+, test models with same names in different apps
|
||||
# (the other models with identical names are in polymorphic/tests.py)
|
||||
from django import VERSION as django_VERSION
|
||||
if not (django_VERSION[0]<=1 and django_VERSION[1]<=1):
|
||||
class Model2A(PolymorphicModel):
|
||||
class Model2A(PolymorphicModel):
|
||||
field1 = models.CharField(max_length=10)
|
||||
class Model2B(Model2A):
|
||||
class Model2B(Model2A):
|
||||
field2 = models.CharField(max_length=10)
|
||||
class Model2C(Model2B):
|
||||
class Model2C(Model2B):
|
||||
field3 = models.CharField(max_length=10)
|
||||
|
||||
try: from polymorphic.test_tools import UUIDField
|
||||
except: pass
|
||||
if 'UUIDField' in globals():
|
||||
class UUIDModelA(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
if django.VERSION < (1,8):
|
||||
from polymorphic.tools_for_tests import UUIDField
|
||||
else:
|
||||
from django.db.models import UUIDField
|
||||
|
||||
class UUIDModelA(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
uuid_primary_key = UUIDField(primary_key = True)
|
||||
field1 = models.CharField(max_length=10)
|
||||
class UUIDModelB(UUIDModelA):
|
||||
class UUIDModelB(UUIDModelA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
class UUIDModelC(UUIDModelB):
|
||||
class UUIDModelC(UUIDModelB):
|
||||
field3 = models.CharField(max_length=10)
|
||||
|
||||
class ProxyBase(PolymorphicModel):
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ Please see LICENSE and AUTHORS for more information.
|
|||
"""
|
||||
from __future__ import absolute_import
|
||||
import django
|
||||
from .polymorphic_model import PolymorphicModel
|
||||
from .manager import PolymorphicManager
|
||||
from .query import PolymorphicQuerySet
|
||||
from .query_translate import translate_polymorphic_Q_object
|
||||
from .showfields import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||
from .showfields import ShowFields, ShowFieldTypes, ShowFieldsAndTypes # import old names for compatibility
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ ModelAdmin code to display polymorphic models.
|
|||
"""
|
||||
import sys
|
||||
from django import forms
|
||||
from django.conf.urls import patterns, url
|
||||
from django.conf.urls import url
|
||||
from django.contrib import admin
|
||||
from django.contrib.admin.helpers import AdminForm, AdminErrorList
|
||||
from django.contrib.admin.widgets import AdminRadioSelect
|
||||
|
|
@ -18,6 +18,7 @@ from django.utils.encoding import force_text
|
|||
from django.utils.http import urlencode
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import django
|
||||
|
||||
|
||||
try:
|
||||
|
|
@ -268,7 +269,6 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
|
|||
|
||||
def change_view(self, request, object_id, *args, **kwargs):
|
||||
"""Redirect the change view to the real admin."""
|
||||
# between Django 1.3 and 1.4 this method signature differs. Hence the *args, **kwargs
|
||||
real_admin = self._get_real_admin(object_id)
|
||||
return real_admin.change_view(request, object_id, *args, **kwargs)
|
||||
|
||||
|
|
@ -296,17 +296,28 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
|
|||
urls = super(PolymorphicParentModelAdmin, self).get_urls()
|
||||
info = _get_opt(self.model)
|
||||
|
||||
# Patch the change URL so it's not a big catch-all; allowing all custom URLs to be added to the end.
|
||||
# The url needs to be recreated, patching url.regex is not an option Django 1.4's LocaleRegexProvider changed it.
|
||||
new_change_url = url(r'^{0}/$'.format(self.pk_regex), self.admin_site.admin_view(self.change_view), name='{0}_{1}_change'.format(*info))
|
||||
# Patch the change view URL so it's not a big catch-all; allowing all
|
||||
# custom URLs to be added to the end. This is done by adding '/$' to the
|
||||
# end of the regex. The url needs to be recreated, patching url.regex
|
||||
# is not an option Django 1.4's LocaleRegexProvider changed it.
|
||||
if django.VERSION < (1, 9):
|
||||
# On Django 1.9, the change view URL has been changed from
|
||||
# /<app>/<model>/<pk>/ to /<app>/<model>/<pk>/change/, which is
|
||||
# why we can skip this workaround for Django >= 1.9.
|
||||
new_change_url = url(
|
||||
r'^{0}/$'.format(self.pk_regex),
|
||||
self.admin_site.admin_view(self.change_view),
|
||||
name='{0}_{1}_change'.format(*info)
|
||||
)
|
||||
|
||||
for i, oldurl in enumerate(urls):
|
||||
if oldurl.name == new_change_url.name:
|
||||
urls[i] = new_change_url
|
||||
|
||||
# Define the catch-all for custom views
|
||||
custom_urls = patterns('',
|
||||
custom_urls = [
|
||||
url(r'^(?P<path>.+)$', self.admin_site.admin_view(self.subclass_view))
|
||||
)
|
||||
]
|
||||
|
||||
# At this point. all admin code needs to be known.
|
||||
self._lazy_setup()
|
||||
|
|
@ -383,7 +394,8 @@ class PolymorphicParentModelAdmin(admin.ModelAdmin):
|
|||
context = {
|
||||
'title': _('Add %s') % force_text(opts.verbose_name),
|
||||
'adminform': adminForm,
|
||||
'is_popup': "_popup" in request.REQUEST,
|
||||
'is_popup': ("_popup" in request.POST or
|
||||
"_popup" in request.GET),
|
||||
'media': mark_safe(media),
|
||||
'errors': AdminErrorList(form, ()),
|
||||
'app_label': opts.app_label,
|
||||
|
|
@ -465,7 +477,7 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
|
|||
kwargs.setdefault('form', self.base_form or self.form)
|
||||
|
||||
# prevent infinite recursion in django 1.6+
|
||||
if not self.declared_fieldsets:
|
||||
if not getattr(self, 'declared_fieldsets', None):
|
||||
kwargs.setdefault('fields', None)
|
||||
|
||||
return super(PolymorphicChildModelAdmin, self).get_form(request, obj, **kwargs)
|
||||
|
|
@ -529,7 +541,8 @@ class PolymorphicChildModelAdmin(admin.ModelAdmin):
|
|||
|
||||
def get_fieldsets(self, request, obj=None):
|
||||
# If subclass declares fieldsets, this is respected
|
||||
if self.declared_fieldsets or not self.base_fieldsets:
|
||||
if (hasattr(self, 'declared_fieldset') and self.declared_fieldsets) \
|
||||
or not self.base_fieldsets:
|
||||
return super(PolymorphicChildModelAdmin, self).get_fieldsets(request, obj)
|
||||
|
||||
# Have a reasonable default fieldsets,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,229 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
IMPORTANT:
|
||||
Seamless Polymorphic Inheritance for Django Models
|
||||
==================================================
|
||||
|
||||
The models.py module is not used anymore.
|
||||
Please use the following import method in your apps:
|
||||
Please see README.rst and DOCS.rst for further information.
|
||||
|
||||
from polymorphic import PolymorphicModel, ...
|
||||
Or on the Web:
|
||||
http://chrisglass.github.com/django_polymorphic/
|
||||
http://github.com/chrisglass/django_polymorphic
|
||||
|
||||
Copyright:
|
||||
This code and affiliated files are (C) by Bert Constantin and individual contributors.
|
||||
Please see LICENSE and AUTHORS for more information.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import six
|
||||
|
||||
from .base import PolymorphicModelBase
|
||||
from .manager import PolymorphicManager
|
||||
from .query_translate import translate_polymorphic_Q_object
|
||||
|
||||
###################################################################################
|
||||
### PolymorphicModel
|
||||
|
||||
class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
||||
"""
|
||||
Abstract base class that provides polymorphic behaviour
|
||||
for any model directly or indirectly derived from it.
|
||||
|
||||
For usage instructions & examples please see documentation.
|
||||
|
||||
PolymorphicModel declares one field for internal use (polymorphic_ctype)
|
||||
and provides a polymorphic manager as the default manager
|
||||
(and as 'objects').
|
||||
|
||||
PolymorphicModel overrides the save() and __init__ methods.
|
||||
|
||||
If your derived class overrides any of these methods as well, then you need
|
||||
to take care that you correctly call the method of the superclass, like:
|
||||
|
||||
super(YourClass,self).save(*args,**kwargs)
|
||||
"""
|
||||
|
||||
# for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
|
||||
polymorphic_model_marker = True
|
||||
|
||||
# for PolymorphicQuery, True => an overloaded __repr__ with nicer multi-line output is used by PolymorphicQuery
|
||||
polymorphic_query_multiline_output = False
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
# avoid ContentType related field accessor clash (an error emitted by model validation)
|
||||
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
|
||||
related_name='polymorphic_%(app_label)s.%(class)s_set+')
|
||||
|
||||
# some applications want to know the name of the fields that are added to its models
|
||||
polymorphic_internal_model_fields = ['polymorphic_ctype']
|
||||
|
||||
# Note that Django 1.5 removes these managers because the model is abstract.
|
||||
# They are pretended to be there by the metaclass in PolymorphicModelBase.get_inherited_managers()
|
||||
objects = PolymorphicManager()
|
||||
base_objects = models.Manager()
|
||||
|
||||
@classmethod
|
||||
def translate_polymorphic_Q_object(self_class, q):
|
||||
return translate_polymorphic_Q_object(self_class, q)
|
||||
|
||||
def pre_save_polymorphic(self):
|
||||
"""Normally not needed.
|
||||
This function may be called manually in special use-cases. When the object
|
||||
is saved for the first time, we store its real class in polymorphic_ctype.
|
||||
When the object later is retrieved by PolymorphicQuerySet, it uses this
|
||||
field to figure out the real class of this object
|
||||
(used by PolymorphicQuerySet._get_real_instances)
|
||||
"""
|
||||
if not self.polymorphic_ctype_id:
|
||||
self.polymorphic_ctype = ContentType.objects.get_for_model(self, for_concrete_model=False)
|
||||
pre_save_polymorphic.alters_data = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Overridden model save function which supports the polymorphism
|
||||
functionality (through pre_save_polymorphic)."""
|
||||
self.pre_save_polymorphic()
|
||||
return super(PolymorphicModel, self).save(*args, **kwargs)
|
||||
save.alters_data = True
|
||||
|
||||
def get_real_instance_class(self):
|
||||
"""
|
||||
Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the real class/type of these objects may be
|
||||
determined using this method.
|
||||
"""
|
||||
# the following line would be the easiest way to do this, but it produces sql queries
|
||||
# return self.polymorphic_ctype.model_class()
|
||||
# so we use the following version, which uses the ContentType manager cache.
|
||||
# Note that model_class() can return None for stale content types;
|
||||
# when the content type record still exists but no longer refers to an existing model.
|
||||
try:
|
||||
model = ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
except AttributeError:
|
||||
# Django <1.6 workaround
|
||||
return None
|
||||
|
||||
# Protect against bad imports (dumpdata without --natural) or other
|
||||
# issues missing with the ContentType models.
|
||||
if model is not None \
|
||||
and not issubclass(model, self.__class__) \
|
||||
and not issubclass(model, self.__class__._meta.proxy_for_model):
|
||||
raise RuntimeError("ContentType {0} for {1} #{2} does not point to a subclass!".format(
|
||||
self.polymorphic_ctype_id, model, self.pk,
|
||||
))
|
||||
return model
|
||||
|
||||
def get_real_concrete_instance_class_id(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).pk
|
||||
|
||||
def get_real_concrete_instance_class(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).model_class()
|
||||
|
||||
def get_real_instance(self):
|
||||
"""Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the complete object with it's real class/type
|
||||
and all fields may be retrieved with this method.
|
||||
Each method call executes one db query (if necessary)."""
|
||||
real_model = self.get_real_instance_class()
|
||||
if real_model == self.__class__:
|
||||
return self
|
||||
return real_model.objects.get(pk=self.pk)
|
||||
|
||||
def __init__(self, * args, ** kwargs):
|
||||
"""Replace Django's inheritance accessor member functions for our model
|
||||
(self.__class__) with our own versions.
|
||||
We monkey patch them until a patch can be added to Django
|
||||
(which would probably be very small and make all of this obsolete).
|
||||
|
||||
If we have inheritance of the form ModelA -> ModelB ->ModelC then
|
||||
Django creates accessors like this:
|
||||
- ModelA: modelb
|
||||
- ModelB: modela_ptr, modelb, modelc
|
||||
- ModelC: modela_ptr, modelb, modelb_ptr, modelc
|
||||
|
||||
These accessors allow Django (and everyone else) to travel up and down
|
||||
the inheritance tree for the db object at hand.
|
||||
|
||||
The original Django accessors use our polymorphic manager.
|
||||
But they should not. So we replace them with our own accessors that use
|
||||
our appropriate base_objects manager.
|
||||
"""
|
||||
super(PolymorphicModel, self).__init__(*args, ** kwargs)
|
||||
|
||||
if self.__class__.polymorphic_super_sub_accessors_replaced:
|
||||
return
|
||||
self.__class__.polymorphic_super_sub_accessors_replaced = True
|
||||
|
||||
def create_accessor_function_for_model(model, accessor_name):
|
||||
def accessor_function(self):
|
||||
attr = model.base_objects.get(pk=self.pk)
|
||||
return attr
|
||||
return accessor_function
|
||||
|
||||
subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models()
|
||||
|
||||
try:
|
||||
from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor
|
||||
except ImportError:
|
||||
# django < 1.9
|
||||
from django.db.models.fields.related import (
|
||||
SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor,
|
||||
ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor,
|
||||
)
|
||||
for name, model in subclasses_and_superclasses_accessors.items():
|
||||
orig_accessor = getattr(self.__class__, name, None)
|
||||
if type(orig_accessor) in [ReverseOneToOneDescriptor, ForwardManyToOneDescriptor]:
|
||||
#print >>sys.stderr, '---------- replacing', name, orig_accessor, '->', model
|
||||
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)))
|
||||
|
||||
def _get_inheritance_relation_fields_and_models(self):
|
||||
"""helper function for __init__:
|
||||
determine names of all Django inheritance accessor member functions for type(self)"""
|
||||
|
||||
def add_model(model, field_name, result):
|
||||
result[field_name] = model
|
||||
|
||||
def add_model_if_regular(model, field_name, result):
|
||||
if (issubclass(model, models.Model)
|
||||
and model != models.Model
|
||||
and model != self.__class__
|
||||
and model != PolymorphicModel):
|
||||
add_model(model, field_name, result)
|
||||
|
||||
def add_all_super_models(model, result):
|
||||
for super_cls, field_to_super in model._meta.parents.items():
|
||||
if field_to_super is not None: #if not a link to a proxy model
|
||||
field_name = field_to_super.name #the field on model can have a different name to super_cls._meta.module_name, if the field is created manually using 'parent_link'
|
||||
add_model_if_regular(super_cls, field_name, result)
|
||||
add_all_super_models(super_cls, result)
|
||||
|
||||
def add_all_sub_models(super_cls, result):
|
||||
for sub_cls in super_cls.__subclasses__(): #go through all subclasses of model
|
||||
if super_cls in sub_cls._meta.parents: #super_cls may not be in sub_cls._meta.parents if super_cls is a proxy model
|
||||
field_to_super = sub_cls._meta.parents[super_cls] #get the field that links sub_cls to super_cls
|
||||
if field_to_super is not None: # if filed_to_super is not a link to a proxy model
|
||||
super_to_sub_related_field = field_to_super.rel
|
||||
if super_to_sub_related_field.related_name is None:
|
||||
#if related name is None the related field is the name of the subclass
|
||||
to_subclass_fieldname = sub_cls.__name__.lower()
|
||||
else:
|
||||
#otherwise use the given related name
|
||||
to_subclass_fieldname = super_to_sub_related_field.related_name
|
||||
|
||||
add_model_if_regular(sub_cls, to_subclass_fieldname, result)
|
||||
|
||||
result = {}
|
||||
add_all_super_models(self.__class__, result)
|
||||
add_all_sub_models(self.__class__, result)
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -1,229 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Seamless Polymorphic Inheritance for Django Models
|
||||
==================================================
|
||||
|
||||
Please see README.rst and DOCS.rst for further information.
|
||||
|
||||
Or on the Web:
|
||||
http://chrisglass.github.com/django_polymorphic/
|
||||
http://github.com/chrisglass/django_polymorphic
|
||||
|
||||
Copyright:
|
||||
This code and affiliated files are (C) by Bert Constantin and individual contributors.
|
||||
Please see LICENSE and AUTHORS for more information.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import six
|
||||
|
||||
from .base import PolymorphicModelBase
|
||||
from .manager import PolymorphicManager
|
||||
from .query_translate import translate_polymorphic_Q_object
|
||||
|
||||
###################################################################################
|
||||
### PolymorphicModel
|
||||
|
||||
class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
|
||||
"""
|
||||
Abstract base class that provides polymorphic behaviour
|
||||
for any model directly or indirectly derived from it.
|
||||
|
||||
For usage instructions & examples please see documentation.
|
||||
|
||||
PolymorphicModel declares one field for internal use (polymorphic_ctype)
|
||||
and provides a polymorphic manager as the default manager
|
||||
(and as 'objects').
|
||||
|
||||
PolymorphicModel overrides the save() and __init__ methods.
|
||||
|
||||
If your derived class overrides any of these methods as well, then you need
|
||||
to take care that you correctly call the method of the superclass, like:
|
||||
|
||||
super(YourClass,self).save(*args,**kwargs)
|
||||
"""
|
||||
|
||||
# for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
|
||||
polymorphic_model_marker = True
|
||||
|
||||
# for PolymorphicQuery, True => an overloaded __repr__ with nicer multi-line output is used by PolymorphicQuery
|
||||
polymorphic_query_multiline_output = False
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
# avoid ContentType related field accessor clash (an error emitted by model validation)
|
||||
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
|
||||
related_name='polymorphic_%(app_label)s.%(class)s_set+')
|
||||
|
||||
# some applications want to know the name of the fields that are added to its models
|
||||
polymorphic_internal_model_fields = ['polymorphic_ctype']
|
||||
|
||||
# Note that Django 1.5 removes these managers because the model is abstract.
|
||||
# They are pretended to be there by the metaclass in PolymorphicModelBase.get_inherited_managers()
|
||||
objects = PolymorphicManager()
|
||||
base_objects = models.Manager()
|
||||
|
||||
@classmethod
|
||||
def translate_polymorphic_Q_object(self_class, q):
|
||||
return translate_polymorphic_Q_object(self_class, q)
|
||||
|
||||
def pre_save_polymorphic(self):
|
||||
"""Normally not needed.
|
||||
This function may be called manually in special use-cases. When the object
|
||||
is saved for the first time, we store its real class in polymorphic_ctype.
|
||||
When the object later is retrieved by PolymorphicQuerySet, it uses this
|
||||
field to figure out the real class of this object
|
||||
(used by PolymorphicQuerySet._get_real_instances)
|
||||
"""
|
||||
if not self.polymorphic_ctype_id:
|
||||
self.polymorphic_ctype = ContentType.objects.get_for_model(self, for_concrete_model=False)
|
||||
pre_save_polymorphic.alters_data = True
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Overridden model save function which supports the polymorphism
|
||||
functionality (through pre_save_polymorphic)."""
|
||||
self.pre_save_polymorphic()
|
||||
return super(PolymorphicModel, self).save(*args, **kwargs)
|
||||
save.alters_data = True
|
||||
|
||||
def get_real_instance_class(self):
|
||||
"""
|
||||
Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the real class/type of these objects may be
|
||||
determined using this method.
|
||||
"""
|
||||
# the following line would be the easiest way to do this, but it produces sql queries
|
||||
# return self.polymorphic_ctype.model_class()
|
||||
# so we use the following version, which uses the ContentType manager cache.
|
||||
# Note that model_class() can return None for stale content types;
|
||||
# when the content type record still exists but no longer refers to an existing model.
|
||||
try:
|
||||
model = ContentType.objects.get_for_id(self.polymorphic_ctype_id).model_class()
|
||||
except AttributeError:
|
||||
# Django <1.6 workaround
|
||||
return None
|
||||
|
||||
# Protect against bad imports (dumpdata without --natural) or other
|
||||
# issues missing with the ContentType models.
|
||||
if model is not None \
|
||||
and not issubclass(model, self.__class__) \
|
||||
and not issubclass(model, self.__class__._meta.proxy_for_model):
|
||||
raise RuntimeError("ContentType {0} for {1} #{2} does not point to a subclass!".format(
|
||||
self.polymorphic_ctype_id, model, self.pk,
|
||||
))
|
||||
return model
|
||||
|
||||
def get_real_concrete_instance_class_id(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).pk
|
||||
|
||||
def get_real_concrete_instance_class(self):
|
||||
model_class = self.get_real_instance_class()
|
||||
if model_class is None:
|
||||
return None
|
||||
return ContentType.objects.get_for_model(model_class, for_concrete_model=True).model_class()
|
||||
|
||||
def get_real_instance(self):
|
||||
"""Normally not needed.
|
||||
If a non-polymorphic manager (like base_objects) has been used to
|
||||
retrieve objects, then the complete object with it's real class/type
|
||||
and all fields may be retrieved with this method.
|
||||
Each method call executes one db query (if necessary)."""
|
||||
real_model = self.get_real_instance_class()
|
||||
if real_model == self.__class__:
|
||||
return self
|
||||
return real_model.objects.get(pk=self.pk)
|
||||
|
||||
def __init__(self, * args, ** kwargs):
|
||||
"""Replace Django's inheritance accessor member functions for our model
|
||||
(self.__class__) with our own versions.
|
||||
We monkey patch them until a patch can be added to Django
|
||||
(which would probably be very small and make all of this obsolete).
|
||||
|
||||
If we have inheritance of the form ModelA -> ModelB ->ModelC then
|
||||
Django creates accessors like this:
|
||||
- ModelA: modelb
|
||||
- ModelB: modela_ptr, modelb, modelc
|
||||
- ModelC: modela_ptr, modelb, modelb_ptr, modelc
|
||||
|
||||
These accessors allow Django (and everyone else) to travel up and down
|
||||
the inheritance tree for the db object at hand.
|
||||
|
||||
The original Django accessors use our polymorphic manager.
|
||||
But they should not. So we replace them with our own accessors that use
|
||||
our appropriate base_objects manager.
|
||||
"""
|
||||
super(PolymorphicModel, self).__init__(*args, ** kwargs)
|
||||
|
||||
if self.__class__.polymorphic_super_sub_accessors_replaced:
|
||||
return
|
||||
self.__class__.polymorphic_super_sub_accessors_replaced = True
|
||||
|
||||
def create_accessor_function_for_model(model, accessor_name):
|
||||
def accessor_function(self):
|
||||
attr = model.base_objects.get(pk=self.pk)
|
||||
return attr
|
||||
return accessor_function
|
||||
|
||||
subclasses_and_superclasses_accessors = self._get_inheritance_relation_fields_and_models()
|
||||
|
||||
try:
|
||||
from django.db.models.fields.related import ReverseOneToOneDescriptor, ForwardManyToOneDescriptor
|
||||
except ImportError:
|
||||
# django < 1.9
|
||||
from django.db.models.fields.related import (
|
||||
SingleRelatedObjectDescriptor as ReverseOneToOneDescriptor,
|
||||
ReverseSingleRelatedObjectDescriptor as ForwardManyToOneDescriptor,
|
||||
)
|
||||
for name, model in subclasses_and_superclasses_accessors.items():
|
||||
orig_accessor = getattr(self.__class__, name, None)
|
||||
if type(orig_accessor) in [ReverseOneToOneDescriptor, ForwardManyToOneDescriptor]:
|
||||
#print >>sys.stderr, '---------- replacing', name, orig_accessor, '->', model
|
||||
setattr(self.__class__, name, property(create_accessor_function_for_model(model, name)))
|
||||
|
||||
def _get_inheritance_relation_fields_and_models(self):
|
||||
"""helper function for __init__:
|
||||
determine names of all Django inheritance accessor member functions for type(self)"""
|
||||
|
||||
def add_model(model, field_name, result):
|
||||
result[field_name] = model
|
||||
|
||||
def add_model_if_regular(model, field_name, result):
|
||||
if (issubclass(model, models.Model)
|
||||
and model != models.Model
|
||||
and model != self.__class__
|
||||
and model != PolymorphicModel):
|
||||
add_model(model, field_name, result)
|
||||
|
||||
def add_all_super_models(model, result):
|
||||
for super_cls, field_to_super in model._meta.parents.items():
|
||||
if field_to_super is not None: #if not a link to a proxy model
|
||||
field_name = field_to_super.name #the field on model can have a different name to super_cls._meta.module_name, if the field is created manually using 'parent_link'
|
||||
add_model_if_regular(super_cls, field_name, result)
|
||||
add_all_super_models(super_cls, result)
|
||||
|
||||
def add_all_sub_models(super_cls, result):
|
||||
for sub_cls in super_cls.__subclasses__(): #go through all subclasses of model
|
||||
if super_cls in sub_cls._meta.parents: #super_cls may not be in sub_cls._meta.parents if super_cls is a proxy model
|
||||
field_to_super = sub_cls._meta.parents[super_cls] #get the field that links sub_cls to super_cls
|
||||
if field_to_super is not None: # if filed_to_super is not a link to a proxy model
|
||||
super_to_sub_related_field = field_to_super.rel
|
||||
if super_to_sub_related_field.related_name is None:
|
||||
#if related name is None the related field is the name of the subclass
|
||||
to_subclass_fieldname = sub_cls.__name__.lower()
|
||||
else:
|
||||
#otherwise use the given related name
|
||||
to_subclass_fieldname = super_to_sub_related_field.related_name
|
||||
|
||||
add_model_if_regular(sub_cls, to_subclass_fieldname, result)
|
||||
|
||||
result = {}
|
||||
add_all_super_models(self.__class__, result)
|
||||
add_all_sub_models(self.__class__, result)
|
||||
return result
|
||||
|
|
@ -43,6 +43,14 @@ def transmogrify(cls, obj):
|
|||
###################################################################################
|
||||
### PolymorphicQuerySet
|
||||
|
||||
def _query_annotations(query):
|
||||
try:
|
||||
return query.annotations
|
||||
except AttributeError:
|
||||
# Django < 1.8
|
||||
return query.aggregates
|
||||
|
||||
|
||||
class PolymorphicQuerySet(QuerySet):
|
||||
"""
|
||||
QuerySet for PolymorphicModel
|
||||
|
|
@ -137,7 +145,18 @@ class PolymorphicQuerySet(QuerySet):
|
|||
qs = self.non_polymorphic()
|
||||
return super(PolymorphicQuerySet, qs).aggregate(*args, **kwargs)
|
||||
|
||||
# Since django_polymorphic 'V1.0 beta2', extra() always returns polymorphic results.^
|
||||
if django.VERSION >= (1, 9):
|
||||
# On Django < 1.9, 'qs.values(...)' returned a new special ValuesQuerySet
|
||||
# object, which our polymorphic modifications didn't apply to.
|
||||
# Starting with Django 1.9, the copy returned by 'qs.values(...)' has the
|
||||
# same class as 'qs', so our polymorphic modifications would apply.
|
||||
# We want to leave values queries untouched, so we set 'polymorphic_disabled'.
|
||||
def _values(self, *args, **kwargs):
|
||||
clone = super(PolymorphicQuerySet, self)._values(*args, **kwargs)
|
||||
clone.polymorphic_disabled = True
|
||||
return clone
|
||||
|
||||
# Since django_polymorphic 'V1.0 beta2', extra() always returns polymorphic results.
|
||||
# The resulting objects are required to have a unique primary key within the result set
|
||||
# (otherwise an error is thrown).
|
||||
# The "polymorphic" keyword argument is not supported anymore.
|
||||
|
|
@ -197,7 +216,7 @@ class PolymorphicQuerySet(QuerySet):
|
|||
for base_object in base_result_objects:
|
||||
ordered_id_list.append(base_object.pk)
|
||||
|
||||
# check if id of the result object occeres more than once - this can happen e.g. with base_objects.extra(tables=...)
|
||||
# check if id of the result object occurres more than once - this can happen e.g. with base_objects.extra(tables=...)
|
||||
if not base_object.pk in base_result_objects_by_id:
|
||||
base_result_objects_by_id[base_object.pk] = base_object
|
||||
|
||||
|
|
@ -239,8 +258,8 @@ class PolymorphicQuerySet(QuerySet):
|
|||
if real_class != real_concrete_class:
|
||||
real_object = transmogrify(real_class, real_object)
|
||||
|
||||
if self.query.aggregates:
|
||||
for anno_field_name in six.iterkeys(self.query.aggregates):
|
||||
if _query_annotations(self.query):
|
||||
for anno_field_name in six.iterkeys(_query_annotations(self.query)):
|
||||
attr = getattr(base_result_objects_by_id[o_pk], anno_field_name)
|
||||
setattr(real_object, anno_field_name, attr)
|
||||
|
||||
|
|
@ -255,8 +274,8 @@ class PolymorphicQuerySet(QuerySet):
|
|||
resultlist = [results[ordered_id] for ordered_id in ordered_id_list if ordered_id in results]
|
||||
|
||||
# set polymorphic_annotate_names in all objects (currently just used for debugging/printing)
|
||||
if self.query.aggregates:
|
||||
annotate_names = list(six.iterkeys(self.query.aggregates)) # get annotate field list
|
||||
if _query_annotations(self.query):
|
||||
annotate_names = list(six.iterkeys(_query_annotations(self.query))) # get annotate field list
|
||||
for real_object in resultlist:
|
||||
real_object.polymorphic_annotate_names = annotate_names
|
||||
|
||||
|
|
@ -289,7 +308,7 @@ class PolymorphicQuerySet(QuerySet):
|
|||
if self.polymorphic_disabled:
|
||||
for o in base_iter:
|
||||
yield o
|
||||
raise StopIteration
|
||||
return
|
||||
|
||||
while True:
|
||||
base_result_objects = []
|
||||
|
|
@ -309,7 +328,7 @@ class PolymorphicQuerySet(QuerySet):
|
|||
yield o
|
||||
|
||||
if reached_end:
|
||||
raise StopIteration
|
||||
return
|
||||
|
||||
def __repr__(self, *args, **kwargs):
|
||||
if self.model.polymorphic_query_multiline_output:
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ def translate_polymorphic_field_path(queryset_model, field_path):
|
|||
# so no tripple ClassName___field was intended.
|
||||
try:
|
||||
# rel = (field_object, model, direct, m2m)
|
||||
field = queryset_model._meta.get_field_by_name(classname)[0]
|
||||
field = queryset_model._meta.get_field(classname)
|
||||
if isinstance(field, RelatedObject):
|
||||
# Can also test whether the field exists in the related object to avoid ambiguity between
|
||||
# class names and field names, but that never happens when your class names are in CamelCase.
|
||||
|
|
@ -234,7 +234,7 @@ def _create_model_filter_Q(modellist, not_instance_of=False):
|
|||
if not modellist:
|
||||
return None
|
||||
|
||||
from .polymorphic_model import PolymorphicModel
|
||||
from .models import PolymorphicModel
|
||||
|
||||
if type(modellist) != list and type(modellist) != tuple:
|
||||
if issubclass(modellist, PolymorphicModel):
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ from __future__ import print_function
|
|||
import uuid
|
||||
import re
|
||||
import django
|
||||
from django.utils.unittest import skipIf
|
||||
try:
|
||||
from unittest import skipIf
|
||||
except ImportError:
|
||||
# python<2.7
|
||||
from django.utils.unittest import skipIf
|
||||
from django.db.models.query import QuerySet
|
||||
|
||||
from django.test import TestCase
|
||||
|
|
@ -15,9 +19,15 @@ from django.db import models
|
|||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import six
|
||||
|
||||
from polymorphic import PolymorphicModel, PolymorphicManager, PolymorphicQuerySet
|
||||
from polymorphic.models import PolymorphicModel
|
||||
from polymorphic.manager import PolymorphicManager
|
||||
from polymorphic.query import PolymorphicQuerySet
|
||||
from polymorphic import ShowFieldContent, ShowFieldType, ShowFieldTypeAndContent
|
||||
from polymorphic.tools_for_tests import UUIDField
|
||||
try:
|
||||
from django.db.models import UUIDField
|
||||
except ImportError:
|
||||
# django<1.8
|
||||
from polymorphic.tools_for_tests import UUIDField
|
||||
|
||||
|
||||
class PlainA(models.Model):
|
||||
|
|
@ -206,7 +216,7 @@ class Bottom(Middle):
|
|||
author = models.CharField(max_length=50)
|
||||
|
||||
class UUIDProject(ShowFieldTypeAndContent, PolymorphicModel):
|
||||
uuid_primary_key = UUIDField(primary_key = True)
|
||||
uuid_primary_key = UUIDField(primary_key = True, default=uuid.uuid1)
|
||||
topic = models.CharField(max_length = 30)
|
||||
class UUIDArtProject(UUIDProject):
|
||||
artist = models.CharField(max_length = 30)
|
||||
|
|
@ -214,7 +224,7 @@ class UUIDResearchProject(UUIDProject):
|
|||
supervisor = models.CharField(max_length = 30)
|
||||
|
||||
class UUIDPlainA(models.Model):
|
||||
uuid_primary_key = UUIDField(primary_key = True)
|
||||
uuid_primary_key = UUIDField(primary_key = True, default=uuid.uuid1)
|
||||
field1 = models.CharField(max_length=10)
|
||||
class UUIDPlainB(UUIDPlainA):
|
||||
field2 = models.CharField(max_length=10)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
####################################################################
|
||||
|
||||
# Compatibility module for Django < 1.8
|
||||
import uuid
|
||||
|
||||
from django import forms
|
||||
|
|
@ -9,6 +6,7 @@ from django.db import models
|
|||
from django.utils.encoding import smart_text
|
||||
from django.utils import six
|
||||
|
||||
|
||||
class UUIDVersionError(Exception):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,12 @@ if not settings.configured:
|
|||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
),
|
||||
TEMPLATE_CONTEXT_PROCESSORS = default_settings.TEMPLATE_CONTEXT_PROCESSORS + (
|
||||
TEMPLATE_CONTEXT_PROCESSORS=(
|
||||
# list() is only needed for older versions of django where this is
|
||||
# a tuple:
|
||||
list(default_settings.TEMPLATE_CONTEXT_PROCESSORS) + [
|
||||
'django.core.context_processors.request',
|
||||
]
|
||||
),
|
||||
TEST_RUNNER = 'django.test.runner.DiscoverRunner' if django.VERSION >= (1,7) else 'django.test.simple.DjangoTestSuiteRunner',
|
||||
INSTALLED_APPS = (
|
||||
|
|
|
|||
Loading…
Reference in New Issue