Skip to content

Instantly share code, notes, and snippets.

@tpoisseau
Forked from andialbrecht/models.py
Created January 9, 2019 08:37
Show Gist options
  • Save tpoisseau/9adcf0f8d129ec2e4fc1bdc050355d39 to your computer and use it in GitHub Desktop.
Save tpoisseau/9adcf0f8d129ec2e4fc1bdc050355d39 to your computer and use it in GitHub Desktop.
Adds Meta option db_column_prefix that prepends a per-model prefix to db column names
# -*- coding: utf-8 -*-
# This hack adds a new Meta option "db_column_prefix". If not None all
# database columns in a model will be prepended with this prefix (the
# database columns, not the attributes). See Person model for an
# example.
#
# Issues:
# - How to set a prefix for a m2m table? - If "through" argument is
# not specified on a M2MField, Django automatically adds a model
# representing the m2m table (search for
# create_many_to_many_intermediary_model). It's not easy to hook
# into this process without really ugly hacks. So the easiest way is
# to use the "through" argument...
# - Get rid of monkey patching
# - If your db admin is sitting next to you, you need to have prefixes
# for tables generated by other apps (like auth) too. With this
# approach there's currently no way to achieve this. It'd be great
# to have a central place for building table and column names (maybe
# the db backend?)
from django.db import models
# Monkey-patch DEFAULT_NAMES for Meta options. Otherwise
# db_column_prefix would raise an error.
from django.db.models import options
options.DEFAULT_NAMES += ("db_column_prefix",)
class ColumnPrefixModelMeta(models.base.ModelBase):
"""Model metaclass that adds Meta.db_column_prefix to a db column name."""
def __new__(cls, name, bases, attrs):
new_cls = super(ColumnPrefixModelMeta, cls).__new__(
cls, name, bases, attrs)
prefix = getattr(new_cls._meta, "db_column_prefix", None)
# db_column_prefix on abstract base classes will be ignored.
if not new_cls._meta.abstract and prefix is not None:
# Shamelessly stolen from ModelBase.__new__:
new_fields = new_cls._meta.local_fields + \
new_cls._meta.local_many_to_many + \
new_cls._meta.virtual_fields
for field in new_fields:
if field.db_column is None:
field.db_column = field.get_attname()
if not field.db_column.startswith(prefix):
field.db_column = "%s%s" % (prefix, field.db_column)
# This sets field.column which is needed for build SQL
# statements
field.set_attributes_from_name(field.name)
return new_cls
class Person(models.Model):
__metaclass__ = ColumnPrefixModelMeta
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
created = models.DateTimeField(
auto_now_add=True, db_column="crdate")
modified = models.DateTimeField(
auto_now=True, db_column="upddate")
class Meta:
# The following column names will be used: per_first_name,
# per_last_name, per_crdate, per_uppdate.
db_column_name = "per_"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment