[Django]-Single Django model, multiple tables?

25👍

You could, I believe, make a factory function that would return your model with a dynamic db_table.

def getModel(db_table):
  class MyClass(models.Model):
     # define as usual ...
     class Meta:
       db_table = db_table

  return MyClass

newClass = getModel('29345794_table')
newClass.objects.filter( ...

EDIT: Django does not create a new instance of the class’s _meta attribute each time this function is called. Creating a new instance for _meta it is dependent upon the name of the class (Django must cache it somewhere). A metaclass can be used to change the name of the class at runtime:

def getModel(db_table):
  class MyClassMetaclass(models.base.ModelBase):
    def __new__(cls, name, bases, attrs):
      name += db_table
      return models.base.ModelBase.__new__(cls, name, bases, attrs)

  class MyClass(models.Model):
    __metaclass__ = MyClassMetaclass

    class Meta:
      db_table = db_table

  return MyClass

not sure if it can be set dynamically on an already-defined class. I haven’t done this myself but it might work.

You can set this whenever.

>>> MyModel._meta.db_table = '10293847_table'
>>> MyModel.objects.all()

9👍

Create a model for your table dynamically.

from django.db import models
from django.db.models.base import ModelBase

def create_model(db_table):

    class CustomMetaClass(ModelBase):
        def __new__(cls, name, bases, attrs):
            model = super(CustomMetaClass, cls).__new__(cls, name, bases, attrs)
            model._meta.db_table = db_table
            return model

    class CustomModel(models.Model):

        __metaclass__ = CustomMetaClass

        # define your fileds here
        srno = models.IntegerField(db_column='SRNO', primary_key=True)

    return CustomModel

and you can start querying the database.

In [6]: t = create_model('trial1')

In [7]: t._meta.db_table
Out[7]: 'trial1'

In [8]: t.objects.all()  # default db
Out[8]: [<CustomModel: CustomModel object>, '(remaining elements truncated)...']

In [9]: t.objects.using('test').all()  # test db
Out[9]: [<CustomModel: CustomModel object>, '(remaining elements truncated)...']

0👍

combined many stackflow answer to reach this

#connection should be passed from the django_project
#using django.db import connection

 class multiModel():

def __init__(self, model_name, table_name, prototype, app_name, connection) -> None:
    """
        @model_name: name of the new table object
        @table_name: name of the table with which it has to
                     be created in database
        @prototype: model which has to be used as prototype for
                    creating new table 

        @app_name: app for which table has to be created

        @connection: connection to be used


    """

    self.model_name = model_name
    self.table_name = table_name
    self.prototype = prototype
    self.app_name = app_name
    self.connection = connection
    self.name_table_db = f"{self.app_name}_{self.table_name}"


def get(self):

    if self.__exists__():

        Model = self.__create_model__(create=False)


    else:

        Model = self.__create_model__()

    return Model



def __exists__(self):

    with self.connection.cursor() as cursor:
        
        cursor.execute("show tables;")
        tables = [each[0] for each in cursor.fetchall()]

    result = False

    if self.name_table_db.lower() in tables:
        
        result = True

    return result

    



def __create_model__(self, create = True):

    class Meta:
        pass

    setattr(Meta, "db_table", self.name_table_db)
    #self.db_table = f"{self.app_name}_{self.table_name}"

    fields = {}

    for field in self.prototype._meta.fields:

        fields[field.name] = field.clone()

    attrs = {'__module__':f"{self.app_name}.models", "Meta":Meta}
    self.attrs = attrs
    attrs.update(fields)


    model = type(self.model_name, (models.Model,), attrs)

    if create:
        
        with self.connection.schema_editor() as schema_editor: schema_editor.create_model(model)

    return model

Leave a comment