11

I have the following function:

def create_act(user, verb, fk_name=None, fk_value=None):
    fk = getattr(Action, fk_name)
    action = Action(user=user, verb=verb, fk=fk_value)
    action.save()

Action is a class. The class has multiple attributes, and I don't know at the beginning, which attribute will get a value.

I get the attribute name dynamic.

I want the kwarg fk, to be an actual attribute of the class action. FK can be account or company.

class Action(models.Model):

account = models.ForeignKey(Account, blank=True, null=True, related_name='activity', on_delete=models.CASCADE)
company = models.ForeignKey(Company, blank=True, null=True, related_name='activity', on_delete=models.CASCADE)

I found on the forums some answers but nothing relevant to me, or in python. I saw some suggestion on other sites to use eval, but eval is not safe.

Hai Vu
  • 37,849
  • 11
  • 66
  • 93
user3541631
  • 3,686
  • 8
  • 48
  • 115
  • here is a helpful link: https://stackoverflow.com/questions/3394835/args-and-kwargs – Harry_Hirsch Dec 20 '17 at 13:39
  • It would be helpful if you gave some example *values* and an example of what kind of call you want to emulate. Are you trying to say that `fk` in `Action(.., fk=...)` should be a variable value based on the `fk` variable, e.g. `Action(..., foo=fk_value)`? – deceze Dec 20 '17 at 13:39
  • @deceze more info added, is django I create an instance, and fk is a Foreign Key – user3541631 Dec 20 '17 at 13:41
  • Still rather unclear. You want the actual call to look like `Action(..., Account='foo')`? The most helpful thing would be if you showed us what the call would look like if you wrote it by hand. – deceze Dec 20 '17 at 13:45

2 Answers2

19

Use dict unpacking with **. Note that -- if fk_name is the name of a ForeignKey -- fk will not be a string, but a ForwardManyToOneDescriptor. You'd probably still want to set the attribute named fk_name:

action = Action(user=user, verb=verb, **{fk_name: fk_value})
user2390182
  • 72,016
  • 6
  • 67
  • 89
  • 1
    Dict unpacking is right, but the whole point surely is that `fk` *isn't* a string. – Daniel Roseman Dec 20 '17 at 13:36
  • @DanielRoseman fk_name that is the argument of the initial function is a string, can I used directly without using getattr ? – user3541631 Dec 20 '17 at 13:38
  • 2
    @DanielRoseman Well, it still shows which form can be used to achieve dynamic keyword assignment. OP's problem should be translatable into this. – Arne Dec 20 '17 at 13:39
2

The idea is to dynamically pass to the class creation a pair of attribute name and value, to do that, create a dictionary using the name/value attribute and pass it along using dictionary unpacking:

def create_act(user, verb, name, value):
    attrs = {name: value}
    action = Action(user=user, verb=verb, **attrs)
    action.save()
    return action
Hai Vu
  • 37,849
  • 11
  • 66
  • 93