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:
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:
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:
- When using
render()
in the corresponding view - 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:
Code completion will work even if only one of the views uses
render()
(and also uses a string literal fortemplate_name
withinrender()
). This is confusing, because the other view(s) could useTemplateResponse()
, for example, and code completion of context variables would still appear to work correctly in the corresponding template.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 itscontext
but another view does not. PyCharm will offer to autocomplete theerror_message
variable in the template without warning that some views don't pass this variable in thecontext
.
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.