Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django model fields with dynamic names

Tags:

python

django

I'd like to add to existing models new CharFields via one common mixin or abstract model but names of these fields depend on configuraton. so one model will have someprefix1_title field and another model - someprefix2_title.

Is it possible to make this approach to work:

class AbstractModel(models.Model):
    self.fields_prefix + '_title' = models.CharField(max_length=255, blank=True, default='')

    class Meta:
        abstract = True

class ModelOne(AbstractModel):
    fields_prefix = 'someprefix1'
    id = models.AutoField(primary_key=True)

class ModelTwo(AbstractModel):
    fields_prefix = 'someprefix2'
    id = models.AutoField(primary_key=True)

so ModelOne could have fields id and someprefix1_title.

upd: what about monkey-patching with add_to_class() will it work or it's an antipattern and should not be used?

like image 351
MrBinWin Avatar asked Mar 01 '19 11:03

MrBinWin


People also ask

What are dynamic fields in Django?

Such fields are derived from other fields of the same entity (model) and they might change over time (age, for example). We can create dynamic fields in django in more than one way.

What are model data types and fields in Django?

Django model data types and fields list. The most important part of a model and the only required part of a model is the list of database fields it defines. Fields are specified by class attributes. Be careful not to choose field names that conflict with the models API like clean, save, or delete.

How do I add a field to a model in Django?

Each model and field in Django has an associated table and column in the database. To add new fields usually requires either raw sql, or migrations using South. From a front end interface, you could create pseudo fields, and store them in a json format in a single model field.

What is an instance of a field class in Django?

Each field in the model should be an instance of the appropriate Field class. Django uses field class types to determine a few things: The column type, which tells the database what kind of data to store (e.g. INTEGER, VARCHAR, TEXT).


2 Answers

Try using a factory pattern to set up your different versions of AbstractModel.

With this approach, you can more strictly control the way AbstractModel is modified by way of the factory function dynamic_fieldname_model_factory.

We're also not modifying ModelOne or ModelTwo after their definitions -- other solutions have pointed out that this helps avoid maintainability problems.

models.py:

from django.db import models


def dynamic_fieldname_model_factory(fields_prefix):
    class AbstractModel(models.Model):

        class Meta:
            abstract = True

    AbstractModel.add_to_class(
        fields_prefix + '_title',
        models.CharField(max_length=255, blank=True, default=''),
    )
    return AbstractModel


class ModelOne(dynamic_fieldname_model_factory('someprefix1')):
    id = models.AutoField(primary_key=True)


class ModelTwo(dynamic_fieldname_model_factory('someprefix2')):
    id = models.AutoField(primary_key=True)

Here is the migration generated by this code:

# Generated by Django 2.1.7 on 2019-03-07 19:53

from django.db import migrations, models


class Migration(migrations.Migration):

    initial = True

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='ModelOne',
            fields=[
                ('someprefix1_title', models.CharField(blank=True, default='', max_length=255)),
                ('id', models.AutoField(primary_key=True, serialize=False)),
            ],
            options={
                'abstract': False,
            },
        ),
        migrations.CreateModel(
            name='ModelTwo',
            fields=[
                ('someprefix2_title', models.CharField(blank=True, default='', max_length=255)),
                ('id', models.AutoField(primary_key=True, serialize=False)),
            ],
            options={
                'abstract': False,
            },
        ),
    ]
like image 181
whp Avatar answered Nov 08 '22 23:11

whp


Django models can be created with dynamic field names . Here is a simple Django model:

class Animal(models.Model):
    name = models.CharField(max_length=32)

And here is the equivalent class built using type():

attrs = {
    'name': models.CharField(max_length=32),
    '__module__': 'myapp.models'
}
Animal = type("Animal", (models.Model,), attrs)

Any Django model that can be defined in the normal fashion can be made using type().

To run migrations:South has a reliable set of functions to handle schema and database migrations for Django projects. When used in development, South can suggest migrations but does not attempt to automatically apply them

from south.db import db
model_class = generate_my_model_class()
fields = [(f.name, f) for f in model_class._meta.local_fields]
table_name = model_class._meta.db_table
db.create_table(table_name, fields)
# some fields (eg GeoDjango) require additional SQL to be executed
db.execute_deferred_sql()
like image 29
Mohit Harshan Avatar answered Nov 08 '22 23:11

Mohit Harshan