Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

311
Views
DjangoORM: resolver la expresión F en la función de base de datos personalizada

Estoy tratando de escribir una función PostgreSQL personalizada en Django que forzará las fechas y horas a una zona horaria específica dentro de un conjunto de consultas. Mi primer paso en la función db se ve así:

 from django.db.models.expressions import Func class DateTimeInTimezone(Func): template="%(expressions)s AT TIME ZONE %(tz_info)s"

Esta función funciona en el caso simple en el que paso una cadena de zona horaria a la función directamente así:

 MyModel.objects.filter(timefield__gte=DateTimeInTimezone(Now(), tz_info='EST'))

Sin embargo, no funciona en el caso más complejo, donde la zona horaria se define en algún campo del modelo. Considere el siguiente ejemplo artificial:

 class User(models.Model): time_zone = models.CharField() class Meeting(models.Model): users = models.ManyToManyField(User, related_name='meetings') start_time = models.DateTimeField() # in UTC end_time = models.DateTimeField() # in UTC

Para responder a la pregunta "¿Qué usuarios estarán en una reunión hoy a las 12:00 p. m., hora local?", necesitaría alguna variación de este conjunto de consultas:

 noon_utc = ... User.objects.filter( meetings__start_time__lte=DateTimeInTimezone(noon_utc, tz_info=F('time_zone')), meetings__end_time__gt=DateTimeInTimezone(noon_utc, tz_info=F('time_zone')) )

Sin embargo, como está escrito actualmente, DateTimeInTimezone simplemente inyectará la cadena F('time_zone') en el sql en lugar de resolver el campo.

¿Es posible agregar soporte para expresiones F a esta función? ¿Hay algún otro enfoque que deba considerar?

about 4 years ago · Santiago Trujillo
2 answers
Answer question

0

Es posible una solución simple con el parámetro arg_joiner :

 class DateTimeInTimezone(Func): function = '' arg_joiner = ' AT TIME ZONE ' def __init__(self, timestamp, tz_info): super(DateTimeInTimezone, self).__init__(timestamp, tz_info)

El método __init__ se usa solo con fines de legibilidad con nombres claros de parámetros. Entonces arity no es importante si los parámetros son declarados por __init__ .

Una función oneliner es útil para un desarrollo rápido si la legibilidad no es importante:

 ...filter( meetings__start_time__lte=Func(noon_utc, tz_info=F('time_zone'), arg_joiner=' AT TIME ZONE ', function=''), )

Verificado:

 >>> qs = User.objects.filter(...) >>> print(str(qs.query)) SELECT ... WHERE ("app_meeting"."start_time" <= ((2017-10-03 08:18:12.663640 AT TIME ZONE "app_user"."time_zone")) AND ...)
about 4 years ago · Santiago Trujillo Report

0

Encontré una solución aceptable. as_sql el método as_sql para que funcionara así, permitiendo que las partes internas de django resolvieran la expresión F y luego separándola nuevamente en un kwarg que podría usar en una parte diferente de la plantilla.

 class DateTimeInTimezone(Func): ''' Coerce a datetime into a specified timezone ''' template="%(expressions)s AT TIME ZONE %(tz_info)s" arity = 2 def as_sql(self, compiler, connection, function=None, template=None, arg_joiner=None, **extra_context): connection.ops.check_expression_support(self) sql_parts = [] params = [] for arg in self.source_expressions: arg_sql, arg_params = compiler.compile(arg) sql_parts.append(arg_sql) params.extend(arg_params) data = self.extra.copy() data.update(**extra_context) # Use the first supplied value in this order: the parameter to this # method, a value supplied in __init__()'s **extra (the value in # `data`), or the value defined on the class. if function is not None: data['function'] = function else: data.setdefault('function', self.function) template = template or data.get('template', self.template) arg_joiner = arg_joiner or data.get('arg_joiner', self.arg_joiner) data['expressions'] = data['field'] = arg_joiner.join(sql_parts) parts = data['expressions'].split(', ') data['expressions'] = parts[0] data['tz_info'] = parts[1] return template % data, params

Agregué las tres líneas entre la asignación de data['expressions'] y la return template % data, params . Esta no es una gran solución a largo plazo, ya que las partes internas de Django para este método podrían cambiar en la próxima versión, pero se adapta a mis necesidades por el momento.

about 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!