Opened 5 years ago

Closed 5 years ago

Last modified 5 years ago

#12344 closed (fixed)

select_related() uses always QuerySet and Query classes from originating Model

Reported by: jtiai Owned by: nobody
Component: Database layer (models, ORM) Version: master
Severity: Keywords: query orm
Cc: ikelly, mboersma Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

When having two models:

class ModelNormal(models.Model):
    geo = models.ForeignKey('ModelGeo')

class ModelGeo(models.Model):
    geom = models.PointField()
    objects = models.GeoManager()

When querying:

ModelNormal.objects.all().select_related()

Accessing objects causes traceback because Django used Manager instead of GeoManager for GeoModel instance(s).

Further investigation indicated that QSL queries didn't have any decorators that GeoManager does for geometry fields needed in Oracle.

Attachments (4)

select_related_fix_v1.diff (900 bytes) - added by jbronn 5 years ago.
select_related_fix_v2.diff (1.9 KB) - added by jbronn 5 years ago.
issue_12344.tar.gz (4.9 KB) - added by jtiai 5 years ago.
Sample case when select_related() fails to use custom compiler
custom_backend_example.tar.gz (3.3 KB) - added by anonymous 5 years ago.

Download all attachments as: .zip

Change History (9)

comment:1 Changed 5 years ago by ikelly

  • Cc ikelly mboersma added
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset

comment:2 Changed 5 years ago by jtiai

  • Component changed from GIS to Database layer (models, ORM)
  • Keywords gis Oracle removed
  • Summary changed from Mixing geo and nongeo with select_related() doesn't work with Oracle to select_related() uses always QuerySet and Query classes from originating Model

When you have models just like in description and both have different, custom QuerySet containing managers using .select_related() fails.

This is because Django assumes that all related models uses same QuerySet (and Query) as originating table, in case of example ModelNormal.

This is most visible with GeoDjango models since many of them uses custom decorations to produce WKT text from spatial column and trying to use normal, nongeomodel as a originating model and .select_related() fails with exceptions.

Note:

In multidb branch this appears to be problem of sql compiler that expects that all related models uses same compiler as originating model. Which for example in GeoDjango models case is not true - they have their own compiler that adds spatial field decorations to queries causing different errors on different spatial backends.

Changed 5 years ago by jbronn

comment:3 Changed 5 years ago by jtiai

Since multidb landed on trunk I update this ticket:

When using custom SQLCompiler that overrides get_columns code to mangle column selects instead of just getting fields as they are doesn't work with Django when using select_related() queryset method.

select_related() always uses it's own Manager SQLCompiler to construct SQL needed to query fields. (code that generates SQL for related fields is in django/trunk/django/db/models/sql/compiler.py around line 209). For example contributed gis application uses it's own thus creating it impossible to mix non-geo and geomodels.

Each related field should produce SQL using their own SQLCompiler instead of using originating model SQLCompiler which will produce incorrect SQL.

Changed 5 years ago by jbronn

Changed 5 years ago by jtiai

Sample case when select_related() fails to use custom compiler

Changed 5 years ago by anonymous

comment:4 Changed 5 years ago by jbronn

  • Resolution set to fixed
  • Status changed from new to closed

(In [12022]) Fixed #12344 -- Using select_related() on geographic fields with the Oracle spatial backend now works.

comment:5 Changed 5 years ago by jbronn

(In [12023]) [1.1.X] Fixed #12344 -- Using select_related() on geographic fields with the Oracle spatial backend now works.

Backport of r12022 from trunk.

Note: See TracTickets for help on using tickets.
Back to Top