2

I have been trying to figure out the best way to automate creating multiple SQL tables based on separate but identical models, all based on the same base class. I'm basically creating pseudo message boards or walls with different Groups, and I wanted each Group to have its own db_table of Posts, each Post containing the user id, timestamp, etc.

My first thought was to have one base class of Posts and just include a field for Group name, but I thought this would be bad practice. My rationale was that one table containing every Post for all Groups would get really big (in theory anyway) and slow down filtering, and also that the extra field for group name would in the long run be a waste of memory when I could have separate tables per group and skip this field.

I've also considered using a ForeignKey with a Many-to-One relationship, but as far as I can tell this has the same drawbacks. Am I wrong to think that? Or are these size concerns not really an issue?

So my next idea was to make Posts an abstract class, and then create subclasses based on each Group. This is ultimately what I did. However, I found myself having to copy and paste the code over and over and change the class name each time. This felt very unPythonic to me. It was something like:

class Posts(models.Model):
    timestamp = models.DateTimeField(auto_now_add=True, unique=False)
    user_id = ...
    #etc.
    #
    class Meta:
        abstract = True


class GroupA(Posts):
    class Meta(Posts.Meta):
        db_table = 'groupa_board'

class GroupB(Posts):
    class Meta(Posts.Meta):
        db_table = 'groupb_board'

class GroupC...etc.

What I really was looking for was a factory function to do this for me. I tried this sort of thing:

def makeBoard(group):
    class Board(Posts):
        class Meta(Posts.Meta):
            db_table = group
    return board        #note I tried with and without this line

And then I ran a simple for loop using a list of groups.

for group in groups:
    makeBoard(group)

I found myself hitting a RuntimeError: conflicting models in application, and I probably deserved it. So then I figured what I need is something like:

def makeBoard(group):
    class group(Posts):      #***group here being a variable, not the class name
        class Meta(Posts.Meta):
            db_table = '%s' % group #maybe issues here too, but the table
    return group                    #name is not that important if the class
                                    #name works

But I couldn't figure out how to make this work! Is there a way to pass a variable from a list to a class name?

Anyway if you're still with me I appreciate it. I've been on stackoverflow all day and while I've found guides for creating abstract base classes and subclasses to solve similar issues, I didn't see a way to create a function to do this for me. I ultimately punted here and just make a subclass for each group by hand. If there is a way to automate this process, I'd love to hear it.

Also, if I'm being stupid for not just going with one db table containing every post, I'd like to know that too, and why! Or if there's a better way to implement this kind of system altogether. I apologize if this has been answered before, I really couldn't find it.

Thank you!

2 Answers 2

2

Using a single table would not be bad practice. The extra memory is minimal, on modern systems that shouldn't be a problem. You shouldn't worry about performance either, premature optimization (not including the actual system design) is considered bad practice, but if you run into performance problems you can always specify an index on the group column:

group = models.CharField(max_length=100, db_index=True)

That's not to say that it is the best option, or that your method isn't good. Also, it is entirely possible to dynamically create models, using the type() built-in function. The only difference with dynamically creating models and creating other classes is that you must specifically pass the __module__ attribute. You can create subclasses for Posts in the following way:

def fabric(names, baseclass=Posts):
    for name in names:
        class Meta:
            db_table = '%s_table' % name.lower()
        attrs = {'__module__': baseclass.__module__, 'Meta': Meta} 
        # specify any other class attributes here. E.g. you can specify extra fields:
        attrs.update({'my_field': models.CharField(max_length=100)})
        newclass = type(str(name), (baseclass,), attrs)
        globals()[name] = newclass 

fabric(['GroupA', 'GroupB', 'GroupC', etc...])

Put that code in your models.py after your Posts class, and all classes will be created for you. They can be used in any way normal classes can be used: Django doesn't even know you dynamically created this class. Though your Meta class doesn't inherit from Posts.Meta, your meta settings should still be preserved.

Tested with Django 1.4.

Sign up to request clarification or add additional context in comments.

1 Comment

Awesome! Worked perfectly in 1.8. And thanks for the explanation. I never thought to use indexes, although now I suppose that would've handled my concerns just as well. Anyway I've already cleaned up my code with the above. It really did exactly what I had in mind. Good to know about the module attribute, I'll have to remember that. Thanks again!
0

Try smth like this

import app.models as group_models
from django.db.models.base import ModelBase

def fabric(group):
    for item in dir(group_models):
        c = getattr(group_models, item)
        if type(c) is ModelBase:
            if c._meta.db_table == '%s_table' % group:
                return c
    return None

4 Comments

hmm.. didn't seem to have an effect for me. Could you explain how this works? Doesn't it presuppose I've already made my models? Anyway I'll play around with it and ModelBases some more. Thanks!
I didn't understand the question correctly. I suppose there is no any simple way to do what you need.
But, if it not necessary to have foreign tables for each models, you can experiment with ModelManager. I mean you can have some field to identify group, and rewrite ModelManager in fabric.
no worries! actually knbk provided a great answer here, definitely check it out.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.