Программно указывающие атрибуты модели Django

Я хотел бы добавить атрибуты к Django модели программно. Во время создания класса (время определения образцового класса). Модель не собирается изменяться после этого во время выполнения. Например, позволяет, говорят, что я хочу определить a Car образцовый класс и хочет добавить тот price атрибут (столбец базы данных) на валюту, учитывая список валют. (Этот список валют нужно считать константой, которая не изменит время выполнения. Я не хочу связанную модель за эти цены.)

Каков был бы лучший способ сделать это?

У меня был подход, что я думал, будет работать, но он не сделал точно. Это - то, как я пытался делать его, с помощью автомобильного примера выше:

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())

Это, действительно кажется, работает вполне прилично на первый взгляд:

$ ./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)

Но когда я пытаюсь создать новый Автомобиль, он действительно больше не работает:

>>> 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...
                                                             ^

По-видимому, тем не менее, мой код, кажется, несколько раз работает, заставляя атрибут "price_eur" несколько раз добавляться.

Комментарий: Первоначально я использовал формулировку "во время выполнения" ("Я хотел бы добавить атрибуты к Django модели программно во время выполнения".). Эта формулировка не была лучшей. Что я действительно хочу, должны были добавить те поля в "образцовое время определения" или "время создания класса".

6
задан mojbro 24 March 2010 в 06:50
поделиться

4 ответа

Все еще неприятно, но лучше, чем использование locals , решение - использовать Field.contribute_to_class :

for currency in currencies:
    models.IntegerField().contribute_to_class(Car, 'price_%s' % currency.lower())

Я использовал его для MPTT (для чего я был очень плохим сопровождающим * прячется *)

Изменить: Если подумать, ваш код работал нормально ( Car.add_to_class вызывает поле identify_to_class за вас), но проблема, похоже, в том, что код для добавления дополнительных полей выполняется несколько раз, поэтому ваша модель считает, что необходимо сохранить несколько полей с тем же именем. Вам нужно что-то вставить туда, чтобы убедиться, что вы динамически добавляете поля только один раз.

django.db.models.fields.Field.contribute_to_class вызывает django.db.models.options.Options.add_field (атрибут объекта _meta является экземпляром an Options ), который не проверяет, существует ли уже поле с таким именем, и с радостью добавляет детали поля в список полей, о которых он знает.

4
ответ дан 10 December 2019 в 00:36
поделиться

Вы не можете сделать это во время выполнения. Это приведет к изменению модели базы данных, и база данных будет изменена с помощью команды syncdb , и почему-то это кажется действительно уродливым.

Почему бы не создать вторую модель Price , которая содержит цену для разных валют. Или, если возможно, конвертировать цену на лету к определенной валюте.

0
ответ дан 10 December 2019 в 00:36
поделиться

«Я хотел бы программно добавлять атрибуты к моделям Django. Во время создания класса»

Не делайте этого. «Программное» добавление столбцов глупо и сбивает с толку. Это кажется прекрасным вам, разработчику, который глубоко разбирается в нюансах Django.

Для нас, сопровождающих, этот код (а) не имеет смысла и (б) должен быть заменен простым, очевидным кодом, который выполняет ту же работу самым простым и очевидным способом.

Помните, сопровождающие - жестокие социопаты, которые знают, где вы живете. Порадуйте их простым и понятным кодом.

Нет причин заменять кластер простых определений атрибутов сложным на вид циклом. Нет ни улучшений, ни экономии.

  1. Производительность функций просмотра во время выполнения такая же.

  2. Одноразовое определение класса сохранило несколько строк кода, которые выполняются один раз во время запуска приложения.

  3. Стоимость разработки (т.е. решения этого вопроса) выше.

  4. Стоимость обслуживания (т. Е. Передача этого кода кому-то другому, чтобы он продолжал работать) астрономически высока. Код будет просто заменен на что-нибудь более простое и очевидное.

Даже если у вас есть сотни валют, это все равно плохая идея.

«Мне не нужна похожая модель по такой цене»

Почему бы и нет? Родственная модель: (1) простая, (2) очевидная, (3) стандартная, (4) расширяемая. Он почти не требует измеримых затрат во время выполнения или разработки и, конечно же, не имеет сложности.

«Например, допустим, у меня есть класс модели автомобиля и я хочу добавить один атрибут цены (столбец базы данных) для каждой валюты, учитывая список валют."

То есть вовсе не новый атрибут.

Это новое значение (в строке) таблицы, в которой в качестве ключей используются модель автомобиля и валюта.

Вы классифицируете выглядит примерно так:

class Price( models.Model ):
    car = models.ForeignKey( Car )
    currency = models.ForeignKey( Currency )
    amount = models.DecimalField()

Предыдущая версия вопроса

«Я хотел бы добавить атрибуты к моделям Django программно, во время выполнения».

Не делайте этого. Нет абсолютно никаких обстоятельств, при которых вы когда-либо хотите добавить атрибуты в базу данных «во время выполнения».

3
ответ дан 10 December 2019 в 00:36
поделиться

Мое решение плохое по разным причинам, но оно работает:

from django.db import models

currencies = ["EUR", "USD"]

class Car(models.Model):

    name = models.CharField(max_length=50)

    for currency in currencies:
        locals()['price_%s' % currency.lower()] = models.IntegerField()

В том месте, где я должен был это сделать, у меня был выбор между чем-то вот так и ведение таблицы с более чем 200 столбцами (я знаю, насколько это было плохо, но я не имел на это никакого влияния).

3
ответ дан 10 December 2019 в 00:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: