Difference between setattr and object manipulation in python/django
Asked Answered
S

2

44

I have the following model:

class Ticket(models.Model):
    title = models.CharField()
    merged_to = models.ForeignKey("self", related_name='merger_ticket', null=True, blank=True)
    looser_ticket = models.BooleanField(default=False)

There are couple of ways of manipulating the model:

First

ticket = Ticket.objects.get(pk=1)
ticket.title = "This is edit title"
ticket.merged_to_id = 2
ticket.looser_ticket = True

Second

ticket = Ticket.objects.get(pk=1)
setattr(ticket, "title", "Edit Title")
setattr(ticket, "merged_to_id", 2)
setattr(ticket, "looser_ticket", True)

When I was manipulating the stuff, in views for the boolean value updation the first method did not work, however the second method worked. What is the difference between using first and second, and when they should be used?

Thanks!

Spoilage answered 9/10, 2012 at 13:48 Comment(1)
ah actually there was a typo, and the values were not being saved to the database.Spoilage
S
59

This is more of a Python question.

Python is very dynamic language. You can code things (classes) ahead of time, or Python allows you to create classes completely dynamically at run-time. Consider the following example of a simple vector class. You can create/code the class ahead of time like:

class MyVector(object):
    x = 0
    y = 0

or you can create the class dynamically by doing:

fields = {'x':0, 'y':0}
MyVector = type('MyVector', (object,), fields)

The main difference between the methods is that for one you know the class attributes ahead of time, whereas for the second method as you can imagine, you can programmatically create the fields dictionary, therefore you can create completely dynamically class(es).

So when you know the attributes of the class ahead of time, you can set class attributes using the object notation:

instance.attribute = value

Keep in mind that that is equivalent to:

instance.__setattr__("attribute", value)

However there are scenarios where you don't know the class attributes you will need to manipulate ahead of time. This is where you can use __setattr__ function. However it is not recommended practice. So instead the recommendation is to use Python's build-in method setattr which internally calls the __setattr__ method:

setattr(instance, attribute, value)

Using this approach you can set attributes you don't know ahead of time or you can even loop of some dict and set values from dict:

values = {
    'title': 'This is edit title',
    ...
}
for k, v in values.items():
    setattr(ticket, k, v)

Not sure why the regular notation did not work for. It probably has nothing to do with the method you used to set attributes.

Syne answered 9/10, 2012 at 14:9 Comment(5)
Just to add, since the context is a django model, the concept of creating a dynamic class is some what useless since all model fields are fixed during runtime. In such a case the only purpose of using setattr is to assign optional property values based on the subset of property values specified in the request dataMendelssohn
@Pratik Mandrekar: many people are interested in dynamic class in django. For instance, when you want to manipulate a user uploaded table. You dont know what the fields attributes would be. see: https://mcmap.net/q/57349/-django-dynamic-model-fieldsCroupier
Thanks for that reference. That explains a few things. However I have solved this problem by using a JSONField (pypi.python.org/pypi/django-jsonfield) with postgres as the backend. I'm not sure why the OP in the linked question thinks reporting is hard with JSON attribute on a django model. I do it all the time. Any idea?Mendelssohn
@PratikMandrekar Because you cannot easily query JSON field. For example you cannot do things like Model.objects.filter(date_field__lt=datetime.now())...Syne
I use the sql directly for certain complex queries and with postgres 9.3 onward, querying JSON in sql is straightforward (postgresql.org/docs/9.3/static/functions-json.html). But yeah you do lose the benefits of the Django ORM.Mendelssohn
P
11

If you know the objects properties before hand you should probably be using the first method. Just assign property values directly.

The second can be useful when you need to dynamically assign a value to a property. Perhaps a user has the ability to change the values of a number of different attributes and you don't know which one will have a value before hand, you can dynamically add values

possible_fields = ['title', 'looser_ticket']
ticket = Ticket.objects.get(pk=1)
for field in possible_fields:
  if request.GET.get(field):
     setattr(ticket, field, request.GET[field])

The "not working" is probably not dependent on the way you are setting values, are you sure that you saved your changes afterwards??

Pend answered 9/10, 2012 at 13:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.