2

I have a Django class

class Chat(models.Model):
    primary_node = models.ForeignKey('nodes.Node', blank=True, null=True, related_name='chats_at_this_pri_node', on_delete=models.SET_NULL)
    secondary_node = models.ForeignKey('nodes.Node', blank=True, null=True, related_name='chats_at_this_sec_node', on_delete=models.SET_NULL)

I want to forbid direct assigning of fields, such as

chat.primary_node = some_node

and instead create a method chat.assign_node(primary, secondary) that updates nodes via Django Chat.update() model method.

The reason is that I want to log all changes to these nodes (count changes and update other model fields with the new count), but dont want myself and other developers to forget that we cannot assign fields directly, as this wouldn't trigger custom assign_node logic.

How сan I do that?

kurtgn
  • 8,140
  • 13
  • 55
  • 91

2 Answers2

1

You could try do prevent assignment to these fields with __setattr__, but I recommend you don't do that for two reasons:

  1. this will probably introduce all kinds of unexpected side-effects
  2. you are performing a check at runtime, to prevent something introduced at the time the code was written. That's like running a (tiny) unit-test on every request.

I would simply rename the fields to _primary_node and _secondary_node to indicate that these are private fields and not intended to be used directly.

In addition, you could write a hook for your version control system that checks for assignments to these fields. This could be a simple grep for _primary_node = or something fancier like a plugin for a linter like flake8.

Daniel Hepper
  • 28,981
  • 10
  • 72
  • 75
0

This simply disable setting of 'primary_node'

class Chat(models.Model)
    def __setattr__(self, attrname, val):
        if attrname == 'primary_node': #sets only the attribute if it's not primary_node
            print('[!] You cannot assign primary_node like that! Use assign_node() method please.')
        else:
            super(Chat, self).__setattr__(attrname, val) 

EDIT : You should probably use the @property decorator: Using @property versus getters and setters

Loïc
  • 11,804
  • 1
  • 31
  • 49
  • How do you implement `assign_node()`? Will assignment in the setter also trigger `__setattr__`? – mhatch May 13 '20 at 14:40
  • Actually I was wrong, you should probably give a look to ```@property``` decorator : https://stackoverflow.com/questions/6618002/using-property-versus-getters-and-setters – Loïc May 14 '20 at 01:06