From 4a4cfd82a249f9439f8c81a98d852e1c6ed9b2d0 Mon Sep 17 00:00:00 2001 From: Bert Constantin Date: Fri, 12 Nov 2010 06:33:50 +0100 Subject: [PATCH] translate_polymorphic_Q_object: fixed test case, and made the function a member of PolymorphicObject. Minor test fixes: warnings fixed, test_tool.py renamed as it's no test --- CHANGES.html | 3 ++ CHANGES.rst | 3 ++ DOCS.html | 32 ++++++++----------- DOCS.rst | 31 ++++++++---------- polymorphic/polymorphic_model.py | 5 +++ polymorphic/tests.py | 10 ++++-- .../{test_tools.py => tools_for_tests.py} | 4 +-- 7 files changed, 47 insertions(+), 41 deletions(-) rename polymorphic/{test_tools.py => tools_for_tests.py} (98%) diff --git a/CHANGES.html b/CHANGES.html index b008805..f7d0b15 100644 --- a/CHANGES.html +++ b/CHANGES.html @@ -276,6 +276,9 @@ more simple and intuitive use:

>>> ModelA.objects.all() +
  • added member function: +normal_q_object = ModelA.translate_polymorphic_Q_object(enhanced_q_object)

    +
  • misc changes/improvements

  • diff --git a/CHANGES.rst b/CHANGES.rst index 2772f3a..b35344f 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -60,6 +60,9 @@ New Features and API changes in Beta 2 since Beta 1 >>> ModelA.objects.all() +* added member function: + ``normal_q_object = ModelA.translate_polymorphic_Q_object(enhanced_q_object)`` + * misc changes/improvements Bugfixes diff --git a/DOCS.html b/DOCS.html index a8b886c..581f29e 100644 --- a/DOCS.html +++ b/DOCS.html @@ -310,7 +310,7 @@ output the ShowFieldType mixin has been used (
  • Fully automatic - generally makes sure that the same objects are returned from the database that were stored there, regardless how they are retrieved
  • -
  • Only on models that request polymorphic behaviour however (and the +
  • Only on models that request polymorphic behaviour (and the models inheriting from them)
  • Full support for ForeignKeys, ManyToManyFields and OneToToneFields
  • Filtering for classes, equivalent to python's isinstance(): @@ -527,23 +527,17 @@ in the future).
  • Using enhanced Q-objects in any Places

    -

    Sometimes it would be nice to be able to use the enhanced filter-definitions/Q-objects -outside of polymorphic models/querysets. Example (using limit_choices_to -to filter the selection of objects in the admin):

    +

    The queryset enhancements (e.g. instance_of) 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:

    -class MyModel(models.Model):
    -    somekey = model.ForeignKey(Model2A,
    -        limit_choices_to = Q(instance_of=Model2B) )
    -
    -

    instance_of is a django_polymorphic-specific enhancement of Q objects, which the -vanilla django function ForeignKey cannot process. In such cases you can do:

    -
    -from polymorphic import translate_polymorphic_Q_object
    -
    -class MyModel(models.Model):
    -    somekey = model.ForeignKey(Model2A,
    -        limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) )
    +normal_q_object = ModelA.translate_polymorphic_Q_object( Q(instance_of=Model2B) )
     
    +

    This function cannot be used at model creation time however (in models.py), +as it may need to access the ContentTypes database table.

    Nicely Displaying Polymorphic Querysets

    @@ -677,7 +671,7 @@ SQL query for every object in the result which is not of class

    Performance Problems with PostgreSQL, MySQL and SQLite3

    -

    Current relational DBM systems seem to be have general problems with +

    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. @@ -690,12 +684,12 @@ multi table Model inheritance.

    Restrictions & Caveats

      -
    • Database Performance regarding concrete Model inheritance in general +
    • Database Performance regarding concrete Model inheritance in general. Please see "Performance Problems" above.
    • Queryset methods values(), values_list(), select_related(), defer() and only() are not yet fully supported (see above). extra() has one restriction: the resulting objects are required to have -a unique primary key within the result set
    • +a unique primary key within the result set.
    • Django Admin Integration: There currently is no specific admin integration, but it would most likely make sense to have one.
    • Diamond shaped inheritance: There seems to be a general problem diff --git a/DOCS.rst b/DOCS.rst index 94e70a8..ded88dd 100644 --- a/DOCS.rst +++ b/DOCS.rst @@ -78,7 +78,7 @@ List of Features * Fully automatic - generally makes sure that the same objects are returned from the database that were stored there, regardless how they are retrieved -* Only on models that request polymorphic behaviour however (and the +* Only on models that request polymorphic behaviour (and the models inheriting from them) * Full support for ForeignKeys, ManyToManyFields and OneToToneFields * Filtering for classes, equivalent to python's isinstance(): @@ -319,22 +319,17 @@ About Queryset Methods Using enhanced Q-objects in any Places -------------------------------------- -Sometimes it would be nice to be able to use the enhanced filter-definitions/Q-objects -outside of polymorphic models/querysets. Example (using ``limit_choices_to`` -to filter the selection of objects in the admin):: +The queryset enhancements (e.g. ``instance_of``) 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:: - class MyModel(models.Model): - somekey = model.ForeignKey(Model2A, - limit_choices_to = Q(instance_of=Model2B) ) + normal_q_object = ModelA.translate_polymorphic_Q_object( Q(instance_of=Model2B) ) -``instance_of`` is a django_polymorphic-specific enhancement of Q objects, which the -vanilla django function ``ForeignKey`` cannot process. In such cases you can do:: - - from polymorphic import translate_polymorphic_Q_object - - class MyModel(models.Model): - somekey = model.ForeignKey(Model2A, - limit_choices_to = translate_polymorphic_Q_object( Model2A, Q(instance_of=Model2B) ) ) +This function cannot be used at model creation time however (in models.py), +as it may need to access the ContentTypes database table. Nicely Displaying Polymorphic Querysets @@ -482,7 +477,7 @@ that it only needs one sql request per *object type*, and not *per object*. Performance Problems with PostgreSQL, MySQL and SQLite3 ------------------------------------------------------- -Current relational DBM systems seem to be have general problems with +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. @@ -501,13 +496,13 @@ Please also see this `post (and comments) from Jacob Kaplan-Moss`_. Restrictions & Caveats ====================== -* Database Performance regarding concrete Model inheritance in general +* Database Performance regarding concrete Model inheritance in general. Please see "Performance Problems" above. * Queryset methods ``values()``, ``values_list()``, ``select_related()``, ``defer()`` and ``only()`` are not yet fully supported (see above). ``extra()`` has one restriction: the resulting objects are required to have - a unique primary key within the result set + a unique primary key within the result set. * Django Admin Integration: There currently is no specific admin integration, but it would most likely make sense to have one. diff --git a/polymorphic/polymorphic_model.py b/polymorphic/polymorphic_model.py index c9acd30..99adf45 100644 --- a/polymorphic/polymorphic_model.py +++ b/polymorphic/polymorphic_model.py @@ -27,6 +27,7 @@ from base import PolymorphicModelBase from manager import PolymorphicManager from query import PolymorphicQuerySet from showfields import ShowFieldType +from query_translate import translate_polymorphic_Q_object ################################################################################### @@ -76,6 +77,10 @@ class PolymorphicModel(models.Model): 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 diff --git a/polymorphic/tests.py b/polymorphic/tests.py index 64f5105..4984968 100644 --- a/polymorphic/tests.py +++ b/polymorphic/tests.py @@ -141,7 +141,7 @@ class BlogEntry(ShowFieldTypeAndContent, PolymorphicModel): text = models.CharField(max_length=10) class BlogEntry_limit_choices_to(ShowFieldTypeAndContent, PolymorphicModel): - blog = models.ForeignKey(BlogBase, limit_choices_to=translate_polymorphic_Q_object(BlogBase, Q(instance_of=BlogA) ) ) + blog = models.ForeignKey(BlogBase) text = models.CharField(max_length=10) class ModelFieldNameTest(ShowFieldType, PolymorphicModel): @@ -159,7 +159,7 @@ class InitTestModelSubclass(InitTestModel): # UUID tests won't work with Django 1.1 if not (django_VERSION[0] <= 1 and django_VERSION[1] <= 1): - try: from polymorphic.test_tools import UUIDField + try: from polymorphic.tools_for_tests import UUIDField except: pass if 'UUIDField' in globals(): import uuid @@ -369,6 +369,12 @@ __test__ = {"doctest": """ , ] +# translate_polymorphic_Q_object +>>> q=Model2A.translate_polymorphic_Q_object( Q(instance_of=Model2C) ) +>>> Model2A.objects.filter(q) +[ , + ] + ### test inheritance pointers & _base_managers diff --git a/polymorphic/test_tools.py b/polymorphic/tools_for_tests.py similarity index 98% rename from polymorphic/test_tools.py rename to polymorphic/tools_for_tests.py index 3bdb639..3d93201 100644 --- a/polymorphic/test_tools.py +++ b/polymorphic/tools_for_tests.py @@ -82,7 +82,7 @@ class UUIDField(models.CharField): else: raise UUIDVersionError("UUID version %s is not valid." % self.version) - def db_type(self): + def db_type(self, connection): from django.conf import settings return UUIDField._CREATE_COLUMN_TYPES.get(settings.DATABASE_ENGINE, "char(%s)" % self.max_length) @@ -122,7 +122,7 @@ class UUIDField(models.CharField): setattr(model_instance, self.attname, value) return value - def get_db_prep_value(self, value): + def get_db_prep_value(self, value, connection, prepared): """Casts uuid.UUID values into the format expected by the back end for use in queries""" if isinstance(value, uuid.UUID): return smart_unicode(value)