PyCharm autocomplete not working for context variables in Django templates

Normally, PyCharm autocomplete (code completion) works for context variables in Django templates. For example, if the context is {"question": question}, where question is an instance of the Question model, PyCharm will offer autocomplete suggestions when I start typing {{ question. }}, like this:

screenshot of autocomplete working for a context variable in a Django template

However, in my current Django project, PyCharm autocomplete fails to work for context variables in Django templates. Even pressing Ctrl + Space will not bring up code completion suggestions:

autocomplete failure in Django

I think that I have everything configured in PyCharm for Django as usual for this project, although I could be mistaken. It's a larger project, so I suspect that something about the project structure itself might be causing the problem.

Other convenience features are still working in Django templates, e.g., Emmet abbreviations, auto-closing of {{ and {%, etc.

At first, I suspected the problem was that I have the models, views, etc., split out into multiple files. For example, instead of all models being contained in a single models.py file, the models are organized like this:

models/
  __init__.py
  model_a.py
  model_b.py
  model_c.py

However, I don't think this is causing the autocomplete failure, because when I created a small Django project and organized it this way, autocomplete still worked for context variables in the Django templates.

This is for PyCharm Professional 2023.3.3, although I just updated to 2024.2.3 and am still experiencing the same issue so far.

Can anyone suggest some reasons why PyCharm autocomplete would fail for context variables in Django templates?

After some trial and error, I have code completion working in my project.

Here is what I've figured out:

PyCharm code completion for context variables in Django templates works under the following conditions:

  1. When using render() in the corresponding view
  2. When passing the template name as a string literal to the render() function

As far as I can tell, it will not work when using either TemplateResponse() or HttpResponse() (along with the django.template.loader module) instead of render(). It will also not work when the template path is passed to render() as a variable instead of as a string literal.

For example, code completion works when the view is written like this:

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, "polls/detail.html", {"question": question})

It's also fine to pass the context as a variable, so this works, too:

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    context = {"question": question}
    return render(request, "polls/detail.html", context)

However, code completion fails when the view is written in any of the following ways:

e.g., when passing the template_name as a variable to render() instead of as a string literal:

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    template_name = "polls/detail.html"
    return render(request, template_name, {"question": question})

e.g., when using TemplateResponse() instead of render():

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return TemplateResponse(request, "polls/detail.html", {"question": question})

e.g., when using HttpResponse() like this:

def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    template = loader.get_template('polls/detail.html')
    return HttpResponse(template.render({"question": question}, request))

But why?

I assume that the code completion failure with TemplateResponse() might have something to do with how it implements delayed rendering. Maybe the failure when using HttpResponse() (along with the django.template.loader module) happens for similar reasons.

I'm not sure why code completion fails unless the template_name is provided directly to render as a string literal. It seems that static code analysis should be able to figure it out if it's passed as a variable that's defined using a string literal in the same view.

Caveats

There are a couple of things to watch out for if you have at least two views using the same template:

  1. Code completion will work even if only one of the views uses render() (and also uses a string literal for template_name within render()). This is confusing, because the other view(s) could use TemplateResponse(), for example, and code completion of context variables would still appear to work correctly in the corresponding template.

  2. If the views using the same template have different context variables, PyCharm will offer to code complete all of them in the template. This could cause problems, for example, if one of the views passes an error_message variable within its context but another view does not. PyCharm will offer to autocomplete the error_message variable in the template without warning that some views don't pass this variable in the context.

Epilogue

I didn't change anything about my project structure other than changing the views to use render() (from django.shortcuts), and making sure that string literals are used for the template_name within render(). I don't recall this being a requirement before for code completion in templates, but maybe it always was and I just didn't notice.

I'm still not convinced that PyCharm shouldn't be able to do code completion of context variables within templates unless this magic formula is followed. I'm not sure if this is a bug, or maybe just something I don't understand about PyCharm.

Back to Top