Using .filter() on a constant expression

2011-10-15 Thread Tim Chase

I have some gnarly AND/OR logic combining Q() objects but deep
within the logic, I need to do a comparison of two constants.  In 
an ideal world, the logic would look something like


   def age(self, age):
 ... & Q(12=as_of.month) & ...

but that's invalid python.

I *can* break out this rats' nest of logic into individual
queries, evaluate the constant expression, conditionally OR the
logic into the tree, then reassemble all the parts.  However,
that gets even uglier.  Actual code is included below
("dob"="date of birth", "dod"="date of death"; the "as_of" is a
datetime.date instance; part of the complication is that the
month/day DOB/DOD can be absent and the year can be an
approximate range, e.g. born between 1943 and 1947; and the age
query can be for a single age or a range of ages, e.g. 47-52
years old) and I'm looking to make the magic happen at the two
places marked "MAGIC HAPPENS HERE".

I tried hacking it with

   Q(dob_month=F(str(as_of.month))-11)

(since dob_month is known to be 1 at this point) but that gives me

   FieldError: Cannot resolve keyword '6' into field. Choices
are: 

when I run my tests.  Trying without wrapping the number in str() 
complains that my integer/month has no attribute .split() (which 
obviously it doesn't).


Is there any way to include a constant comparison in a Q()?

Thanks,

-tim

##

   alive_q = use_as_of & (
 # dob1 >= y1
 Q(dob_year_min__gt=alive_y1) | (
 Q(dob_year_min=alive_y1) & (
   Q(dob_month=None) |
   Q(dob_month__gt=as_of.month) | (
   Q(dob_month=as_of.month) & (
 Q(dob_day=None) |
 Q(dob_day__gte=as_of.day)
 ))
   )) | (
   Q(dob_month=1) &
   Q("""MAGIC HAPPENS HERE checking if as_of.month=12""") & (
 Q(dob_day=None) |
 (Q(dob_day=1) & Q("""MAGIC HAPPENS HERE checking if
as_of.day=31"""))
 ))
 ) & (
 # dob2 < y2
 Q(dob_year_max__lt=alive_y2) | (
 Q(dob_year_max=alive_y2) & (
   Q(dob_month=None) |
   Q(dob_month__lt=as_of.month) | (
   Q(dob_month=as_of.month) & (
 Q(dob_day=None) |
 Q(dob_day__lt=as_of.day)
 ))
   )) | (
 Q(dob_year_max=alive_y2+1) & (
   Q(dob_month=1) & (
 Q(dob_day=None) |
 Q(dob_day__lt=1)
   ))
  ))
   dead_q = use_dod & (
 # dob1 >= y1
 Q(dob_year_min__gt=dead_y1) | (
 Q(dob_year_min=dead_y1) & (
   Q(dob_month=None) |
   Q(dod_month=None) |
   Q(dob_month__gt=F("dod_month")) | (
   Q(dob_month=F("dod_month")) & (
 Q(dob_day=None) |
 Q(dod_day=None) |
 Q(dob_day__gte=F("dod_day"))
 )) | (
   Q(dob_month=1, dod_month=12) & (
 Q(dob_day=None) |
 Q(dod_day=None) |
 Q(dob_day=1, dod_day=31)
 ))
   ))
 ) & (
 # dob2 < y2
 Q(dob_year_max__lt=dead_y2) | (
 Q(dob_year_max=dead_y2) & (
   Q(dob_month=None) |
   Q(dod_month=None) |
   Q(dob_month__lt=F("dod_month")) | (
   Q(dob_month=F("dod_month")) & (
 Q(dob_day=None) |
 Q(dod_day=None) |
 Q(dob_day__lt=F("dod_day"))
 ))
   ))
 )
   master_q = (alive_q | dead_q)
   return self.get_query_set().filter(master_q)

--
You received this message because you are subscribed to the Google Groups "Django 
users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.



Re: Using .filter() on a constant expression

2011-10-21 Thread Ian Clelland
On Sat, Oct 15, 2011 at 9:52 PM, Tim Chase
wrote:

> I have some gnarly AND/OR logic combining Q() objects but deep
> within the logic, I need to do a comparison of two constants.  In an ideal
> world, the logic would look something like
>
>   def age(self, age):
> ... & Q(12=as_of.month) & ...
>
> but that's invalid python.
>
> I *can* break out this rats' nest of logic into individual
> queries, evaluate the constant expression, conditionally OR the
> logic into the tree, then reassemble all the parts.  However,
> that gets even uglier.

...

> Is there any way to include a constant comparison in a Q()?


Q objects are definitely not meant to handle that -- they have to operate on
some field in the database.

Besides, if you need to compare two constants, and you have both of them
available in python before you construct your query, then why would you want
to compare them in SQL? If the point is to include or exclude some other
conditions based on the calling parameters, then I would just use those
parameters to construct the right query to begin with.

I would think that breaking it into smaller, manageable pieces would make
the code easier to understand, rather than uglier, but that's a matter of
aesthetics, I suppose.

If you don't want to do that, then just use python to selectively include or
exclude the other Q objects (that you wanted to depend on your comparison):

Using your code as a base, I would do something like this: (I don't know if
this is correct; I get a headache trying to count all of the parentheses :)
)

 alive_q = use_as_of & (
# dob1 >= y1
Q(dob_year_min__gt=alive_y1) | (
Q(dob_year_min=alive_y1) & (
  Q(dob_month=None) |
  Q(dob_month__gt=as_of.month) | (
  Q(dob_month=as_of.month) & (
Q(dob_day=None) |
Q(dob_day__gte=as_of.day)
))
  )) | (

*# MAGIC HAPPENS HERE*
* ( Q(dob_month=1, dob_day=None) if as_of.month==12 else Q() )  |*
* ( Q(dob_day=1) if as_of_day==31 else Q())*
*# END MAGIC*

))
) & (
# dob2 < y2
Q(dob_year_max__lt=alive_y2) | (
Q(dob_year_max=alive_y2) & (
  Q(dob_month=None) |
  Q(dob_month__lt=as_of.month) | (
  Q(dob_month=as_of.month) & (
Q(dob_day=None) |
Q(dob_day__lt=as_of.day)
))
  )) | (
Q(dob_year_max=alive_y2+1) & (
  Q(dob_month=1) & (
Q(dob_day=None) |
Q(dob_day__lt=1)
  ))
 ))

-- 
Regards,
Ian Clelland


-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.



Re: Using .filter() on a constant expression

2011-10-21 Thread Tim Chase

On 10/21/11 13:58, Ian Clelland wrote:

If you don't want to do that, then just use python to selectively include or
exclude the other Q objects (that you wanted to depend on your comparison):

Using your code as a base, I would do something like this: (I don't know if
this is correct; I get a headache trying to count all of the parentheses :)
)

  alive_q = use_as_of&  (
 # dob1>= y1
 Q(dob_year_min__gt=alive_y1) | (
 Q(dob_year_min=alive_y1)&  (
   Q(dob_month=None) |
   Q(dob_month__gt=as_of.month) | (
   Q(dob_month=as_of.month)&  (
 Q(dob_day=None) |
 Q(dob_day__gte=as_of.day)
 ))
   )) | (

*# MAGIC HAPPENS HERE*
* ( Q(dob_month=1, dob_day=None) if as_of.month==12 else Q() )  |*
* ( Q(dob_day=1) if as_of_day==31 else Q())*
*# END MAGIC*


I like your solution!  I didn't know "Q()" would behave as a 
null-op like you use here--are there docs somewhere on that?  I 
ended up solving the problem by breaking the bits into named 
pieces (with pretty impenetrable names) and then conditionally 
"|"ing in the pieces at the right point before reassembling the 
whole mess.  Thanks for your ideas.


-tkc


--
You received this message because you are subscribed to the Google Groups "Django 
users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.