Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying model Meta data through decorators (django)

The problem: I wish to use Postgres Schemas to separate the tables of different parts of my django app at database level.

Aside

You can skip this section, but I think it's helpful to add context to these things. My app is working on a database of existing data (stored in the public schema, helpfully), which it's very important I don't modify. As such, I want to separate "my" data into a separate schema (to which django will be given read/write/play in the sand access), while restricting access to the public schema to read-only. I originally tried to solve this by separating my data out into a separate database and using database routing, but it turns out (if I'd only read the documentation) that django doesn't support cross database dependencies (which is fair enough I suppose), and my models have foreign keys into the read-only data.

The meat

There exists a workaround for Django's lack of schema support (which you can read about here) which is to specify the db_table attribute in your model's meta, like so:

class MyModel(models.Model):
    attribute1 = models.CharField()

     #Fool django into using the schema
    class Meta:
        db_table = 'schema_name\".\"table_name'

This is great, but I didn't really want to have to write this for every single model in my app - for a start, it doesn't seem pythonic, and also there's every chance of me forgetting when I have to add a new model.

My solution was the following snippet:

def SchemaBasedModel(cls):
    class Meta:
        db_table = '%s\".\"%s' % (schema_name, cls.__name__)
    cls.Meta = Meta
    return cls

@SchemaBasedModel
class MyModel(models.Model):
    attribute1 = models.CharField()
    ...

When I then run python manage.py shell I get the following:

>>> from myapp import models
>>> myModel = models.MyModel
>>> myModel.Meta.db_table
'myschema"."mymodel'
>>> 

"Looks good to me," I thought. I then ran: python manage.py sqlall myapp. Sadly, this yielded the original table names - that is, the table names as they were before I applied this meta info. When I went back and applied the meta info "by hand" (i.e. by adding Meta inner classes to all my models), things were as expected (new table names).

I was hoping somebody could enlighten me as to what was going on here? Or, more usefully, what's the "right" way to do this? I thought the decorator pattern I've talked about here would be just the ticket for this problem, but apparently it's a non-starter. How can I quickly and easily apply this meta info to all my models, without typing it out every single time?

Edit: Perhaps I was a little unclear when I asked this - I'm as interested in know what's "actually going on" (i.e. why things aren't working the way I thought they would - what did I misunderstand here?) as how to solve my problem (clear separation of "my" data from legacy data, preferably on a schema level - but it's not the end of the world if I have to dump everything into the public schema and manage permissions on a per-table basis).

Second Edit: The accepted answer doesn't necessarily tell me what I really want to know, but it is probably the right solution for the actual problem. Short answer: don't do this.

like image 491
jelford Avatar asked Jun 27 '26 00:06

jelford


1 Answers

I didn't really want to have to write this for every single model in my app - for a start, it doesn't seem pythonic,

That's false. Some things have to be written down explicitly. "Explicit is better than Implicit".

and also there's every chance of me forgetting when I have to add a new model

That's false, also.

You won't "forget".

Bottom Line: Don't mess with this kind of thing. Simply include the 2 lines of code explicitly where necessary.

You don't have that many tables.

You won't forget.

Also, be sure to use DB permissions. Grant SELECT permission only on your "legacy" tables (the tables you don't want to write to). Then you can't write to them.

like image 68
S.Lott Avatar answered Jun 28 '26 15:06

S.Lott



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!