MeL
MeL

Reputation: 1269

Django - NameError when trying to call model method in views

I have a method in my model that I would like to call in my views. However, I get a NameError that this function is not defined (I have imported all models in my views and all migrations are up to date). It has worked for other functions in the past.

Here is my model:

class Song(models.Model):
    """
    fields
    """

    def lyrics_as_list(self):
       return self.lyrics_ru.split()

    def sorted_strings(self, strings, locale=None):
       if locale is None:
           return sorted(strings)
       collator = icu.Collator.createInstance(icu.Locale(locale))
       return sorted(strings, key=collator.getSortKey)

And in my views the relevant part where I want to use it:

lyrics_list = models.Song.objects.get(pk=self.kwargs['pk']).lyrics_as_list() #this is also a method on my model, and it does work
lyrics_sorted = sorted_strings(set(lyrics_list, "ru_RU.UTF8")) #but this one gives me an error

The error: name 'sorted_strings' is not defined

When I move the method to my views, it works. But I have to use this in several views, so I am trying to follow the DRY principle, and so it would be great to get it to work.

Edit - What the sorted_strings function is for: My Song model contains lyrics. I am displaying all words in the lyrics in a table, but I want to show it alphabetically. In order to be able to sort Russian words, I needed to add this function.

Upvotes: 0

Views: 156

Answers (2)

Pruthvi Barot
Pruthvi Barot

Reputation: 2018

lyrics_list = models.Song.objects.get(pk=self.kwargs['pk']).lyrics_as_list()
lyrics_sorted = models.Song.objects.sorted_strings(set(lyrics_list), "ru_RU.UTF8")

Manager's method can only be called with Model.objects

Upvotes: 0

cezar
cezar

Reputation: 12012

The method sorted strings as defined in the class Song:

class Song(models.Model):
    """
    fields
    """

    def sorted_strings(self, strings, locale=None):
        if locale is None:
            return sorted(strings)
        collator = icu.Collator.createInstance(icu.Locale(locale))
        return sorted(strings, key=collator.getSortKey)

cannot be called like this:

sorted_strings(strings, locale)

This obviously leads to a NameError. It is a method, so you need to call it on an object as instance of that class.

You might get tempted to try this:

Song.sorted_strigns(strings, locale)

but it won't work. It will result in missing 1 required positional argument. That is you have to provide self. You can't call it directly on the class.

If you introspect this method well, it doesn't perform any action on an object. The argument self is not used. You'd like to call it without instantiating an object, but directly on the class. Therefore you can use the decorator @staticmethod:

@staticmethod
def sorted_strings(strings, locale=None):
    if locale is None:
        return sorted(strings)
    collator = icu.Collator.createInstance(icu.Locale(locale))
    return sorted(strings, key=collator.getSortKey)

Please notice how the static method doesn't need to be passed the argument self.

Now your code should work like this:

lyrics_list = Song.objects.get(pk=self.kwargs['pk']).lyrics_as_list()
lyrics_sorted = Song.sorted_strings(set(lyrics_list), "ru_RU.UTF8")

Also in your code example you have an error with the parentheses in the second line.

Besides static methods, there are also class methods, defined using the decorator @classmethod. For this purpose here I'd go with the static method, but if someone has an objection, I'm ready to learn.

Static methods are not very common in Python and frowned upon by many.
Nevertheless it's good to know they exist and how they work.

Upvotes: 3

Related Questions