1

When reading the docs, there is very little information how and why to use @property in a class. All I can find is:

Also known as “managed attributes”, and a feature of Python since version 2.2. This is a neat way to implement attributes whose usage resembles attribute access, but whose implementation uses method calls.

When I have a function inside a model like def get_absolute_url(self): should I be decorating it with @property?

@property
def get_absolute_url(self):
    pass

What is the difference between an un-decorated def and one decorated with @property? When should I use it and when not?

alias51
  • 8,178
  • 22
  • 94
  • 166
  • 2
    There is enough of details on @property on python documentation site which django docs lead to as it is python feature (See property link) -> https://docs.python.org/3/library/functions.html#property – iklinac Jan 11 '20 at 22:47

3 Answers3

7

When should you use @property in a model class?

You should use @property when your class attribute is formed from other attributes in the class, and you want it to get updated when source attributes are changed.

Example without @property

class Coordinates():
    def __init__(self, x, y):
        self.x = 'x'
        self.y = 'y'
        self.coordinates = [self.x, self.y]

    def revers_coordinates(self):
        return [self.y, self.x]

>>> a = Coordinates('x', 'y')
>>> a.coordinates
['x', 'y']
>>> a.revers_coordinates()
['y', 'x']
>>> a.x = 'z'
>>> a.coordinates 
['x', 'y'] # <===== no changes in a.x
>>> a.revers_coordinates()
['y', 'z']

As you see revers_coordinates() reacted, and self.coordinates did not. And if you want it to react, @property is an option.

Of course you could just change self.coordinates on function def coordinates(self), but this will break all places in your code when it is called as property without () (maybe your code is opensource, and it will break not only for you). In this case @property is what you want.

Example with @property

class CoordinatesP():
    def __init__(self, x, y):
        self.x = 'x'
        self.y = 'y'
    
    @property
    def coordinates(self):
        return [self.x, self.y]

    def revers_coordinates(self):
        return [self.y, self.x]

>>> a = CoordinatesP('x', 'y')
>>> a.coordinates
['x', 'y']
>>> a.revers_coordinates()
['y', 'x']
>>> a.x = 'z'
>>> a.coordinates
['z', 'y'] # <===== a.x has changed
>>> a.revers_coordinates()
['y', 'z']
Mohd
  • 5,523
  • 7
  • 19
  • 30
weAreStarsDust
  • 2,634
  • 3
  • 10
  • 22
2

The difference is that, a method decorated with @property behaves like a class attribute. So you can call it directly without using parentheses (thus no arguments. So if your function takes arguments you can't really use @property decorator)

For me, the most useful scenario for using @property with models is with templates. If you have a instance of a model object in a template, you can call it's properties, but not it's functions (you have to write your custom template tag). So for example, if you have a property called total_entries for model User, you can call it in a template like {{ user_instance.total_entries }} to access it instead of writing a template tag.

Actually, you can call your methods as properties in templates, if that method has no arguments.

Another case: Say that you have a operation which calculates a boolean value using your model's fields. For example, for User model you may want to calculate if the user is 'beloved user' based on current his score and years of membership. In this case it's more appropriate to use a property decorator as it looks pretty clear syntax wise. (So you will be using user.is_beloved in your views)

suayip uzulmez
  • 628
  • 8
  • 23
  • That's helpful, but I can call `{{ obj.get_absolute_url}}` without needing a property decorator? – alias51 Jan 12 '20 at 08:58
  • It behaves so, because it's django's built-in model method. And that's how developers implemented it. – suayip uzulmez Jan 12 '20 at 09:13
  • But in the source code there is no @property decorator... – alias51 Jan 12 '20 at 10:46
  • OK, I just checked and it turns out, if your method has no arguments you can call them in templates directly without needing to make them properties in models. (This is likely a feature of django template engine). So most of the time, you should stick to property decorator for readability and simplicity reasons. Also check out @weAreStarDust 's answer. – suayip uzulmez Jan 12 '20 at 11:09
0

When you use @propertydecorator, the given method will not be a callable but an attribute of the class. In other words, you cannot call it like self.get_absolute_url() .

You may ask what is the real difference then? With no parameters, there is not a real difference. But with the decorator, you guarantee that it is a property so that you can give extra parameters to the decorater, to dictate the setter/getter methods of that attribute. Like this: @property(fget=foo,fset=goo).

engin_ipek
  • 887
  • 1
  • 8
  • 10