When referencing imported Django model, I get 'local variable referenced before assignment' error
I am trying to import a model into my Django view and then query all objects, sort them, and iterate over them. I am not getting any error when importing the model, however, when trying to query the model with songs = song.objects.all()#.order_by('-release_date')
, I am getting an error:
UnboundLocalError at /hotline/dbm
local variable 'song' referenced before assignment
/home/path/to/site/views.py, line 82, in dbm
songs = song.objects.all()#.order_by('-release_date')
I do not understand what the problem is, as the variable song
is clearly imported from my models.py
file, and I am not getting any errors importing it - so why is Python not recognizing song
as what I imported from my models.py
file?
My models.py
file:
class song(models.Model):
name = models.TextField()
file = models.FileField()
release_date = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name = 'Song'
verbose_name_plural = f'{verbose_name}s'
my views.py
file:
#list of modules removed to keep code clean
from .models import *
@csrf_exempt
def dbm(request: HttpRequest) -> HttpResponse:
songs = song.objects.all()#.order_by('-release_date')
response = request.POST.get('Digits')
if response == None:
vr = VoiceResponse()
vr.say("Please choose a song, and then press pound")
vr.pause(length=1)
with vr.gather(finish_on_key='#', timeout=6, numDigits="1") as gather:
for song, num in songs:
gather.pause(length=1)
gather.say(f"For {song.name}, please press {num}")
vr.redirect(reverse('dbm'))
return HttpResponse(str(vr), content_type='text/xml')
elif response != None:
vr = VoiceResponse()
vr.say("hi")
return HttpResponse(str(vr), content_type='text/xml')
Thanks!
I do not understand what the problem is, as the variable song is clearly imported from my models.py file
Yes, but in your function you also work with a local variable named song
, indeed:
for song, num in songs:
# …
That means Python says that song
is a local variable, and it thus refuses to look for the one outside the function.
But I think the main problem is that classes are typically written in PascalCase
, not , so you probably better rename the model snake_case
Song
, not . Since local variables are given song
snake_case
names, this avoids clashes, so:
# 🖟 not song
class Song(models.Model):
name = models.TextField()
file = models.FileField()
release_date = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name = 'Song'
verbose_name_plural = f'{verbose_name}s'
then the view looks like:
from .models import Song
@csrf_exempt
def dbm(request: HttpRequest) -> HttpResponse:
songs = Song.objects..order_by('-release_date')
response = request.POST.get('Digits')
if response is None:
vr = VoiceResponse()
vr.say('Please choose a song, and then press pound')
vr.pause(length=1)
with vr.gather(finish_on_key='#', timeout=6, numDigits="1") as gather:
for song, num in songs:
gather.pause(length=1)
gather.say(f"For {song.name}, please press {num}")
vr.redirect(reverse('dbm'))
return HttpResponse(str(vr), content_type='text/xml')
else:
vr = VoiceResponse()
vr.say('hi')
return HttpResponse(str(vr), content_type='text/xml')
Note: Please do not use wildcard imports [quantifiedcode.com]. It makes the statement less predictable, it can easily result in failing code if you later decide to change what is exported in a certain module, and furthermore it can override variables.
in your code, the lowercase song conflicts with the class name.
when you use:
from .models import *
django imports the song class and assigns it to a local name song.
for song, num in songs:
However, the interpreter assumes song in your function refers to a local variable (not the model class) because the for loop temporarily creates a local variable
and in Django, model classes should follow the PascalCase naming convention.
models.py:
class Song(models.Model):
name = models.TextField()
file = models.FileField()
release_date = models.DateTimeField(default=timezone.now)
views.py
from .models import Song
@csrf_exempt
def dbm(request: HttpRequest) -> HttpResponse:
songs = Song.objects.all()#.order_by('-release_date')