Django Parler "The 'language_code' attribute cannot be changed directly

854 views Asked by At

I am currently extending a plugin to play well with Django translations and languages. Here is the method in question. Prior to getting translations involved, it worked as needed.

I extended the query to pull data from a couple of translation tables. However, I am getting an error, which I am unsure how to deal with.

    def get_authors_and_article_counts(self, authors):
        """method returning authors and their article counts"""

        # first, we collect ids of authors for which we need to get data
        author_ids = [author.id for author in self.authors.all()]
        author_ids_tuple_str = '(' +  str(author_ids).strip('[]') + ')'

        #limit subquery to published articles
        published_clause = """ AND
            is_published %s AND
            publishing_date <= %s
            """ % (SQL_IS_TRUE, SQL_NOW_FUNC, )

        query = """
                with article_count as (
                  select author_id, count(*) as article_count
                    from aldryn_newsblog_article
                   where app_config_id = 1
                    %s
                   group by author_id
                )

                select distinct prof.*, coalesce(ac.article_count, 0) as article_count, author_trans.*, aldryn_people_trans.slug
                from common_authorprofile prof

                left join article_count ac
                on ac.author_id = prof.profile_id

                left join common_authorprofile_translation author_trans
                on prof.id = author_trans.master_id

                left join aldryn_people_person_translation aldryn_people_trans
                on prof.profile_id = aldryn_people_trans.master_id
                WHERE
                        prof.id IN %s AND
                        author_trans.language_code = 'ru';

""" % (published_clause, author_ids_tuple_str)

        print(query)
        #print(author_ids)

        raw_authors = list(AuthorProfile.objects.raw(query))
        #print(raw_authors)
        authors = [author for author in raw_authors if author.article_count]

        print(authors)
        return sorted(authors, key=lambda x: x.article_count, reverse=True)

The traceback:

Traceback (most recent call last):
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
core/handlers/exception.py", line 34, in inner                                          
    response = get_response(request)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
core/handlers/base.py", line 156, in _get_response                                      
    response = self.process_exception_by_middleware(e, request)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
core/handlers/base.py", line 154, in _get_response                                      
    response = response.render()
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/response.py", line 106, in render                                              
    self.content = self.rendered_content
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/response.py", line 83, in rendered_content                                     
    content = template.render(context, self._request)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/backends/django.py", line 61, in render                                        
    return self.template.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 171, in render                                                  
    return self._render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 163, in _render                                                 
    return self.nodelist.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 937, in render                                                  
    bit = node.render_annotated(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 904, in render_annotated                                        
    return self.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/classyt
ags/core.py", line 153, in render                                                       
    return self.render_tag(context, **kwargs)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/sekizai
/templatetags/sekizai_tags.py", line 93, in render_tag                                  
    rendered_contents = nodelist.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 937, in render                                                  
    bit = node.render_annotated(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 904, in render_annotated                                        
    return self.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/classyt
ags/core.py", line 153, in render                                                       
    return self.render_tag(context, **kwargs)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/tem
platetags/cms_tags.py", line 447, in render_tag                                         
    return toolbar.render_with_structure(context, nodelist)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/too
lbar/toolbar.py", line 477, in render_with_structure                                    
    rendered_contents = nodelist.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 937, in render                                                  
    bit = node.render_annotated(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
template/base.py", line 904, in render_annotated                                        
    return self.render(context)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/classyt
ags/core.py", line 153, in render                                                       
    return self.render_tag(context, **kwargs)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/tem
platetags/cms_tags.py", line 313, in render_tag                                         
    nodelist=nodelist,
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/plu
gin_rendering.py", line 343, in render_page_placeholder                                 
    nodelist=None,
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/plu
gin_rendering.py", line 259, in render_placeholder                                      
    placeholder_content = ''.join(plugin_content)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/plu
gin_rendering.py", line 456, in render_plugins                                          
    yield self.render_plugin(plugin, context, placeholder, editable)
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/cms/plu
gin_rendering.py", line 429, in render_plugin                                           
    context = plugin.render(context, instance, placeholder.slot)
  File "/home/user/sites/app-web/app/apps/plugins/cms_plugins.py", line 1
8, in render                                                                            
    authors_list = instance.get_authors_and_article_counts(request)
  File "/home/user/sites/app-web/app/apps/plugins/models.py", line 88, in
 get_authors_and_article_counts                                                         
    raw_authors = list(AuthorProfile.objects.raw(query))
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
db/models/query.py", line 1339, in __iter__                                             
    self._fetch_all()
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
db/models/query.py", line 1326, in _fetch_all                                           
    self._result_cache = list(self.iterator())
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/
db/models/query.py", line 1368, in iterator                                             
    setattr(instance, column, values[pos])
  File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/parler/
fields.py", line 161, in __set__                                                        
    raise AttributeError("The 'language_code' attribute cannot be changed directly! Use t
he set_current_language() method instead.")                                             
AttributeError: The 'language_code' attribute cannot be changed directly! Use the set_cur
rent_language() method instead.       

What am I doing wrong? Is the problem here raw_authors = list(AuthorProfile.objects.raw(query))? Should I be doing something like MyModel.objects.language('en'). ..., run the query via Parler after changing the query to discard the parts dealing with translations? What is the proper way?

1

There are 1 answers

3
Furkan Siddiqui On

Before we moving on to solution let's discuss why this error happened.

We can perform django raw query in 2 ways either using Model.objects.raw() or execute custom raw query directly. When we say:

  1. Model.objects.raw() ⇾ it basically perform raw queries and return model instances.
  2. directly custom raw query ⇾ In this case, you can always access the database directly, routing around the model layer entirely.

For your case list(AuthorProfile.objects.raw(query)) during this it fetch result successfully but the error happens when it tries to make model instance....

From your traceback: File "/home/user/miniconda3/envs/app-web/lib/python3.7/site-packages/django/ db/models/query.py", line 1368, in iterator
setattr(instance, column, values[pos])

...because this instance doesn't allow to set the attribute. see below codebase of django-parler

class LanguageCodeDescriptor(object):
    ..... some code ....
    def __set__(self, instance, value):
        raise AttributeError("The 'language_code' attribute cannot be changed directly! Use the set_current_language() method instead.")
    ..... some code ....

Coming back to your answer. There are couple of ways you can follow.

  1. Just remove the exception from LanguageCodeDescriptor method __set__. changes in django-parler like this of course this is not recommended.

  2. I would like to consider your suggestion MyModel.objects.language('en'). your query seems little bit more complex try to use ORM if possible, if not possible just follow points 3.

  3. Execute raw query using method 2 from discussed above i.e directly custom raw query,

    from django.db import connection
    with connection.cursor() as cursor:
       cursor.execute(query, [published_clause, author_ids_tuple_str])
       rows = cursor.fetchall()    
    

NOTE: if you use method 3 then you can not access model_instance like this author.article_count since this method doesn't return model instance.

The following links can be followed for more details:

performing raw sql

django-parler codebase