Hi!
I would like to add attributes to a Django models programmatically. At class creation time (the time of the definition of the model class). The model is not going to change after that in run time. For instance, lets say I want to define a Car
model class and want to add one price
attribute (database column) per currency, given a list of currencies. (This list of currencies should be considered a constant that won't change runtime. I don't want a related model for these prices.)
What would be the best way to do this?
I had an approach that I thought would work, but it didn't exactly. This is how I tried doing it, using the car example above:
from django.db import models
class Car(models.Model):
name = models.CharField(max_length=50)
currencies = ['EUR', 'USD']
for currency in currencies:
Car.add_to_class('price_%s' % currency.lower(), models.IntegerField())
This does seem to work pretty well at first sight:
$ ./manage.py syncdb
Creating table shop_car
$ ./manage.py dbshell
shop=# \d shop_car
Table "public.shop_car"
Column | Type | Modifiers
-----------+-----------------------+-------------------------------------------------------
id | integer | not null default nextval('shop_car_id_seq'::regclass)
name | character varying(50) | not null
price_eur | integer | not null
price_usd | integer | not null
Indexes:
"shop_car_pkey" PRIMARY KEY, btree (id)
But when I try to create a new Car, it doesn't really work anymore:
>>> from shop.models import Car
>>> mycar = Car(name='VW Jetta', price_eur=100, price_usd=130)
>>> mycar
<Car: Car object>
>>> mycar.save()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/base.py", line 410, in save
self.save_base(force_insert=force_insert, force_update=force_update)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/base.py", line 495, in save_base
result = manager._insert(values, return_id=update_pk)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/manager.py", line 177, in _insert
return insert_query(self.model, values, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/query.py", line 1087, in insert_query
return query.execute_sql(return_id)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/sql/subqueries.py", line 320, in execute_sql
cursor = super(InsertQuery, self).execute_sql(None)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/models/sql/query.py", line 2369, in execute_sql
cursor.execute(sql, params)
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages/django/db/backends/util.py", line 19, in execute
return self.cursor.execute(sql, params)
ProgrammingError: column "price_eur" specified more than once
LINE 1: ...NTO "shop_car" ("name", "price_eur", "price_usd", "price_eur...
^
Apparently, though, my code seem to run several times, causing the "price_eur" attribute to be added several times.
Comment: Initially I used the wording "at run time" ("I would like to add attributes to a Django models programmatically, at run time."). This wording wasn't the best. What I really want to was to add those fields at "model definition time" or "class creation time".