Got basic webpage working. Need to make base page, static folders
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
from .base import (
|
||||
Cast, Coalesce, Concat, ConcatPair, Greatest, Least, Length, Lower, Now,
|
||||
StrIndex, Substr, Upper,
|
||||
)
|
||||
from .datetime import (
|
||||
Extract, ExtractDay, ExtractHour, ExtractMinute, ExtractMonth,
|
||||
ExtractQuarter, ExtractSecond, ExtractWeek, ExtractWeekDay, ExtractYear,
|
||||
Trunc, TruncDate, TruncDay, TruncHour, TruncMinute, TruncMonth,
|
||||
TruncQuarter, TruncSecond, TruncTime, TruncYear,
|
||||
)
|
||||
from .window import (
|
||||
CumeDist, DenseRank, FirstValue, Lag, LastValue, Lead, NthValue, Ntile,
|
||||
PercentRank, Rank, RowNumber,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# base
|
||||
'Cast', 'Coalesce', 'Concat', 'ConcatPair', 'Greatest', 'Least', 'Length',
|
||||
'Lower', 'Now', 'StrIndex', 'Substr', 'Upper',
|
||||
# datetime
|
||||
'Extract', 'ExtractDay', 'ExtractHour', 'ExtractMinute', 'ExtractMonth',
|
||||
'ExtractQuarter', 'ExtractSecond', 'ExtractWeek', 'ExtractWeekDay',
|
||||
'ExtractYear', 'Trunc', 'TruncDate', 'TruncDay', 'TruncHour', 'TruncMinute',
|
||||
'TruncMonth', 'TruncQuarter', 'TruncSecond', 'TruncTime', 'TruncYear',
|
||||
# window
|
||||
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
|
||||
'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber',
|
||||
]
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
Classes that represent database functions.
|
||||
"""
|
||||
from django.db.models import Func, Transform, Value, fields
|
||||
|
||||
|
||||
class Cast(Func):
|
||||
"""Coerce an expression to a new field type."""
|
||||
function = 'CAST'
|
||||
template = '%(function)s(%(expressions)s AS %(db_type)s)'
|
||||
|
||||
def __init__(self, expression, output_field):
|
||||
super().__init__(expression, output_field=output_field)
|
||||
|
||||
def as_sql(self, compiler, connection, **extra_context):
|
||||
extra_context['db_type'] = self.output_field.cast_db_type(connection)
|
||||
return super().as_sql(compiler, connection, **extra_context)
|
||||
|
||||
def as_postgresql(self, compiler, connection):
|
||||
# CAST would be valid too, but the :: shortcut syntax is more readable.
|
||||
return self.as_sql(compiler, connection, template='%(expressions)s::%(db_type)s')
|
||||
|
||||
|
||||
class Coalesce(Func):
|
||||
"""Return, from left to right, the first non-null expression."""
|
||||
function = 'COALESCE'
|
||||
|
||||
def __init__(self, *expressions, **extra):
|
||||
if len(expressions) < 2:
|
||||
raise ValueError('Coalesce must take at least two expressions')
|
||||
super().__init__(*expressions, **extra)
|
||||
|
||||
def as_oracle(self, compiler, connection):
|
||||
# we can't mix TextField (NCLOB) and CharField (NVARCHAR), so convert
|
||||
# all fields to NCLOB when we expect NCLOB
|
||||
if self.output_field.get_internal_type() == 'TextField':
|
||||
class ToNCLOB(Func):
|
||||
function = 'TO_NCLOB'
|
||||
|
||||
expressions = [
|
||||
ToNCLOB(expression) for expression in self.get_source_expressions()]
|
||||
clone = self.copy()
|
||||
clone.set_source_expressions(expressions)
|
||||
return super(Coalesce, clone).as_sql(compiler, connection)
|
||||
return self.as_sql(compiler, connection)
|
||||
|
||||
|
||||
class ConcatPair(Func):
|
||||
"""
|
||||
Concatenate two arguments together. This is used by `Concat` because not
|
||||
all backend databases support more than two arguments.
|
||||
"""
|
||||
function = 'CONCAT'
|
||||
|
||||
def as_sqlite(self, compiler, connection):
|
||||
coalesced = self.coalesce()
|
||||
return super(ConcatPair, coalesced).as_sql(
|
||||
compiler, connection, template='%(expressions)s', arg_joiner=' || '
|
||||
)
|
||||
|
||||
def as_mysql(self, compiler, connection):
|
||||
# Use CONCAT_WS with an empty separator so that NULLs are ignored.
|
||||
return super().as_sql(
|
||||
compiler, connection, function='CONCAT_WS', template="%(function)s('', %(expressions)s)"
|
||||
)
|
||||
|
||||
def coalesce(self):
|
||||
# null on either side results in null for expression, wrap with coalesce
|
||||
c = self.copy()
|
||||
expressions = [
|
||||
Coalesce(expression, Value('')) for expression in c.get_source_expressions()
|
||||
]
|
||||
c.set_source_expressions(expressions)
|
||||
return c
|
||||
|
||||
|
||||
class Concat(Func):
|
||||
"""
|
||||
Concatenate text fields together. Backends that result in an entire
|
||||
null expression when any arguments are null will wrap each argument in
|
||||
coalesce functions to ensure a non-null result.
|
||||
"""
|
||||
function = None
|
||||
template = "%(expressions)s"
|
||||
|
||||
def __init__(self, *expressions, **extra):
|
||||
if len(expressions) < 2:
|
||||
raise ValueError('Concat must take at least two expressions')
|
||||
paired = self._paired(expressions)
|
||||
super().__init__(paired, **extra)
|
||||
|
||||
def _paired(self, expressions):
|
||||
# wrap pairs of expressions in successive concat functions
|
||||
# exp = [a, b, c, d]
|
||||
# -> ConcatPair(a, ConcatPair(b, ConcatPair(c, d))))
|
||||
if len(expressions) == 2:
|
||||
return ConcatPair(*expressions)
|
||||
return ConcatPair(expressions[0], self._paired(expressions[1:]))
|
||||
|
||||
|
||||
class Greatest(Func):
|
||||
"""
|
||||
Return the maximum expression.
|
||||
|
||||
If any expression is null the return value is database-specific:
|
||||
On Postgres, the maximum not-null expression is returned.
|
||||
On MySQL, Oracle, and SQLite, if any expression is null, null is returned.
|
||||
"""
|
||||
function = 'GREATEST'
|
||||
|
||||
def __init__(self, *expressions, **extra):
|
||||
if len(expressions) < 2:
|
||||
raise ValueError('Greatest must take at least two expressions')
|
||||
super().__init__(*expressions, **extra)
|
||||
|
||||
def as_sqlite(self, compiler, connection):
|
||||
"""Use the MAX function on SQLite."""
|
||||
return super().as_sqlite(compiler, connection, function='MAX')
|
||||
|
||||
|
||||
class Least(Func):
|
||||
"""
|
||||
Return the minimum expression.
|
||||
|
||||
If any expression is null the return value is database-specific:
|
||||
On Postgres, return the minimum not-null expression.
|
||||
On MySQL, Oracle, and SQLite, if any expression is null, return null.
|
||||
"""
|
||||
function = 'LEAST'
|
||||
|
||||
def __init__(self, *expressions, **extra):
|
||||
if len(expressions) < 2:
|
||||
raise ValueError('Least must take at least two expressions')
|
||||
super().__init__(*expressions, **extra)
|
||||
|
||||
def as_sqlite(self, compiler, connection):
|
||||
"""Use the MIN function on SQLite."""
|
||||
return super().as_sqlite(compiler, connection, function='MIN')
|
||||
|
||||
|
||||
class Length(Transform):
|
||||
"""Return the number of characters in the expression."""
|
||||
function = 'LENGTH'
|
||||
lookup_name = 'length'
|
||||
output_field = fields.IntegerField()
|
||||
|
||||
def as_mysql(self, compiler, connection):
|
||||
return super().as_sql(compiler, connection, function='CHAR_LENGTH')
|
||||
|
||||
|
||||
class Lower(Transform):
|
||||
function = 'LOWER'
|
||||
lookup_name = 'lower'
|
||||
|
||||
|
||||
class Now(Func):
|
||||
template = 'CURRENT_TIMESTAMP'
|
||||
output_field = fields.DateTimeField()
|
||||
|
||||
def as_postgresql(self, compiler, connection):
|
||||
# Postgres' CURRENT_TIMESTAMP means "the time at the start of the
|
||||
# transaction". We use STATEMENT_TIMESTAMP to be cross-compatible with
|
||||
# other databases.
|
||||
return self.as_sql(compiler, connection, template='STATEMENT_TIMESTAMP()')
|
||||
|
||||
|
||||
class StrIndex(Func):
|
||||
"""
|
||||
Return a positive integer corresponding to the 1-indexed position of the
|
||||
first occurrence of a substring inside another string, or 0 if the
|
||||
substring is not found.
|
||||
"""
|
||||
function = 'INSTR'
|
||||
arity = 2
|
||||
output_field = fields.IntegerField()
|
||||
|
||||
def as_postgresql(self, compiler, connection):
|
||||
return super().as_sql(compiler, connection, function='STRPOS')
|
||||
|
||||
|
||||
class Substr(Func):
|
||||
function = 'SUBSTRING'
|
||||
|
||||
def __init__(self, expression, pos, length=None, **extra):
|
||||
"""
|
||||
expression: the name of a field, or an expression returning a string
|
||||
pos: an integer > 0, or an expression returning an integer
|
||||
length: an optional number of characters to return
|
||||
"""
|
||||
if not hasattr(pos, 'resolve_expression'):
|
||||
if pos < 1:
|
||||
raise ValueError("'pos' must be greater than 0")
|
||||
expressions = [expression, pos]
|
||||
if length is not None:
|
||||
expressions.append(length)
|
||||
super().__init__(*expressions, **extra)
|
||||
|
||||
def as_sqlite(self, compiler, connection):
|
||||
return super().as_sql(compiler, connection, function='SUBSTR')
|
||||
|
||||
def as_oracle(self, compiler, connection):
|
||||
return super().as_sql(compiler, connection, function='SUBSTR')
|
||||
|
||||
|
||||
class Upper(Transform):
|
||||
function = 'UPPER'
|
||||
lookup_name = 'upper'
|
||||
@@ -0,0 +1,277 @@
|
||||
from datetime import datetime
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import (
|
||||
DateField, DateTimeField, DurationField, Field, IntegerField, TimeField,
|
||||
Transform,
|
||||
)
|
||||
from django.db.models.lookups import (
|
||||
YearExact, YearGt, YearGte, YearLt, YearLte,
|
||||
)
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class TimezoneMixin:
|
||||
tzinfo = None
|
||||
|
||||
def get_tzname(self):
|
||||
# Timezone conversions must happen to the input datetime *before*
|
||||
# applying a function. 2015-12-31 23:00:00 -02:00 is stored in the
|
||||
# database as 2016-01-01 01:00:00 +00:00. Any results should be
|
||||
# based on the input datetime not the stored datetime.
|
||||
tzname = None
|
||||
if settings.USE_TZ:
|
||||
if self.tzinfo is None:
|
||||
tzname = timezone.get_current_timezone_name()
|
||||
else:
|
||||
tzname = timezone._get_timezone_name(self.tzinfo)
|
||||
return tzname
|
||||
|
||||
|
||||
class Extract(TimezoneMixin, Transform):
|
||||
lookup_name = None
|
||||
output_field = IntegerField()
|
||||
|
||||
def __init__(self, expression, lookup_name=None, tzinfo=None, **extra):
|
||||
if self.lookup_name is None:
|
||||
self.lookup_name = lookup_name
|
||||
if self.lookup_name is None:
|
||||
raise ValueError('lookup_name must be provided')
|
||||
self.tzinfo = tzinfo
|
||||
super().__init__(expression, **extra)
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
sql, params = compiler.compile(self.lhs)
|
||||
lhs_output_field = self.lhs.output_field
|
||||
if isinstance(lhs_output_field, DateTimeField):
|
||||
tzname = self.get_tzname()
|
||||
sql = connection.ops.datetime_extract_sql(self.lookup_name, sql, tzname)
|
||||
elif isinstance(lhs_output_field, DateField):
|
||||
sql = connection.ops.date_extract_sql(self.lookup_name, sql)
|
||||
elif isinstance(lhs_output_field, TimeField):
|
||||
sql = connection.ops.time_extract_sql(self.lookup_name, sql)
|
||||
elif isinstance(lhs_output_field, DurationField):
|
||||
if not connection.features.has_native_duration_field:
|
||||
raise ValueError('Extract requires native DurationField database support.')
|
||||
sql = connection.ops.time_extract_sql(self.lookup_name, sql)
|
||||
else:
|
||||
# resolve_expression has already validated the output_field so this
|
||||
# assert should never be hit.
|
||||
assert False, "Tried to Extract from an invalid type."
|
||||
return sql, params
|
||||
|
||||
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
|
||||
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
|
||||
field = copy.lhs.output_field
|
||||
if not isinstance(field, (DateField, DateTimeField, TimeField, DurationField)):
|
||||
raise ValueError(
|
||||
'Extract input expression must be DateField, DateTimeField, '
|
||||
'TimeField, or DurationField.'
|
||||
)
|
||||
# Passing dates to functions expecting datetimes is most likely a mistake.
|
||||
if type(field) == DateField and copy.lookup_name in ('hour', 'minute', 'second'):
|
||||
raise ValueError(
|
||||
"Cannot extract time component '%s' from DateField '%s'. " % (copy.lookup_name, field.name)
|
||||
)
|
||||
return copy
|
||||
|
||||
|
||||
class ExtractYear(Extract):
|
||||
lookup_name = 'year'
|
||||
|
||||
|
||||
class ExtractMonth(Extract):
|
||||
lookup_name = 'month'
|
||||
|
||||
|
||||
class ExtractDay(Extract):
|
||||
lookup_name = 'day'
|
||||
|
||||
|
||||
class ExtractWeek(Extract):
|
||||
"""
|
||||
Return 1-52 or 53, based on ISO-8601, i.e., Monday is the first of the
|
||||
week.
|
||||
"""
|
||||
lookup_name = 'week'
|
||||
|
||||
|
||||
class ExtractWeekDay(Extract):
|
||||
"""
|
||||
Return Sunday=1 through Saturday=7.
|
||||
|
||||
To replicate this in Python: (mydatetime.isoweekday() % 7) + 1
|
||||
"""
|
||||
lookup_name = 'week_day'
|
||||
|
||||
|
||||
class ExtractQuarter(Extract):
|
||||
lookup_name = 'quarter'
|
||||
|
||||
|
||||
class ExtractHour(Extract):
|
||||
lookup_name = 'hour'
|
||||
|
||||
|
||||
class ExtractMinute(Extract):
|
||||
lookup_name = 'minute'
|
||||
|
||||
|
||||
class ExtractSecond(Extract):
|
||||
lookup_name = 'second'
|
||||
|
||||
|
||||
DateField.register_lookup(ExtractYear)
|
||||
DateField.register_lookup(ExtractMonth)
|
||||
DateField.register_lookup(ExtractDay)
|
||||
DateField.register_lookup(ExtractWeekDay)
|
||||
DateField.register_lookup(ExtractWeek)
|
||||
DateField.register_lookup(ExtractQuarter)
|
||||
|
||||
TimeField.register_lookup(ExtractHour)
|
||||
TimeField.register_lookup(ExtractMinute)
|
||||
TimeField.register_lookup(ExtractSecond)
|
||||
|
||||
DateTimeField.register_lookup(ExtractHour)
|
||||
DateTimeField.register_lookup(ExtractMinute)
|
||||
DateTimeField.register_lookup(ExtractSecond)
|
||||
|
||||
ExtractYear.register_lookup(YearExact)
|
||||
ExtractYear.register_lookup(YearGt)
|
||||
ExtractYear.register_lookup(YearGte)
|
||||
ExtractYear.register_lookup(YearLt)
|
||||
ExtractYear.register_lookup(YearLte)
|
||||
|
||||
|
||||
class TruncBase(TimezoneMixin, Transform):
|
||||
kind = None
|
||||
tzinfo = None
|
||||
|
||||
def __init__(self, expression, output_field=None, tzinfo=None, **extra):
|
||||
self.tzinfo = tzinfo
|
||||
super().__init__(expression, output_field=output_field, **extra)
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
inner_sql, inner_params = compiler.compile(self.lhs)
|
||||
# Escape any params because trunc_sql will format the string.
|
||||
inner_sql = inner_sql.replace('%s', '%%s')
|
||||
if isinstance(self.output_field, DateTimeField):
|
||||
tzname = self.get_tzname()
|
||||
sql = connection.ops.datetime_trunc_sql(self.kind, inner_sql, tzname)
|
||||
elif isinstance(self.output_field, DateField):
|
||||
sql = connection.ops.date_trunc_sql(self.kind, inner_sql)
|
||||
elif isinstance(self.output_field, TimeField):
|
||||
sql = connection.ops.time_trunc_sql(self.kind, inner_sql)
|
||||
else:
|
||||
raise ValueError('Trunc only valid on DateField, TimeField, or DateTimeField.')
|
||||
return sql, inner_params
|
||||
|
||||
def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False):
|
||||
copy = super().resolve_expression(query, allow_joins, reuse, summarize, for_save)
|
||||
field = copy.lhs.output_field
|
||||
# DateTimeField is a subclass of DateField so this works for both.
|
||||
assert isinstance(field, (DateField, TimeField)), (
|
||||
"%r isn't a DateField, TimeField, or DateTimeField." % field.name
|
||||
)
|
||||
# If self.output_field was None, then accessing the field will trigger
|
||||
# the resolver to assign it to self.lhs.output_field.
|
||||
if not isinstance(copy.output_field, (DateField, DateTimeField, TimeField)):
|
||||
raise ValueError('output_field must be either DateField, TimeField, or DateTimeField')
|
||||
# Passing dates or times to functions expecting datetimes is most
|
||||
# likely a mistake.
|
||||
class_output_field = self.__class__.output_field if isinstance(self.__class__.output_field, Field) else None
|
||||
output_field = class_output_field or copy.output_field
|
||||
has_explicit_output_field = class_output_field or field.__class__ is not copy.output_field.__class__
|
||||
if type(field) == DateField and (
|
||||
isinstance(output_field, DateTimeField) or copy.kind in ('hour', 'minute', 'second', 'time')):
|
||||
raise ValueError("Cannot truncate DateField '%s' to %s. " % (
|
||||
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
|
||||
))
|
||||
elif isinstance(field, TimeField) and (
|
||||
isinstance(output_field, DateTimeField) or copy.kind in ('year', 'quarter', 'month', 'day', 'date')):
|
||||
raise ValueError("Cannot truncate TimeField '%s' to %s. " % (
|
||||
field.name, output_field.__class__.__name__ if has_explicit_output_field else 'DateTimeField'
|
||||
))
|
||||
return copy
|
||||
|
||||
def convert_value(self, value, expression, connection):
|
||||
if isinstance(self.output_field, DateTimeField):
|
||||
if settings.USE_TZ:
|
||||
if value is None:
|
||||
raise ValueError(
|
||||
"Database returned an invalid datetime value. "
|
||||
"Are time zone definitions for your database installed?"
|
||||
)
|
||||
value = value.replace(tzinfo=None)
|
||||
value = timezone.make_aware(value, self.tzinfo)
|
||||
elif isinstance(value, datetime):
|
||||
if isinstance(self.output_field, DateField):
|
||||
value = value.date()
|
||||
elif isinstance(self.output_field, TimeField):
|
||||
value = value.time()
|
||||
return value
|
||||
|
||||
|
||||
class Trunc(TruncBase):
|
||||
|
||||
def __init__(self, expression, kind, output_field=None, tzinfo=None, **extra):
|
||||
self.kind = kind
|
||||
super().__init__(expression, output_field=output_field, tzinfo=tzinfo, **extra)
|
||||
|
||||
|
||||
class TruncYear(TruncBase):
|
||||
kind = 'year'
|
||||
|
||||
|
||||
class TruncQuarter(TruncBase):
|
||||
kind = 'quarter'
|
||||
|
||||
|
||||
class TruncMonth(TruncBase):
|
||||
kind = 'month'
|
||||
|
||||
|
||||
class TruncDay(TruncBase):
|
||||
kind = 'day'
|
||||
|
||||
|
||||
class TruncDate(TruncBase):
|
||||
kind = 'date'
|
||||
lookup_name = 'date'
|
||||
output_field = DateField()
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
# Cast to date rather than truncate to date.
|
||||
lhs, lhs_params = compiler.compile(self.lhs)
|
||||
tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None
|
||||
sql = connection.ops.datetime_cast_date_sql(lhs, tzname)
|
||||
return sql, lhs_params
|
||||
|
||||
|
||||
class TruncTime(TruncBase):
|
||||
kind = 'time'
|
||||
lookup_name = 'time'
|
||||
output_field = TimeField()
|
||||
|
||||
def as_sql(self, compiler, connection):
|
||||
# Cast to date rather than truncate to date.
|
||||
lhs, lhs_params = compiler.compile(self.lhs)
|
||||
tzname = timezone.get_current_timezone_name() if settings.USE_TZ else None
|
||||
sql = connection.ops.datetime_cast_time_sql(lhs, tzname)
|
||||
return sql, lhs_params
|
||||
|
||||
|
||||
class TruncHour(TruncBase):
|
||||
kind = 'hour'
|
||||
|
||||
|
||||
class TruncMinute(TruncBase):
|
||||
kind = 'minute'
|
||||
|
||||
|
||||
class TruncSecond(TruncBase):
|
||||
kind = 'second'
|
||||
|
||||
|
||||
DateTimeField.register_lookup(TruncDate)
|
||||
DateTimeField.register_lookup(TruncTime)
|
||||
@@ -0,0 +1,118 @@
|
||||
from django.db.models import FloatField, Func, IntegerField
|
||||
|
||||
__all__ = [
|
||||
'CumeDist', 'DenseRank', 'FirstValue', 'Lag', 'LastValue', 'Lead',
|
||||
'NthValue', 'Ntile', 'PercentRank', 'Rank', 'RowNumber',
|
||||
]
|
||||
|
||||
|
||||
class CumeDist(Func):
|
||||
function = 'CUME_DIST'
|
||||
name = 'CumeDist'
|
||||
output_field = FloatField()
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class DenseRank(Func):
|
||||
function = 'DENSE_RANK'
|
||||
name = 'DenseRank'
|
||||
output_field = IntegerField()
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class FirstValue(Func):
|
||||
arity = 1
|
||||
function = 'FIRST_VALUE'
|
||||
name = 'FirstValue'
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class LagLeadFunction(Func):
|
||||
window_compatible = True
|
||||
|
||||
def __init__(self, expression, offset=1, default=None, **extra):
|
||||
if expression is None:
|
||||
raise ValueError(
|
||||
'%s requires a non-null source expression.' %
|
||||
self.__class__.__name__
|
||||
)
|
||||
if offset is None or offset <= 0:
|
||||
raise ValueError(
|
||||
'%s requires a positive integer for the offset.' %
|
||||
self.__class__.__name__
|
||||
)
|
||||
args = (expression, offset)
|
||||
if default is not None:
|
||||
args += (default,)
|
||||
super().__init__(*args, **extra)
|
||||
|
||||
def _resolve_output_field(self):
|
||||
sources = self.get_source_expressions()
|
||||
return sources[0].output_field
|
||||
|
||||
|
||||
class Lag(LagLeadFunction):
|
||||
function = 'LAG'
|
||||
name = 'Lag'
|
||||
|
||||
|
||||
class LastValue(Func):
|
||||
arity = 1
|
||||
function = 'LAST_VALUE'
|
||||
name = 'LastValue'
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class Lead(LagLeadFunction):
|
||||
function = 'LEAD'
|
||||
name = 'Lead'
|
||||
|
||||
|
||||
class NthValue(Func):
|
||||
function = 'NTH_VALUE'
|
||||
name = 'NthValue'
|
||||
window_compatible = True
|
||||
|
||||
def __init__(self, expression, nth=1, **extra):
|
||||
if expression is None:
|
||||
raise ValueError('%s requires a non-null source expression.' % self.__class__.__name__)
|
||||
if nth is None or nth <= 0:
|
||||
raise ValueError('%s requires a positive integer as for nth.' % self.__class__.__name__)
|
||||
super().__init__(expression, nth, **extra)
|
||||
|
||||
def _resolve_output_field(self):
|
||||
sources = self.get_source_expressions()
|
||||
return sources[0].output_field
|
||||
|
||||
|
||||
class Ntile(Func):
|
||||
function = 'NTILE'
|
||||
name = 'Ntile'
|
||||
output_field = IntegerField()
|
||||
window_compatible = True
|
||||
|
||||
def __init__(self, num_buckets=1, **extra):
|
||||
if num_buckets <= 0:
|
||||
raise ValueError('num_buckets must be greater than 0.')
|
||||
super().__init__(num_buckets, **extra)
|
||||
|
||||
|
||||
class PercentRank(Func):
|
||||
function = 'PERCENT_RANK'
|
||||
name = 'PercentRank'
|
||||
output_field = FloatField()
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class Rank(Func):
|
||||
function = 'RANK'
|
||||
name = 'Rank'
|
||||
output_field = IntegerField()
|
||||
window_compatible = True
|
||||
|
||||
|
||||
class RowNumber(Func):
|
||||
function = 'ROW_NUMBER'
|
||||
name = 'RowNumber'
|
||||
output_field = IntegerField()
|
||||
window_compatible = True
|
||||
Reference in New Issue
Block a user