#6051: GenericRelation field lookups
-----------------------+----------------------------------------------------
Reporter:  litnimax    |       Owner:  nobody       
  Status:  new         |   Component:  Uncategorized
 Version:  SVN         |    Keywords:  generic      
   Stage:  Unreviewed  |   Has_patch:  1            
-----------------------+----------------------------------------------------
 == Problem descriptions ==
 Currently
 [http://www.djangoproject.com/documentation/models/generic_relations/
 GenericRelations] documentation does not say anything that it's possible.
 I mean expression like
 {{{
 Animal.objects.filter(tags__tag__exact='heavy')
 }}}
  is not described there. But if we look in generic.py we can find there
 pretty much code that makes it possible. But apparently it's not ready
 yet. So I am creating here this ticket for discussion as I was told django
 developers list if not the right place to do it (my post is
 [http://groups.google.ru/group/django-
 developers/browse_thread/thread/60d72cc0d5d9d9d5/ here]).

 So, at the moment I found the following issues.
  * Wrong field name when using intermediate tables,
 {{{
 for example, name__exact='bla-bla' worked,
 but user__account__is_enabled=True did not work.
 }}}
  Fixed in patch attached - [attachment:file:wrong_field_name.patch]
  * Not using content types table that makes possible for wrong results.
 Live example is below.

 {{{
 #!python
 class Subscription(models.Model):
     """
     Universal model for all subscriptions.
     """
     content_type = models.ForeignKey(ContentType)
     object_id = models.PositiveIntegerField(db_index=True)
     account = models.ForeignKey(VoipAccount, verbose_name=_("account"))
     currency = models.ForeignKey(Currency, verbose_name=_("currency"))
     period = models.IntegerField(_("period"), choices=PERIOD_CHOICES)
     setup = models.DecimalField(
         _("setup fee"), max_digits=10, decimal_places=2)
     rate = models.DecimalField(
         _("period rate"),  max_digits=10, decimal_places=2)
     create_date = models.DateTimeField(_("created on"), db_index=True)
     paidtill_date = models.DateField(_("paid till"), db_index=True)
     is_enabled = models.BooleanField(_("is enabled"), db_index=True)
     content_object = generic.GenericForeignKey()
 }}}

 Now model that uses Subscription:
 {{{
 #!python
 class LocalNumber(models.Model):
     number = models.CharField(_('number'), max_length=10, unique=True)
     subscription = generic.GenericRelation(Subscription)
 }}}

 These models make it possible for users to "subscribe" to numbers. It
 generates the following code:
 {{{
 #!python
 In [48]: LocalNumber.objects.filter(subscription__is_enabled=True)
 Out[48]: [<LocalNumber: 1000>, <LocalNumber: 1020>]

 Out[49]:
 (['`provider_localnumber`.`id`',
   '`provider_localnumber`.`pool_id`',
   '`provider_localnumber`.`sub_pool_id`',
   '`provider_localnumber`.`number`'],
  ' FROM `provider_localnumber` LEFT OUTER JOIN `billing_subscription` AS
 `m2m_provider_localnumber__subscription` ON `provider_localnumber`.`id` =
 `m2m_provider_localnumber__subscription`.`object_id` INNER JOIN
 `billing_subscription` AS `provider_localnumber__subscription` ON
 `m2m_provider_localnumber__subscription`.`id` =
 `provider_localnumber__subscription`.`id` WHERE
 (`provider_localnumber__subscription`.`is_enabled` = %s)',
  [True])

 }}}

 I do not see any relations with content types. Imagine that some other
 model has same object_id? Our initial situation is correct:
 {{{
 mysql> select * from billing_subscription;
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 | id | content_type_id | object_id | account_id | currency_id | period |
 setup | rate | create_date         | paidtill_date | is_enabled |
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 | 54 |             174 |     46317 |      20865 |           1 |      1 |
 0.00 | 0.00 | 2007-11-28 23:52:53 | 2008-11-27    |          1 |
 | 53 |             174 |     46337 |      20865 |           1 |      1 |
 0.00 | 0.00 | 2007-11-28 21:09:39 | 2008-11-27    |          1 |
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 }}}
 Now let "subscribe" some other model:
 {{{
 mysql> insert into billing_subscription (content_type_id, object_id,
 account_id, is_enabled) values (12, 46337, 20865, 1);

 mysql> select * from billing_subscription;
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 | id | content_type_id | object_id | account_id | currency_id | period |
 setup | rate | create_date         | paidtill_date | is_enabled |
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 | 54 |             174 |     46317 |      20865 |           1 |      1 |
 0.00 | 0.00 | 2007-11-28 23:52:53 | 2008-11-27    |          1 |
 | 53 |             174 |     46337 |      20865 |           1 |      1 |
 0.00 | 0.00 | 2007-11-28 21:09:39 | 2008-11-27    |          1 |
 | 55 |              12 |     46337 |      20865 |           0 |      0 |
 0.00 | 0.00 | 0000-00-00 00:00:00 | 0000-00-00    |          1 |
 
+----+-----------------+-----------+------------+-------------+--------+-------+------+---------------------+---------------+------------+
 }}}
 As you can see subscription.id=55 has content_type_id=12 and
 content_object is not !LocalNumber. Let proove it:
 {{{
 #!python
 In [61]: print Subscription.objects.get(pk=53).content_object
 1020
 In [62]: print Subscription.objects.get(pk=54).content_object
 1000
 In [63]: print Subscription.objects.get(pk=55).content_object
 None
 }}}
 As you can see there are only 2 subscriptions to numbers (We got None for
 3-rd line because there is no such object_id for model id 12). But let see
 again:
 {{{
 #!python
 In [64]: LocalNumber.objects.filter(subscription__is_enabled=True)
 Out[64]: [<LocalNumber: 1000>, <LocalNumber: 1020>, <LocalNumber: 1020>]
 }}}
 '''What happened!?''' Why do we have 3 numbers instead of 2? This is
 because current implementation does not use content types table.

 To be continued...

-- 
Ticket URL: <http://code.djangoproject.com/ticket/6051>
Django Code <http://code.djangoproject.com/>
The web framework for perfectionists with deadlines
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to