converting date @ hybrid property

583 views
Skip to first unread message

Mehdi

unread,
Aug 22, 2015, 4:30:19 PM8/22/15
to sqlalchemy
Hi
I'm using latest sqlalchemy with an Oracle db backend.
I have a Date column which presents a Gregorian date in db. but i want to query my table by a Jalali date.
So the hybrid_property to convert gregorian date into jalali date should be like this, i think:
@hybrid_property
   
def jalali_date(self):
       
return jdatetime.date.fromgregorian(year=self.input_date.year,
                                            month
=self.input_date,
                                           
day=self.input_date.day)


i've tried different ways, but all of them ends with errors like:
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with MeasureData.input_date has an attribute 'year'
or
TypeError: an integer is required (got type InstrumentedAttribute)

So i guess i have to get access to instance value of MeasureDate.input_date at hybrid_property definition, but How?
Do i have to completely change my approach?

Thanks.

Mike Bayer

unread,
Aug 23, 2015, 10:54:26 PM8/23/15
to sqlal...@googlegroups.com
the hybrid allows a Python representation at the object level and a SQL representation at the class level.  Your error message involves the term "InstrumentedAttribute", which suggests you are attempting to use this hybrid at the class level (please provide full stack traces to make this clearer).     Your hybrid as defined only illustrates instance-level conversions and there's not a simple way to do this conversion at the SQL level especially in Oracle, unless you had some stored procedure which does so.

Therefore, you are probably looking for an in-Python value of a Jalali date to be converted from the alternate calendar to the gregorian (SQL-persisted) calendar *in python*, before it is sent to the database, and converted back to Jalali *in Python* after being received from the database as a result.  For this, you want to build a custom type similar to the "MyEpochType" illustrated at http://docs.sqlalchemy.org/en/rel_1_0/core/custom_types.html#augmenting-existing-types - using this type will cause expressions like equality comparisons to coerce the "value side" of the expression, e.g. the raw datetime object, as according to your rules.  You can then cause this particular type to be used with your hybrid using type_coerce.    Here's an example using most features of hybrids:

from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
import jdatetime
import datetime

Base = declarative_base()


class JDate(TypeDecorator):
    impl = Date

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = jdatetime.date.togregorian(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = jdatetime.date.fromgregorian(
                year=value.year,
                month=value.month,
                day=value.day)
        return value


class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)

    input_date = Column(Date)

    @hybrid_property
    def jalali_date(self):
        return jdatetime.date.fromgregorian(
            year=self.input_date.year,
            month=self.input_date.month,
            day=self.input_date.day)

    @jalali_date.expression
    def jalali_date(cls):
        return type_coerce(cls.input_date, JDate)

    @jalali_date.setter
    def jalali_date(self, value):
        self.input_date = jdatetime.date.togregorian(value)


e = create_engine("postgresql://scott:tiger@localhost/test", echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)


a1 = A(jalali_date=jdatetime.date(1380, 8, 2))
a2 = A(input_date=datetime.date(2001, 10, 24))

s = Session(e)
s.add_all([a1, a2])
s.commit()
s.close()

a1, a2 = s.query(A).filter_by(jalali_date=jdatetime.date(1380, 8, 2)).all()

print a1.jalali_date, a2.jalali_date
print a1.input_date, a2.input_date



in the output, we can see that jalali_date and input_date are consistent from both ways of setting, both ways of matching:

1380-08-02 1380-08-02
2001-10-24 2001-10-24








Thanks.
--
You received this message because you are subscribed to the Google Groups "sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to sqlalchemy+...@googlegroups.com.
To post to this group, send email to sqlal...@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Mehdi

unread,
Aug 24, 2015, 2:27:41 AM8/24/15
to sqlalchemy
After reading more documents i ended up using comparator for my hybrid property instead of expression. like:
class JalaliDateComparator(Comparator):
   
   
def operate(self, op, other):
        input_date
= self.__clause_element__()
        g_date
= other.togregorian()

       
return op(input_date, g_date)

So far it seems working, but i'll consider your solution too. i'm not sure which way is best practice but i'm happy that i have two solutions :)
Thanks for your reply.
Reply all
Reply to author
Forward
0 new messages