class Lower(Func):function = 'LOWER'
Over the last 6 months we've been working on a fairly large refactor to expressions. As a brief catch up, expressions are currently F() expressions. I've expanded their scope to include Aggregates. As a by-product of this we can now use any kind of expressions in the .annotate() function, not just aggregates. Tim Martin has done some work based on this patch that extends support to .order_by() also. See the bottom of this post for links to the various discussions and patches. I highly recommend reading them - and for 3rd party backend maintainers - trying them out.
5. Are we jumping the gun and should we wait until the patches land before even discussing the above?
--To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/daf867fe-bedf-4134-bc92-728e2fa2c17f%40googlegroups.com.
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
def lower_case_function_override(self, compiler, connection):
setattr(Sum, 'as_' + connection.vendor, lower_case_function_override)
At the moment the API sanctions monkey patching by providing the as_vendor() method
class WindowFunction(Func):def as_sql(self, compiler, connection): # standard implementationreturn compiler.compile(self.expressions)def as_sqlite(self, compiler, connection): # unsupported first party backendraise NotSupportedException("WindowFunction not supported with SQLite")def as_mssql(self, compiler, connection): # third party backendself.function = "SlightlyDifferentFunctionName"return self.as_sql(compiler, connection)
Worth noting that Lookups now have exactly the same as_vendor API. The registration API simply allows you to overload the lookup table with a subclass rather than just monkeypatching.
This is harder here are the classes are used directly. We could provide an expressions registry I guess...
Marc
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/bc9df00a-3aa0-4307-885e-25c1bc59d77f%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/53A1C9F9.3090701%40oddbird.net.
class Lower(Func):function = 'LOWER'
def mongo_lower(self, compiler, connection):self.function = '$toLower'return self.as_sql(compiler, connection)
setattr(Lower, 'as_mongo', mongo_lower)
# or perhaps a friendlier registration method:Lower.register('mongo', mongo_lower) # register would internally just call the setattr() above
On 06/18/2014 02:59 PM, Aymeric Augustin wrote:
> 2014-06-18 19:18 GMT+02:00 Carl Meyer <ca...@oddbird.net
> <mailto:ca...@oddbird.net>>:
>
> If you need a Function in your
> project (whether provided by Django or by a third-party library), and
> the Function doesn't natively support the database backend you're using,
> you can simply subclass it, add the appropriate as_vendor method, and
> use your subclass.
>
>
> I was thinking about third-party database backends. How are they expected
> to ship customised implementations of Functions included in Django?
Yes, I wasn't thinking about this case in my first mail, and I agree
that this is a serious problem for the pure as_vendor() approach; it's
not acceptable IMO to expect third-party database backends to
monkeypatch their as_vendor() methods onto the built-in Function classes.
I think database backends should have a hook to override the SQL
implementation of any given Function. I don't think this needs to imply
pushing the default implementation of all Functions down into the
database backend (that just makes life unnecessarily difficult for
third-party Functions).
This is possible to do by supplying a custom SQLCompiler class for the backend, and overriding its .compile() method.
Personally I don't see usage of as_vendor() as that problematic. Supplying as_vendor in first DatabaseWrapper.__init__() should work. Yes, we are dynamically altering the node's class. But we are just using the __dict__ of the node to store backend specific implementation instead of using a dictionary somewhere in the backend for the implementation. Using the __dict__ has the advantage that inheritance works automatically.
As multiple core developers have said they don't like as_vendor() for 3rd party backend support it might be better to offer official hook for altering the generated SQL. That is, change the SQLCompiler.compile() method from
def compile(self, node):
vendor_impl = getattr(
node, 'as_' + self.connection.vendor, None)
if vendor_impl:
return vendor_impl(self, self.connection)
else:
return node.as_sql(self, self.connection)
to
def compile(self, node):
ret = self.connection.compile(node)
The main reason for offering a hook is that overriding SQLCompiler is a bit problematic. There exists various subclasses of SQLCompiler, and the backend needs to support those, too.
If this is going to be moved off SQLCompiler, I think DatabaseOperations is a better place for it because we already have DatabaseOperatoins.combine_expression. The compile method will need access to the compiler to match the signature of as_sql (and it is necessary in some cases to inspect the query).
The main reason for offering a hook is that overriding SQLCompiler is a bit problematic. There exists various subclasses of SQLCompiler, and the backend needs to support those, too.
All of the subclasses are going to inherit SQLCompiler.compile and do the same exact logic. I don't understand what problem is caused by overriding compile that is solved by a function call to compile on some other class.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/1404126051.9408.155.camel%40TTY32.
class DatabaseWrapper(BaseDatabaseWrapper):def __init__(self, *args, **kwargs):self.patch_functions()def patch_functions(self):"""all other functions work correctly, so we justneed to patch Length"""from django.db.models.functions import Lengthdef new_length(self, compiler, connection):self.function = 'LEN' # rather than 'LENGTH'return self.as_sql(compiler, connection)Length.as_mssql = new_length
--
You received this message because you are subscribed to the Google Groups "Django developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-develop...@googlegroups.com.
To post to this group, send email to django-d...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-developers.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-developers/1404228748.9408.189.camel%40TTY32.
def compile(self, node):sql, params = self.connection.ops.compile_node(self, node)if sql is not None:return sql, params
vendor_impl = getattr(node, 'as_' + self.connection.vendor, None)if vendor_impl:return vendor_impl(self, self.connection)else:return node.as_sql(self, self.connection)