Skip to content

Instantly share code, notes, and snippets.

@karanlyons
Created December 24, 2015 20:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save karanlyons/f0e1ed286635745b35cf to your computer and use it in GitHub Desktop.
Save karanlyons/f0e1ed286635745b35cf to your computer and use it in GitHub Desktop.
#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
from __future__ import division, absolute_import, print_function, unicode_literals
from copy import deepcopy
from django.db import models
class HideMetaOpts(models.base.ModelBase):
"""
A metaclass that hides added attributes from a class' ``Meta``, since
otherwise Django's fascistic Meta options sanitizer will throw an
exception. Default values can be set with default_meta_opts. By default
all non standard opts will be hidden from Django; if you just want to hide
the opts you've defined in default_meta_opts, set hide_unknown_opts
to ``False``.
(If you have another mixin that adds to your model's ``Meta``, create a
``metaclass`` that inherits from both this and the other
mixin's ``metaclass``.)
"""
default_meta_opts = {}
hide_unknown_opts = True
def __new__(cls, name, bases, attrs):
if not [b for b in bases if isinstance(b, HideMetaOpts)]:
return super(HideMetaOpts, cls).__new__(cls, name, bases, attrs)
else:
meta_opts = deepcopy(cls.default_meta_opts)
# Deferred fields won't have our model's Meta.
if 'Meta' in attrs and attrs['Meta'].__module__ != 'django.db.models.query_utils':
meta = attrs.get('Meta')
else:
# Meta is at a class level, and could be in any of the bases.
for base in bases:
meta = getattr(base, '_meta', None)
if meta:
break
# If there's no _meta then we're falling back to defaults.
if meta:
for opt, value in vars(meta).items():
if opt not in models.options.DEFAULT_NAMES and (cls.hide_unknown_opts or opt in meta_opts):
meta_opts[opt] = value
delattr(meta, opt)
new_class = super(HideMetaOpts, cls).__new__(cls, name, bases, attrs)
if meta:
for opt in meta_opts:
setattr(meta, opt, meta_opts[opt])
# We theoretically don't have to set this twice, but just in case.
for opt in meta_opts:
setattr(new_class._meta, opt, meta_opts[opt])
return new_class
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment