代码之家  ›  专栏  ›  技术社区  ›  mojbro

以编程方式指定Django模型属性

  •  6
  • mojbro  · 技术社区  · 16 年前

    我想以编程方式向Django模型添加属性。在类创建时(定义模型类的时间)。在运行时,模型不会在这之后改变。例如,假设我想定义 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”属性被添加了好几次。

    4 回复  |  直到 16 年前
        1
  •  4
  •   Jonny Buchanan    16 年前

    locals 解决方法是使用 Field.contribute_to_class :

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

    I used it for MPTT (为此,我一直是一个非常糟糕的维护者)

    仔细想想,你的代码运行得很好( Car.add_to_class contribute_to_class 但是问题似乎是添加额外字段的代码被多次执行,因此您的模型认为需要用相同的名称保存多个字段。您需要在其中添加一些内容,以确保只动态添加一次字段。

    django.db.models.fields.Field.contribute_to_class 电话 django.db.models.options.Options.add_field _meta Options ),它不检查是否已经存在同名字段,并将字段详细信息添加到它知道的字段列表中。

        2
  •  4
  •   Łukasz    16 年前

    我的解决方案是由于各种原因造成的,但它是有效的:

    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
  •  4
  •   S.Lott    16 年前

    “我想以编程方式向Django模型添加属性。“在创建类时”

    对于我们这些维护人员来说,这些代码将(a)毫无意义,(b)必须被简单、明显的代码所取代,这些代码以最简单、最明显的方式完成相同的工作。

    没有理由用一个看起来很棘手的循环来替换一组简单的属性定义。没有进步也没有储蓄。

    1. 视图函数的运行时性能是相同的。

    2. 开发成本(即解决这个问题)更高。

    即使你有100种货币,这仍然是一个非常糟糕的主意。

    “这些价格我不想要相关车型”

    为什么不呢?一个相关的模型是(1)简单,(2)明显,(3)标准,(4)可扩展。它在运行时或开发时几乎没有可测量的成本,当然也没有复杂性。

    (数据库列)每种货币,给定一个货币列表。“

    就是这样 不 一个新的属性。

    这是一个表的新值(在一行中),该表以汽车型号和货币作为键。

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

    “我想在运行时以编程方式向Django模型添加属性。”

        4
  •  0
  •   Felix Kling    16 年前

    不能在运行时执行此操作。它将更改数据库模型,并通过命令更改数据库 syncdb

    为什么不创建第二个模型 Price 这决定了不同货币的价格。或者如果可能的话,把价格转换成一种特定的货币。

    推荐文章