1

I want to specify a model field dynamically in models.py. For example:

class ModelA(models.Model):
   pass # whatever
# immediately after class declaration in models.py
if django_is_not_non_rel:
   MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")

But this code example does not work. When the condition is true, ModelA does not have the all_modelb field after everything is initialized. Why???? How do I effect my intention?

jacob
  • 2,762
  • 1
  • 20
  • 49
  • Why would you want to do this? – Steinar Lima Jan 05 '14 at 23:59
  • I tried to make my example as simple as possible. The real scenario is that the dynamic model field is only added if a particular condition is true. In my case, the field is a ManyToManyField which is not added if the Django framework is NonRel... Do you have a solution? – jacob Jan 06 '14 at 00:25
  • Why don't you just add the field in the model, and add the relationships to it if your condition is true? AFAIK, all instances of a specific model should have the same fields. (but I'm not that experienced with Django.. :) – Steinar Lima Jan 06 '14 at 00:28
  • If I understand your comment, you are saying to define the field as is, but not set it upon instantiation. Is that what you mean? If so, the critical error occurs when the model definition is added by Django, because the field is not supported by NonRel. Also, in the real case I am dealing with, it is actually a ManyToManyField using "through" attribute. That means the field doesn't get written to, just read using a JOIN. – jacob Jan 06 '14 at 00:38
  • There is a lot of magic going on when a model class is instantiated. You have to tap into this magic to add a field dynamically. – Paulo Scardine Jan 06 '14 at 00:39
  • @paulo: How is ModelA instantiated immediately when it is defined? I am setting the extra field right after the definition. Based on Python class semantics, why is it not identical to setting the class attribute inside the class definition? – jacob Jan 06 '14 at 00:43
  • 1
    @jacob it is not identical, because on there is some metaclass magic involved, that is [called at "definition time"](https://github.com/django/django/blob/f630373b929bc62bf4d66d60c532f7832e5fbe67/django/db/models/base.py#L55), see also http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python – sk1p Jan 06 '14 at 01:47
  • Thanks @sk1p. It makes a lot more sense now. I'm planning to use wolph's solution below, which avoids all the metaclass magic. – jacob Jan 06 '14 at 02:03

1 Answers1

2

Creating columns dynamically is not impossible with Django but you'll have to do it using a metaclass and/or inheritance.

For your problem the easiest solution would probably be something like this:

if django_is_not_non_rel:
    class ModelABase(models.Model):
        MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")

        class Meta:
            abstract = True

else:        
    class ModelABase(models.Model):
        class Meta:
            abstract = True

class ModelA(ModelABase):
   pass # whatever

Or even simpler (if you don't need as much customization):

class ModelAORMBase(models.Model):
    MyModel.all_modelb = models.ManyToManyField(ModelB, through="ModelAB")

    class Meta:
        abstract = True

if django_is_not_non_rel:
    ModelABase = ModelAORMBase
else:
    ModelABase = models.Model

class ModelA(ModelABase):
   pass # whatever
Wolph
  • 78,177
  • 11
  • 137
  • 148