pátek 30. listopadu 2012

Django generic relation doesn't prefetch hvad translated objects

I'm using django-hvad translations for one of my projects. But I find out hvad problem with generic relations. For instance I have two models Quote and Author (translated model). Quote is related to Author by generic relation, because there are also another models conected to Quote.
Problem is if you try get list of quotes with prefetched authors.
Quotes.objects.prefetch_related('originator')

Hvad in this case returns non-translated object, you can't access translated attributs. For this reason prefetching is useless. So I created custom TranslationGenericForeignKey, which respects translation objects.
class TranslationGenericForeignKey(GenericForeignKey):
    """
    Custom GenericForeignKey which respects hvad translation objects
    """
    def get_prefetch_query_set(self, instances):
        # For efficiency, group the instances by content type and then do one
        # query per model
        fk_dict = defaultdict(set)
        # We need one instance for each group in order to get the right db:
        instance_dict = {}
        ct_attname = self.model._meta.get_field(self.ct_field).get_attname()
        for instance in instances:
            # We avoid looking for values if either ct_id or fkey value is None
            ct_id = getattr(instance, ct_attname)
            if ct_id is not None:
                fk_val = getattr(instance, self.fk_field)
                if fk_val is not None:
                    fk_dict[ct_id].add(fk_val)
                    instance_dict[ct_id] = instance

        ret_val = []
        for ct_id, fkeys in fk_dict.items():
            instance = instance_dict[ct_id]
            ct = self.get_content_type(id=ct_id, using=instance._state.db)
            qs = ct.model_class()._base_manager
            if hasattr(qs, 'language'):
                qs = qs.language()
            qs = qs.using(ct._state.db).filter(pk__in=fkeys)
            ret_val.extend(qs)

        # For doing the join in Python, we have to match both the FK val and the
        # content type, so we use a callable that returns a (fk, class) pair.
        def gfk_key(obj):
            ct_id = getattr(obj, ct_attname)
            if ct_id is None:
                return None
            else:
                model = self.get_content_type(id=ct_id,
                    using=obj._state.db).model_class()
                return (model._meta.pk.get_prep_value(getattr(obj, self.fk_field)),
                        model)

        return (ret_val,
                lambda obj: (obj._get_pk_val(), obj.__class__),
                gfk_key,
                True,
                self.cache_attr)

class Quote(models.Model):
    originator_type = models.ForeignKey(ContentType)
    originator_id = models.PositiveIntegerField()
    originator = TranslationGenericForeignKey('originator_type', 'originator_id')

    content = models.TextField()

class Author(TranslatableModel):
    last_name = models.CharField(max_length=256)
    translation = TranslatedFields(
        description = models.TextField(),
    )
    objects = AuthorManager()

class AuthorManager(TranslationOriginatorManager):
    """
    use_for_related_fields is very important, otherwise django use pure models.Manager
    For more info check https://docs.djangoproject...managers/#set-use-for-related-fields
    """
    use_for_related_fields = True

Žádné komentáře:

Okomentovat