pythondjangodjango-modelsdjango-views

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!


Solution

  • 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 snake_case, so you probably better rename the model Song, not song. Since local variables are given 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.