Commits

Ionel Cristian Mărieș committed d6fc737

Added initial code. Everything should be broken.

Comments (0)

Files changed (15)

+syntax: glob
+*.egg
+*.pyc
+build
+dist
+django_piston.egg-info
+bin
+parts
+downloads
+.installed.cfg
+develop-eggs
+.DS_Store
+.svn
+*.swp

dcfields/cachedmtmfield.py

+"""
+This module contains the :class:`CachedManyToManyField` will add a primitive ID
+cache in the model that can be accessed via fieldname.cache (preferably) or
+fieldname_cache. The cache is a :class:`SetField`.
+"""
+
+from django.db.models.fields.related import ReverseManyRelatedObjectsDescriptor
+from django.db import models
+import pickle
+
+CACHE_FIELD_POSTFIX = '_cache'
+
+class SetField(models.TextField):
+    """ Implements a set stored as pickled object."""
+
+    __metaclass__ = models.SubfieldBase
+
+    def __init__(self, *args, **kwargs):
+        kwargs['editable'] = False #don't allow editing from admin
+        #TODO: remove this: kwargs['max_length'] = 255 #this should be enough for now
+        super(SetField, self).__init__(*args, **kwargs)
+
+    def to_python(self, value):
+        if isinstance(value, basestring) and value:
+            return pickle.loads(str(value))
+        if isinstance(value, set):
+            return value
+        return set()
+
+    def get_db_prep_value(self, value):
+        if not value:
+            value = set()
+        r = pickle.dumps(value)
+        return r
+
+    def get_prep_lookup(self, lookup_type, value):
+        raise TypeError("Lookup type %s not supported." % lookup_type)
+
+def get_caching_related_manager(superclass, instance, field_name, related_name, cache_field_name):
+    "Creates a new manager class that has some extra (synchronizing the cache field) handling."
+    class CachingRelatedManager(superclass):
+        def add(self, *objs):
+            super(CachingRelatedManager, self).add(*objs)
+            cached_field = getattr(instance, cache_field_name)
+            cached_field.update((int(o) if isinstance(o, (int, str, unicode)) else o.pk) for o in objs)
+ 
+        def remove(self, *objs):
+            super(CachingRelatedManager, self).remove(*objs)
+            cached_field = getattr(instance, cache_field_name)
+            cached_field.symmetric_difference_update((int(o) if isinstance(o, (int, str, unicode)) else o.pk) for o in objs)
+ 
+        def clear(self):
+            super(CachingRelatedManager, self).clear()
+            cached_field = getattr(instance, cache_field_name)
+            cached_field.clear()
+            
+        @property
+        def cache(self):
+            return getattr(instance, cache_field_name)
+
+    return CachingRelatedManager
+ 
+ 
+class CachedReverseManyRelatedObjectsDescriptor(ReverseManyRelatedObjectsDescriptor):
+    def __init__(self, field, cache_field_name):
+        super(CachedReverseManyRelatedObjectsDescriptor, self).__init__(field)
+        self.cache_field_name = cache_field_name
+
+    def __get__(self, instance, cls=None):
+        manager = super(CachedReverseManyRelatedObjectsDescriptor, self).__get__(instance, cls)
+ 
+        CachingRelatedManager = get_caching_related_manager(manager.__class__,
+                                                            instance,
+                                                            self.field.name,
+                                                            self.field.rel.related_name,
+                                                            self.cache_field_name)
+ 
+        manager.__class__ = CachingRelatedManager
+        return manager
+    
+class CachedManyToManyField(models.ManyToManyField):
+    """
+    This field will add a primitive ID cache in the model that can be accessed
+    via fieldname.cache (preferably) or fieldname_cache. The cache is a
+    :class:`SetField`.
+    """
+    def contribute_to_class(self, cls, name):
+        super(CachedManyToManyField, self).contribute_to_class(cls, name)
+        cache_field_name = name + CACHE_FIELD_POSTFIX
+        set_field = SetField()
+        set_field.contribute_to_class(cls, cache_field_name)
+        setattr(cls, name, CachedReverseManyRelatedObjectsDescriptor(self, cache_field_name))

dcfields/inheritedfield.py

+import logging
+from types import TupleType
+logger = logging.getLogger(__name__)
+
+from django.core.exceptions import ObjectDoesNotExist
+from django.db.models import Model, Field, BooleanField, Manager, ManyToManyField
+from django.db.models.query import QuerySet
+from django.db.models.fields.related import RelatedField, add_lazy_relation, \
+                        ReverseManyRelatedObjectsDescriptor, ManyToManyField, \
+                        ForeignKey
+from django.db.models.base import ModelBase
+from django.db.models import signals
+
+from django.utils.functional import curry
+
+import copy
+from collections import defaultdict
+
+INHERITED_FLAG_FIELD_NAME = "%s_is_inherited"
+INHERITED_VALUE_FIELD_NAME = "%s_inherited_value"
+LOCAL_VALUE_FIELD_NAME = "%s_local_value"
+
+__all__ = (
+    'INHERITED_FLAG_FIELD_NAME', 'INHERITED_VALUE_FIELD_NAME',
+    'LOCAL_VALUE_FIELD_NAME', 'InheritedOnlyException',
+    'InheritedField', 'find_in_parent', 'find_on_model'
+)
+
+def log(class_name, msg, *args, **kwargs):
+    if class_name in ('TestModelChild', 'Program', 'Producer', 'TestModelParent'):
+        logger.debug(msg, *args, **kwargs)
+
+class InheritedOnlyException(Exception):
+    pass
+
+class InheritedField(Field):
+    """
+    This field will:
+
+        - check if the model refered by ``parent_name`` contains ``field_name``
+          and raise according exceptions if `validate=True` (default).
+        - copy the field from the parent, or the parent's parent if the field in
+          the parent has is an InheritedField too if `inherit_only=False`
+          (default)
+        - create a boolean flag ``is_<fieldname>_inherited`` in the current
+          model.
+
+    This field implements a descriptor interface so it will work like a property
+    with getters and setters.
+
+        - get will return the value from the parent if
+          `is_{fieldname}_inherited` is `True`
+        - set will raise `InheritedOnlyException` if the field is `inherit_only`
+        - set will save the value in `{fieldname}_value` and set the
+          `is_{fieldname}_inherited` flag accordingly
+    """
+    def __init__(self, parent_name, field_name=None, inherit_only=False, validate=True, sync=False):
+        super(InheritedField, self).__init__()
+
+        self.parent_object_field_name = parent_name
+        self.inherited_field_name_in_parent = field_name
+        self.inherit_only = inherit_only
+        self.validate = validate
+        self.sync = sync
+
+    def get_field_display(self, instance, name):
+        if self.inherit_only or getattr(instance, self.inherited_flag_name):
+            if self.sync:
+                value = getattr(instance, INHERITED_VALUE_FIELD_NAME % name, None)
+            else:
+                rel = getattr(instance, self.parent_object_field_name)
+                value = getattr(rel, self.inherited_field_name_in_parent or name)
+            return u"%s *Inherited" % value
+        else:
+            return getattr(instance, LOCAL_VALUE_FIELD_NAME % name)
+
+    def contribute_to_class(self, cls, name):
+        self.name = self.attname = name
+        cls._meta.add_virtual_field(self)
+
+        self.inherited_flag_name = INHERITED_FLAG_FIELD_NAME % name
+        self.inherited_value_field_name = INHERITED_VALUE_FIELD_NAME % name
+        self.local_value_field_name = LOCAL_VALUE_FIELD_NAME % name
+
+        if not self.inherit_only:
+            flag_field = BooleanField(default=True)
+            flag_field.creation_counter = self.creation_counter
+
+            # Adjust the creation_counter
+            # cls.add_to_class(self.inherited_flag_name, flag_field)
+            flag_field.contribute_to_class(cls, self.inherited_flag_name)
+
+            signals.class_prepared.connect(
+                curry(self.add_value_field, name=name),
+                sender=cls,
+                weak=False
+            )
+
+        setattr(cls, name, self)
+        display_name = 'get_%s_display' % name
+        setattr(cls, display_name, curry(self.get_field_display, name=name))
+        getattr(cls, display_name).__dict__['short_description'] = name.replace('_', ' ')
+
+        if not hasattr(cls, 'FIELD_INHERITANCE_MAP'):
+            cls.FIELD_INHERITANCE_MAP = {}
+
+        cls.FIELD_INHERITANCE_MAP[name] = (self.parent_object_field_name, self.inherited_field_name_in_parent or name, self.sync)
+        if not self.sync:
+            signals.class_prepared.connect(self.patch_manager, sender=cls)
+
+    def patch_manager(self, sender, **kwargs):
+        if not hasattr(sender.objects, 'original_get_query_set'):
+            _get_query_set = sender.objects.get_query_set
+            def get_query_set(qs):
+                model = qs.model
+
+                if hasattr(model, 'FIELD_INHERITANCE_REL'):
+                    related = model.FIELD_INHERITANCE_REL
+                else:
+                    related = set()
+                    for field, (parent, target_field, sync) in model.FIELD_INHERITANCE_MAP.iteritems():
+                        if not sync:
+                            chain = []
+                            find_in_parent(None, model, parent, target_field, validate=False, chain=chain)
+                            related.add('__'.join(chain))
+                    model.FIELD_INHERITANCE_REL = related
+
+                return _get_query_set().select_related(*related)
+
+            sender.objects.original_get_query_set = _get_query_set
+            sender.objects.get_query_set = get_query_set.__get__(sender.objects)
+            logger.debug("Patching %s's get_query_set method to slap a select_related on the returned qs.", sender.__name__)
+
+    def pre_save_hook(self, sender, **kwargs):
+        logger.debug("Entering pre_save_hook")
+        logger.debug(sender.REVERSE_FIELD_INHERITANCE_MAP)
+        for name in sender.REVERSE_FIELD_INHERITANCE_MAP:
+            logger.debug("Inheritance map: %s", name)
+        logger.debug("Exitting pre_save_hook")
+
+    def add_value_field(self, sender, name=None, robust=True, **kwargs):
+        def contribute(cls, field):
+            if not hasattr(cls, 'REVERSE_FIELD_INHERITANCE_MAP'): #TODO: test
+                cls.REVERSE_FIELD_INHERITANCE_MAP = {}
+
+            cls.REVERSE_FIELD_INHERITANCE_MAP[self.inherited_field_name_in_parent or name] = sender
+            if self.sync:
+                signals.pre_save.connect(self.pre_save_hook, sender=cls)
+
+            if isinstance(field, ReverseManyRelatedObjectsDescriptor):
+                field = field.field
+
+            logger.debug(
+                "Cloning field %s for class %s",
+                field, sender.__name__
+            )
+
+            # local value field
+            xfield = copy.deepcopy(field)
+            xfield.blank = True
+            if isinstance(xfield, (ManyToManyField, ForeignKey)):
+                xfield.rel.through = None
+                xfield.rel.related_name = '%s_inherited_set' % cls._meta.object_name
+
+            xfield.creation_counter = self.creation_counter
+            xfield.contribute_to_class(sender, INHERITED_VALUE_FIELD_NAME % name)
+
+            # inherited value field
+            yfield = copy.deepcopy(field)
+            yfield.blank = True
+            if isinstance(yfield, (ManyToManyField, ForeignKey)):
+                yfield.rel.through = None
+                yfield.rel.related_name = '%s_local_set' % cls._meta.object_name
+
+            yfield.creation_counter = self.creation_counter
+            yfield.contribute_to_class(sender, LOCAL_VALUE_FIELD_NAME % name)
+
+        result = find_in_parent(
+            None, sender,
+            self.parent_object_field_name,
+            self.inherited_field_name_in_parent or name,
+            robust and self.validate,
+            callback=contribute
+        )
+        if result:
+            parent, value_field = result
+            contribute(parent, value_field)
+
+    def __get__(self, instance, instance_type=None):
+        if self.inherit_only or getattr(instance, self.inherited_flag_name):
+            if self.sync:
+                return getattr(instance, self.inherited_value_field_name, None)
+            else:
+                rel = getattr(instance, self.parent_object_field_name)
+                if rel:
+                    return getattr(rel, self.inherited_field_name_in_parent or self.name)
+        return getattr(instance, self.local_value_field_name, None)
+
+    def __set__(self, instance, value):
+        if self.inherit_only:
+            raise InheritedOnlyException(
+                "Can't set value for field %s on %s (field is inherit_only). Try to set it on %s.%s." %
+                (self.name, instance, self.parent_object_field_name, self.inherited_field_name_in_parent or self.name))
+        try:
+            rel = getattr(instance, self.parent_object_field_name)
+            if rel:
+                parent_value = getattr(rel, self.inherited_field_name_in_parent or self.name)
+                setattr(instance, self.inherited_flag_name, value == parent_value)
+            else:
+                setattr(instance, self.inherited_flag_name, False)
+        except ObjectDoesNotExist:
+            setattr(instance, self.inherited_flag_name, False)
+        setattr(instance, self.local_value_field_name, value)
+
+def find_on_model(origin, model, field_name, validate=True, callback=None, chain=None):
+    target_fields = [
+        target for target in model._meta.fields
+            if target.name == field_name
+    ]
+
+    if target_fields:
+        return target_fields[0]
+
+    if hasattr(model, field_name):
+        return getattr(model, field_name)
+
+    if hasattr(model, 'FIELD_INHERITANCE_MAP'):
+        MAP = getattr(model, 'FIELD_INHERITANCE_MAP')
+        if field_name in MAP:
+            rel, field, sync = MAP[field_name]
+            return find_in_parent(origin, model, rel, field, validate, callback, chain)
+        elif validate:
+            raise TypeError("InheritedField: %s does not exist in %s." %
+                                (field_name, model))
+
+
+    if validate:
+        raise TypeError("InheritedField: %s does not exist in %s." %
+                                    (field_name, model))
+
+def find_in_parent(origin, model_class, relation_name, field_name, validate=True, callback=None, chain=None):
+    """
+    This function will take a model class and search for the relation so that:
+
+        - if `validate` is `True` will raise `TypeError`'s if the field isn't
+          found in the parent.
+        - will call `callback` if there is something found
+
+    Note that this will not always return the field instance as the field may be
+    on a uninstantiated model class. Use `callback` to do stuff with the field.
+    """
+    for ifield in model_class._meta.fields:
+        if ifield.name == relation_name:
+            if not chain is None:
+                chain.append(relation_name)
+            if isinstance(ifield, RelatedField):
+                if isinstance(ifield.rel.to, basestring):
+                    # the model class isn't instantiated yet so we need to add a
+                    # hook
+                    def resolve_related_class(xfield, xmodel, xcls):
+                        xfield.rel.to = xmodel
+                        field_instance = find_on_model(origin or xmodel, xmodel, field_name, validate, callback, chain)
+                        callback(origin or xmodel, field_instance)
+
+                    add_lazy_relation(model_class, ifield, ifield.rel.to,
+                                        resolve_related_class)
+                    return
+                else:
+                    return (ifield.rel.to, find_on_model(origin or ifield.rel.to, ifield.rel.to, field_name, validate, callback, chain))
+            else:
+                if validate:
+                    raise TypeError(
+                        "InheritedField: %s is a %s instead of a RelatedField."%
+                        (relation_name, type(ifield)))
+                else:
+                    return
+    if validate:
+        raise TypeError("InheritedField: %s does not exist on %s." %
+                        (relation_name, model_class))
+
+
+class InheritedFieldQuerySet(QuerySet):
+    def is_inherited(self, parts):
+        _parts = parts[:]
+        last_field_name, model = self.traverse_models(_parts, self.model)
+        return hasattr(model, 'FIELD_INHERITANCE_MAP') and \
+               last_field_name in model.FIELD_INHERITANCE_MAP
+
+    def traverse_models(self, parts, model):
+        next_part = parts.pop(0)
+        if(parts):
+            next_model = self.model._meta.get_field(next_part).rel.to
+            return self.traverse_models(parts, next_model)
+        return (next_part, model)
+
+    def split_field(self, field):
+        parts = field.split(LOOKUP_SEP)
+        lookup = None
+        if parts[-1] in QUERY_TERMS:
+            lookup = parts.pop(-1)
+        return (parts,lookup)
+        
+    def patch_child(self, parts, lookup, value):
+        inherited_flag = ["is_%s_inherited" % parts[-1]]
+        inherited_value = ["%s_value" % parts[-1]]
+        lookup = [lookup] if lookup else []
+        is_inherited = LOOKUP_SEP.join(parts[:-1] + inherited_flag)
+        field = LOOKUP_SEP.join(parts[:-1] + inherited_value + lookup)
+        print is_inherited
+        print field
+        return Q(**{is_inherited: False, field: value})
+
+    def patch_parent(self, parts, lookup, value):
+        _parts = parts[:]
+        last_field_name, model = self.traverse_models(_parts, self.model)
+        lookup = [lookup] if lookup else []
+        parents_name, parents_field_name = model.FIELD_INHERITANCE_MAP[last_field_name]
+        parent_lookup = LOOKUP_SEP.join(parts[:-1] + [parents_name] + [parents_field_name] + lookup)
+        print parent_lookup
+        return Q(**{parent_lookup: value})
+
+    def patch(self, parts, lookup, value):
+        child = self.patch_child(parts, lookup, value)
+        parent = self.patch_parent(parts, lookup, value)
+        return child | parent
+
+    def filter(self, *args, **kwargs):
+        # We don't support Q objects, bail out if any
+        assert not args
+        args = []
+        old_kwargs = kwargs.copy()
+        for field, value in kwargs.items():
+            parts, lookup = self.split_field(field)
+            if self.is_inherited(parts):
+                old_kwargs.pop(field)
+                args.append(self.patch(parts, lookup, value))
+        return super(InheritedFieldQuerySet, self).filter(*args, **old_kwargs)
+
+class InheritedFieldManager(Manager):
+    def get_query_set(self):
+        return InheritedFieldQuerySet(self.model, using=self._db)
+#!python
+"""Bootstrap setuptools installation
+
+If you want to use setuptools in your package's setup.py, just include this
+file in the same directory with it, and add this to the top of your setup.py::
+
+    from ez_setup import use_setuptools
+    use_setuptools()
+
+If you want to require a specific version of setuptools, set a download
+mirror, or use an alternate download directory, you can do so by supplying
+the appropriate options to ``use_setuptools()``.
+
+This file can also be run as a script to install or upgrade setuptools.
+"""
+import sys
+DEFAULT_VERSION = "0.6c9"
+DEFAULT_URL     = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
+
+md5_data = {
+    'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
+    'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
+    'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
+    'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
+    'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
+    'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
+    'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
+    'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
+    'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
+    'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
+    'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
+    'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
+    'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
+    'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
+    'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
+    'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
+    'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
+    'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
+    'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
+    'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
+    'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
+    'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
+    'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
+    'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
+    'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
+    'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
+    'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
+    'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
+    'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
+    'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
+    'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
+    'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
+    'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
+    'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
+}
+
+import sys, os
+try: from hashlib import md5
+except ImportError: from md5 import md5
+
+def _validate_md5(egg_name, data):
+    if egg_name in md5_data:
+        digest = md5(data).hexdigest()
+        if digest != md5_data[egg_name]:
+            print >>sys.stderr, (
+                "md5 validation of %s failed!  (Possible download problem?)"
+                % egg_name
+            )
+            sys.exit(2)
+    return data
+
+def use_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    download_delay=15
+):
+    """Automatically find/download setuptools and make it available on sys.path
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end with
+    a '/').  `to_dir` is the directory where setuptools will be downloaded, if
+    it is not already available.  If `download_delay` is specified, it should
+    be the number of seconds that will be paused before initiating a download,
+    should one be required.  If an older version of setuptools is installed,
+    this routine will print a message to ``sys.stderr`` and raise SystemExit in
+    an attempt to abort the calling script.
+    """
+    was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
+    def do_download():
+        egg = download_setuptools(version, download_base, to_dir, download_delay)
+        sys.path.insert(0, egg)
+        import setuptools; setuptools.bootstrap_install_from = egg
+    try:
+        import pkg_resources
+    except ImportError:
+        return do_download()       
+    try:
+        pkg_resources.require("setuptools>="+version); return
+    except pkg_resources.VersionConflict, e:
+        if was_imported:
+            print >>sys.stderr, (
+            "The required version of setuptools (>=%s) is not available, and\n"
+            "can't be installed while this script is running. Please install\n"
+            " a more recent version first, using 'easy_install -U setuptools'."
+            "\n\n(Currently using %r)"
+            ) % (version, e.args[0])
+            sys.exit(2)
+        else:
+            del pkg_resources, sys.modules['pkg_resources']    # reload ok
+            return do_download()
+    except pkg_resources.DistributionNotFound:
+        return do_download()
+
+def download_setuptools(
+    version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
+    delay = 15
+):
+    """Download setuptools from a specified location and return its filename
+
+    `version` should be a valid setuptools version number that is available
+    as an egg for download under the `download_base` URL (which should end
+    with a '/'). `to_dir` is the directory where the egg will be downloaded.
+    `delay` is the number of seconds to pause before an actual download attempt.
+    """
+    import urllib2, shutil
+    egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
+    url = download_base + egg_name
+    saveto = os.path.join(to_dir, egg_name)
+    src = dst = None
+    if not os.path.exists(saveto):  # Avoid repeated downloads
+        try:
+            from distutils import log
+            if delay:
+                log.warn("""
+---------------------------------------------------------------------------
+This script requires setuptools version %s to run (even to display
+help).  I will attempt to download it for you (from
+%s), but
+you may need to enable firewall access for this script first.
+I will start the download in %d seconds.
+
+(Note: if this machine does not have network access, please obtain the file
+
+   %s
+
+and place it in this directory before rerunning this script.)
+---------------------------------------------------------------------------""",
+                    version, download_base, delay, url
+                ); from time import sleep; sleep(delay)
+            log.warn("Downloading %s", url)
+            src = urllib2.urlopen(url)
+            # Read/write all in one block, so we don't create a corrupt file
+            # if the download is interrupted.
+            data = _validate_md5(egg_name, src.read())
+            dst = open(saveto,"wb"); dst.write(data)
+        finally:
+            if src: src.close()
+            if dst: dst.close()
+    return os.path.realpath(saveto)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+def main(argv, version=DEFAULT_VERSION):
+    """Install or upgrade setuptools and EasyInstall"""
+    try:
+        import setuptools
+    except ImportError:
+        egg = None
+        try:
+            egg = download_setuptools(version, delay=0)
+            sys.path.insert(0,egg)
+            from setuptools.command.easy_install import main
+            return main(list(argv)+[egg])   # we're done here
+        finally:
+            if egg and os.path.exists(egg):
+                os.unlink(egg)
+    else:
+        if setuptools.__version__ == '0.0.1':
+            print >>sys.stderr, (
+            "You have an obsolete version of setuptools installed.  Please\n"
+            "remove it from your system entirely before rerunning this script."
+            )
+            sys.exit(2)
+
+    req = "setuptools>="+version
+    import pkg_resources
+    try:
+        pkg_resources.require(req)
+    except pkg_resources.VersionConflict:
+        try:
+            from setuptools.command.easy_install import main
+        except ImportError:
+            from easy_install import main
+        main(list(argv)+[download_setuptools(delay=0)])
+        sys.exit(0) # try to force an exit
+    else:
+        if argv:
+            from setuptools.command.easy_install import main
+            main(argv)
+        else:
+            print "Setuptools version",version,"or greater has been installed."
+            print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
+
+def update_md5(filenames):
+    """Update our built-in md5 registry"""
+
+    import re
+
+    for name in filenames:
+        base = os.path.basename(name)
+        f = open(name,'rb')
+        md5_data[base] = md5(f.read()).hexdigest()
+        f.close()
+
+    data = ["    %r: %r,\n" % it for it in md5_data.items()]
+    data.sort()
+    repl = "".join(data)
+
+    import inspect
+    srcfile = inspect.getsourcefile(sys.modules[__name__])
+    f = open(srcfile, 'rb'); src = f.read(); f.close()
+
+    match = re.search("\nmd5_data = {\n([^}]+)}", src)
+    if not match:
+        print >>sys.stderr, "Internal error!"
+        sys.exit(2)
+
+    src = src[:match.start(1)] + repl + src[match.end(1):]
+    f = open(srcfile,'w')
+    f.write(src)
+    f.close()
+
+
+if __name__=='__main__':
+    if len(sys.argv)>2 and sys.argv[1]=='--md5update':
+        update_md5(sys.argv[2:])
+    else:
+        main(sys.argv[1:])
+
+
+
+
+
+
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+try:
+    from setuptools import setup, find_packages
+except ImportError:
+    import ez_setup
+    ez_setup.use_setuptools()
+    from setuptools import setup, find_packages
+    
+import os
+
+setup(
+    name = "django-customfields",
+    version = "0.1.0",
+    url = '',
+    download_url = '',
+    license = 'BSD',
+    description = "",
+    author = '',
+    author_email = '',
+    packages = find_packages(),
+    include_package_data = True,
+    zip_safe = False,
+    classifiers = [
+        'Development Status :: 3 - Alpha',
+        'Framework :: Django',
+        'Intended Audience :: Developers',
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        'Topic :: Internet :: WWW/HTTP',
+    ]
+)

tests/bootstrap.py

+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+try:
+    import pkg_resources
+except ImportError:
+    ez = {}
+    exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+                         ).read() in ez
+    ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+    import pkg_resources
+
+if sys.platform == 'win32':
+    def quote(c):
+        if ' ' in c:
+            return '"%s"' % c # work around spawn lamosity on windows
+        else:
+            return c
+else:
+    def quote (c):
+        return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws  = pkg_resources.working_set
+
+if len(sys.argv) > 2 and sys.argv[1] == '--version':
+    VERSION = ' == %s' % sys.argv[2]
+    args = sys.argv[3:] + ['bootstrap']
+else:
+    VERSION = ''
+    args = sys.argv[1:] + ['bootstrap']
+
+if is_jython:
+    import subprocess
+
+    assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+           quote(tmpeggs), 'zc.buildout' + VERSION],
+           env=dict(os.environ,
+               PYTHONPATH=
+               ws.find(pkg_resources.Requirement.parse('setuptools')).location
+               ),
+           ).wait() == 0
+
+else:
+    assert os.spawnle(
+        os.P_WAIT, sys.executable, quote (sys.executable),
+        '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION,
+        dict(os.environ,
+            PYTHONPATH=
+            ws.find(pkg_resources.Requirement.parse('setuptools')).location
+            ),
+        ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout' + VERSION)
+import zc.buildout.buildout
+zc.buildout.buildout.main(args)
+shutil.rmtree(tmpeggs)

tests/buildout.cfg

+[buildout]
+parts = django-1.1 django-1.0
+develop = ..
+eggs = django-piston
+
+[django-1.1]
+recipe = djangorecipe
+version = 1.1
+project = test_project
+settings = settings
+test = testapp
+eggs = ${buildout:eggs}
+testrunner = test-1.1
+
+[django-1.0]
+recipe = djangorecipe
+version = 1.0.2
+project = test_project
+settings = settings
+test = testapp
+eggs = ${buildout:eggs}
+testrunner = test-1.0

tests/test_project/__init__.py

Empty file added.

tests/test_project/apps/__init__.py

Empty file added.

tests/test_project/apps/testapp/__init__.py

Empty file added.

tests/test_project/apps/testapp/models.py

+from dcfields import cachedmtmfield
+from django.db import models
+
+class Stuff(models.Model):
+    pass
+
+class TestModel1(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = models.ManyToManyField(Stuff)
+
+class TestModel1_Cached(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = cachedmtmfield.CachedManyToManyField(Stuff)
+
+class TestModelA(models.Model): # used to test cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelB(models.Model): # used to thest cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelC(models.Model): # used to thest cached many to many field
+    cmtm_a = cachedmtmfield.CachedManyToManyField(TestModelA)
+    cmtm_b = cachedmtmfield.CachedManyToManyField(TestModelB)
+    
+from dcfields.inheritedfield import InheritedField
+
+class TestModelParent(models.Model):
+    title = models.CharField(max_length=255)
+
+class TestModelChild(models.Model):
+    parent = models.ForeignKey(TestModelParent)
+    title = InheritedField('parent', sync=True)
+
+    
+##############################333
+
+
+class Stuff(models.Model):
+    pass
+
+class TestModel1(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = models.ManyToManyField(Stuff)
+
+class TestModel1_Cached(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = cachedmtmfield.CachedManyToManyField(Stuff)
+
+class TestModel2(models.Model):
+    parent = models.ForeignKey(TestModel1, null=True)
+
+    bar = models.CharField(max_length=10)
+    foo = inheritedfield.InheritedField('parent', 'bar', validate=False)
+    ifoo = inheritedfield.InheritedField('parent', 'bar', inherit_only=True)
+
+class TestModel6(models.Model): #used in test_model_field_double_inheritance
+    parent_for_6 = models.ForeignKey(TestModel1)
+    foo = inheritedfield.InheritedField('parent_for_6', 'bar')
+class TestModel7(models.Model): #used in test_model_field_double_inheritance
+    parent_for_7 = models.ForeignKey(TestModel6)
+    bogus_relation = models.ForeignKey(TestModel6, related_name="bogus", null=True)
+    boo = inheritedfield.InheritedField('parent_for_7', 'foo')
+class TestModel8(models.Model): #used in test_model_field_double_inheritance
+    parent_for_8 = models.ForeignKey(TestModel7)
+    goo = inheritedfield.InheritedField('parent_for_8', 'boo')
+
+class TestModelA(models.Model): # used to test cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelB(models.Model): # used to thest cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelC(models.Model): # used to thest cached many to many field
+    cmtm_a = cachedmtmfield.CachedManyToManyField(TestModelA)
+    cmtm_b = cachedmtmfield.CachedManyToManyField(TestModelB)

tests/test_project/apps/testapp/tests.py

+from django.test import TestCase
+
+from models import *
+
+class CachedManyToManyTests(TestCase):
+    def test_runtime_model(self):
+        d = TestModelC()
+        self.assertEquals(d.cmtm_a_cache, set())
+        self.assertEquals(d.cmtm_b_cache, set())
+
+    def test_cache_field(self):
+        c = TestModelC()
+        assert hasattr(c, 'cmtm_a_cache')
+        assert hasattr(c, 'cmtm_b_cache')
+
+    def test_empty(self):
+        c = TestModelC()
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_empty_save_load(self):
+        c = TestModelC()
+        c.save()
+        pk = c.pk
+        c = TestModelC.objects.get(pk=pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_simple_sync(self):
+        a = TestModelA()
+        a.save()
+        b = TestModelB()
+        b.save()
+        c = TestModelC()
+        c.save() #muste save before addign mtm relations
+        c.cmtm_a.add(a)
+        c.cmtm_b.add(b)
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+        c.save() #save and reload the object
+        c = TestModelC.objects.all()[0]
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+
+    def test_multiple_sync(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c.cmtm_a.add(*objs)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_save_load_sequence(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c_pk = c.pk
+        c.cmtm_a.add(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_set_field(self):
+        sf = cachedmtmfield.SetField()
+
+    def test_lookup(self):
+        c = TestModelC()
+        c.save()
+        self.assertRaises(TypeError, lambda: TestModelC.objects.filter(cmtm_a_cache=1))
+
+
+from django.test import TestCase
+
+from models import *
+
+class InheritedFieldSyncFlagTest(TestCase):
+    def setUp(self):
+        self.parent = TestModelParent.objects.create(title='Parent')
+        self.child = TestModelChild.objects.create(parent=self.parent)
+
+    def test_inheritance_simple(self):
+        self.assertEquals(self.parent.title, 'Parent')
+        self.assertEquals(self.child.title, 'Parent')
+
+    def test_local_value(self):
+        self.child.title = 'Child'
+        self.assertEquals(self.child.title_inherited, False)
+        self.assertEquals(self.child.title, 'Child')
+        self.assertEquals(self.parent.title, 'Parent')
+
+    def test_parent_update_inherited(self):
+        self.parent.title = 'Parent 1'
+        self.assertEquals(self.parent.title, 'Parent 1')
+        self.assertEquals(self.child.title, 'Parent 1')
+
+    def test_parent_update_not_inherited(self):
+        self.child.title = 'Child'
+        self.parent.title = 'Parent 1'
+
+        self.assertEquals(self.parent.title, 'Parent 1')
+        self.assertEquals(self.child.title, 'Child')
+        self.assertEquals(self.child.title_inherited, False)
+
+    def test_restore_inherited_flag(self):
+        self.child.title = 'Child'
+        self.parent.title = 'Parent 1'
+        self.child.title_inherited = True
+        self.assertEquals(self.child.title, 'Parent 1')
+
+
+
+##########################################
+
+
+class Stuff(models.Model):
+    pass
+
+class TestModel1(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = models.ManyToManyField(Stuff)
+
+class TestModel1_Cached(models.Model):
+    bar = models.CharField(max_length=10)
+    m2mrel = cachedmtmfield.CachedManyToManyField(Stuff)
+
+class TestModel2(models.Model):
+    parent = models.ForeignKey(TestModel1, null=True)
+
+    bar = models.CharField(max_length=10)
+    foo = inheritedfield.InheritedField('parent', 'bar', validate=False)
+    ifoo = inheritedfield.InheritedField('parent', 'bar', inherit_only=True)
+
+class TestModel6(models.Model): #used in test_model_field_double_inheritance
+    parent_for_6 = models.ForeignKey(TestModel1)
+    foo = inheritedfield.InheritedField('parent_for_6', 'bar')
+class TestModel7(models.Model): #used in test_model_field_double_inheritance
+    parent_for_7 = models.ForeignKey(TestModel6)
+    bogus_relation = models.ForeignKey(TestModel6, related_name="bogus", null=True)
+    boo = inheritedfield.InheritedField('parent_for_7', 'foo')
+class TestModel8(models.Model): #used in test_model_field_double_inheritance
+    parent_for_8 = models.ForeignKey(TestModel7)
+    goo = inheritedfield.InheritedField('parent_for_8', 'boo')
+
+class InheritedFieldTests(TestCase):
+    def test_model_field_inheritance_broken_parent(self):
+        b = TestModel2()
+        b.save()
+        b.foo = 'abc'
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_local_value, 'abc')
+        self.assertEquals(b.foo_is_inherited, False)
+
+        # this tests handling for broken FKs
+        b = TestModel2()
+        b.parent_id = 99999
+        b.foo = 'abc'
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_local_value, 'abc')
+        self.assertEquals(b.foo_is_inherited, False)
+
+    def test_model_field_inheritance_simple(self):
+        a = TestModel1(bar="123")
+        a.save()
+
+        b = TestModel2(parent=a)
+        b.save()
+
+        self.assertEquals(a.bar, '123')
+        self.assertEquals(b.foo, '123')
+        self.assertEquals(b.foo_is_inherited, True)
+
+        b.foo = 'abc'
+        self.assertEquals(a.bar, '123')
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_local_value, 'abc')
+        self.assertEquals(b.foo_is_inherited, False)
+
+        self.assertEquals(b.ifoo, '123')
+        a.bar = 'qwe'
+        self.assertEquals(b.ifoo, 'qwe')
+
+        from dcfields.inheritedfield import InheritedOnlyException
+        try:
+            b.ifoo = 'fail'
+        except InheritedOnlyException, e:
+            self.assertEquals(e.args[0], "Can't set value for field ifoo on TestModel2 object (field is inherit_only). Try to set it on parent.bar.")
+
+        else:
+            self.fail("Didn't raise any TypeError")
+
+    def test_model_field_inheritance_silly(self):
+        class TestModel3_Valid(models.Model):
+            parent = models.ForeignKey(TestModel1)
+            foo = inheritedfield.InheritedField('parent', 'bar')
+            m2mrel = inheritedfield.InheritedField('parent')
+
+
+        class TestModel_Temp2(models.Model):
+            parent = models.ForeignKey("TestModel_Temp1")
+            m2mrel = inheritedfield.InheritedField('parent')
+        class TestModel_Temp1(models.Model):
+            m2mrel = models.ManyToManyField(Stuff)
+
+    def test_model_field_inheritance_validation(self):
+        try:
+            class TestModel4_Invalid(models.Model):
+                foo = inheritedfield.InheritedField('no_parent', 'bar')
+                parent = models.ForeignKey(TestModel1)
+
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_parent does not exist on <class 'test_project.apps.testapp.models.TestModel4_Invalid'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_parent test)")
+
+        try:
+            class TestModel5_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel1)
+                foo = inheritedfield.InheritedField('parent', 'no_bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_bar does not exist in <class 'test_project.apps.testapp.models.TestModel1'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+        try:
+            class TestModel6_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel2)
+                foo = inheritedfield.InheritedField('parent', 'bar')
+            class TestModel7_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel6_Invalid)
+                foo = inheritedfield.InheritedField('parent', 'no_bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_bar does not exist in <class 'test_project.apps.testapp.models.TestModel6_Invalid'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+    def test_parent_not_a_rel(self):
+        try:
+            class TestModel8_Invalid(models.Model):
+                parent = models.BooleanField()
+                foo = inheritedfield.InheritedField('parent', 'bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: parent is a <class 'django.db.models.fields.BooleanField'> instead of a RelatedField.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+        class TestModel9_Invalid(models.Model):
+            parent = models.BooleanField()
+            foo = inheritedfield.InheritedField('parent', 'bar', validate=False)
+        #should work :)
+
+
+    def test_select_related(self):
+        self.assertFalse(hasattr(TestModel8, "FIELD_INHERITANCE_REL"))
+        qs = TestModel8.objects.filter()
+        self.assertTrue(hasattr(TestModel8, "FIELD_INHERITANCE_REL"))
+        self.assertEquals(`qs.query.select_related`, "{'parent_for_8': {'parent_for_7': {'parent_for_6': {}}}}")
+        self.assertEquals(TestModel8.FIELD_INHERITANCE_REL, set(['parent_for_8__parent_for_7__parent_for_6']))
+        qs = TestModel2.objects.filter()
+        self.assertEquals(`qs.query.select_related`, "{'parent': {}}")
+
+    def test_model_field_double_inheritance(self):
+        a = TestModel1(bar="123")
+        a.save()
+
+        b = TestModel6(parent_for_6=a)
+        b.save()
+
+        c = TestModel7(parent_for_7=b)
+        c.save()
+
+        d = TestModel8(parent_for_8=c)
+        d.save()
+
+        self.assertEquals(b.foo, '123')
+        self.assertEquals(d.goo, '123')
+        self.assertEquals(b.foo_is_inherited, True)
+        self.assertEquals(d.goo_is_inherited, True)
+
+        c.boo = 'abc'
+        self.assertEquals(c.boo, 'abc')
+        self.assertEquals(c.boo_is_inherited, False)
+        self.assertEquals(c.get_boo_display(), "abc")
+        self.assertEquals(d.goo, 'abc')
+        self.assertEquals(d.goo_is_inherited, True)
+        self.assertEquals(d.get_goo_display(), "abc *Inherited")
+
+
+class TestModelA(models.Model): # used to test cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelB(models.Model): # used to thest cached many to many field
+    x = models.CharField(max_length=1)
+
+class TestModelC(models.Model): # used to thest cached many to many field
+    cmtm_a = cachedmtmfield.CachedManyToManyField(TestModelA)
+    cmtm_b = cachedmtmfield.CachedManyToManyField(TestModelB)
+
+class CachedManyToManyTests(TestCase):
+    #def test_that_is_not_a_test(self):
+        #reload(cachedmtmfield)
+
+    def test_inherited_field_compat(self):
+        class TestModel3_Cached_Valid(models.Model):
+            parent = models.ForeignKey(TestModel1_Cached)
+            foo = inheritedfield.InheritedField('parent', 'bar')
+            m2mrel = inheritedfield.InheritedField('parent')
+
+
+    def test_runtime_model(self):
+        class TestModelD(models.Model): # used to thest cached many to many field
+            cmtm_a = cachedmtmfield.CachedManyToManyField(TestModelA)
+            cmtm_b = cachedmtmfield.CachedManyToManyField(TestModelB)
+        d = TestModelC()
+        self.assertEquals(d.cmtm_a_cache, set())
+        self.assertEquals(d.cmtm_b_cache, set())
+
+    def test_cache_field(self):
+        c = TestModelC()
+        assert hasattr(c, 'cmtm_a_cache')
+        assert hasattr(c, 'cmtm_b_cache')
+
+    def test_empty(self):
+        c = TestModelC()
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_empty_save_load(self):
+        c = TestModelC()
+        c.save()
+        pk = c.pk
+        c = TestModelC.objects.get(pk=pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_simple_sync(self):
+        a = TestModelA()
+        a.save()
+        b = TestModelB()
+        b.save()
+        c = TestModelC()
+        c.save() #muste save before addign mtm relations
+        c.cmtm_a.add(a)
+        c.cmtm_b.add(b)
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+        c.save() #save and reload the object
+        c = TestModelC.objects.all()[0]
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+
+    def test_multiple_sync(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c.cmtm_a.add(*objs)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_save_load_sequence(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c_pk = c.pk
+        c.cmtm_a.add(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_set_field(self):
+        sf = cachedmtmfield.SetField()
+
+    def test_lookup(self):
+        c = TestModelC()
+        c.save()
+        self.assertRaises(TypeError, lambda: TestModelC.objects.filter(cmtm_a_cache=1))
+
+
+######################################################
+
+
+class InheritedFieldTests(TestCase):
+    def test_model_field_inheritance_broken_parent(self):
+        b = TestModel2()
+        b.save()
+        b.foo = 'abc'
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_value, 'abc')
+        self.assertEquals(b.is_foo_inherited, False)
+
+        # this tests handling for broken FKs
+        b = TestModel2()
+        b.parent_id = 99999
+        b.foo = 'abc'
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_value, 'abc')
+        self.assertEquals(b.is_foo_inherited, False)
+
+    def test_model_field_inheritance_simple(self):
+        a = TestModel1(bar="123")
+        a.save()
+
+        b = TestModel2(parent=a)
+        b.save()
+
+        self.assertEquals(a.bar, '123')
+        self.assertEquals(b.foo, '123')
+        self.assertEquals(b.is_foo_inherited, True)
+
+        b.foo = 'abc'
+        self.assertEquals(a.bar, '123')
+        self.assertEquals(b.foo, 'abc')
+        self.assertEquals(b.foo_value, 'abc')
+        self.assertEquals(b.is_foo_inherited, False)
+
+        self.assertEquals(b.ifoo, '123')
+        a.bar = 'qwe'
+        self.assertEquals(b.ifoo, 'qwe')
+
+        from dcfields.inheritedfield import InheritedOnlyException
+        try:
+            b.ifoo = 'fail'
+        except InheritedOnlyException, e:
+            self.assertEquals(e.args[0], "Can't set value for field ifoo on TestModel2 object (field is inherit_only). Try to set it on parent.bar.")
+
+        else:
+            self.fail("Didn't raise any TypeError")
+
+    def test_model_field_inheritance_silly(self):
+        class TestModel3_Valid(models.Model):
+            parent = models.ForeignKey(TestModel1)
+            foo = inheritedfield.InheritedField('parent', 'bar')
+            m2mrel = inheritedfield.InheritedField('parent')
+
+
+        class TestModel_Temp2(models.Model):
+            parent = models.ForeignKey("TestModel_Temp1")
+            m2mrel = inheritedfield.InheritedField('parent')
+        class TestModel_Temp1(models.Model):
+            m2mrel = models.ManyToManyField(Stuff)
+
+    def test_model_field_inheritance_validation(self):
+        try:
+            class TestModel4_Invalid(models.Model):
+                foo = inheritedfield.InheritedField('no_parent', 'bar')
+                parent = models.ForeignKey(TestModel1)
+
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_parent does not exist on <class 'test_project.apps.testapp.models.TestModel4_Invalid'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_parent test)")
+
+        try:
+            class TestModel5_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel1)
+                foo = inheritedfield.InheritedField('parent', 'no_bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_bar does not exist in <class 'test_project.apps.testapp.models.TestModel1'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+        try:
+            class TestModel6_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel2)
+                foo = inheritedfield.InheritedField('parent', 'bar')
+            class TestModel7_Invalid(models.Model):
+                parent = models.ForeignKey(TestModel6_Invalid)
+                foo = inheritedfield.InheritedField('parent', 'no_bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: no_bar does not exist in <class 'test_project.apps.testapp.models.TestModel6_Invalid'>.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+    def test_parent_not_a_rel(self):
+        try:
+            class TestModel8_Invalid(models.Model):
+                parent = models.BooleanField()
+                foo = inheritedfield.InheritedField('parent', 'bar')
+        except TypeError, e:
+            self.assertEquals(e.args[0], "InheritedField: parent is a <class 'django.db.models.fields.BooleanField'> instead of a RelatedField.")
+        else:
+            self.fail("Didn't raise any TypeError (no_bar (target field) test)")
+
+        class TestModel9_Invalid(models.Model):
+            parent = models.BooleanField()
+            foo = inheritedfield.InheritedField('parent', 'bar', validate=False)
+        #should work :)
+
+
+    def test_select_related(self):
+        self.assertFalse(hasattr(TestModel8, "FIELD_INHERITANCE_REL"))
+        qs = TestModel8.objects.filter()
+        self.assertTrue(hasattr(TestModel8, "FIELD_INHERITANCE_REL"))
+        self.assertEquals(`qs.query.select_related`, "{'parent_for_8': {'parent_for_7': {'parent_for_6': {}}}}")
+        self.assertEquals(TestModel8.FIELD_INHERITANCE_REL, set(['parent_for_8__parent_for_7__parent_for_6']))
+        qs = TestModel2.objects.filter()
+        self.assertEquals(`qs.query.select_related`, "{'parent': {}}")
+
+    def test_model_field_double_inheritance(self):
+        a = TestModel1(bar="123")
+        a.save()
+
+        b = TestModel6(parent_for_6=a)
+        b.save()
+
+        c = TestModel7(parent_for_7=b)
+        c.save()
+
+        d = TestModel8(parent_for_8=c)
+        d.save()
+
+        self.assertEquals(b.foo, '123')
+        self.assertEquals(d.goo, '123')
+        self.assertEquals(b.is_foo_inherited, True)
+        self.assertEquals(d.is_goo_inherited, True)
+
+        c.boo = 'abc'
+        self.assertEquals(c.boo, 'abc')
+        self.assertEquals(c.is_boo_inherited, False)
+        self.assertEquals(c.get_boo_display(), "abc")
+        self.assertEquals(d.goo, 'abc')
+        self.assertEquals(d.is_goo_inherited, True)
+        self.assertEquals(d.get_goo_display(), "abc *Inherited")
+
+class CachedManyToManyTests(TestCase):
+    #def test_that_is_not_a_test(self):
+        #reload(cachedmtmfield)
+
+    def test_inherited_field_compat(self):
+        class TestModel3_Cached_Valid(models.Model):
+            parent = models.ForeignKey(TestModel1_Cached)
+            foo = inheritedfield.InheritedField('parent', 'bar')
+            m2mrel = inheritedfield.InheritedField('parent')
+
+
+    def test_runtime_model(self):
+        class TestModelD(models.Model): # used to thest cached many to many field
+            cmtm_a = cachedmtmfield.CachedManyToManyField(TestModelA)
+            cmtm_b = cachedmtmfield.CachedManyToManyField(TestModelB)
+        d = TestModelC()
+        self.assertEquals(d.cmtm_a_cache, set())
+        self.assertEquals(d.cmtm_b_cache, set())
+
+    def test_cache_field(self):
+        c = TestModelC()
+        assert hasattr(c, 'cmtm_a_cache')
+        assert hasattr(c, 'cmtm_b_cache')
+
+    def test_empty(self):
+        c = TestModelC()
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_empty_save_load(self):
+        c = TestModelC()
+        c.save()
+        pk = c.pk
+        c = TestModelC.objects.get(pk=pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+        self.assertEquals(c.cmtm_b_cache, set())
+
+    def test_simple_sync(self):
+        a = TestModelA()
+        a.save()
+        b = TestModelB()
+        b.save()
+        c = TestModelC()
+        c.save() #muste save before addign mtm relations
+        c.cmtm_a.add(a)
+        c.cmtm_b.add(b)
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+        c.save() #save and reload the object
+        c = TestModelC.objects.all()[0]
+        self.assertEquals(c.cmtm_a_cache, set([a.pk]))
+        self.assertEquals(c.cmtm_b_cache, set([b.pk]))
+
+    def test_multiple_sync(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c.cmtm_a.add(*objs)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_save_load_sequence(self):
+        objs = [TestModelA() for x in range(5)]
+        for o in objs:
+            o.save()
+        objs_pks = [obj.pk for obj in objs]
+        c = TestModelC()
+        c.save() #muste save before adding mtm relations
+        c_pk = c.pk
+        c.cmtm_a.add(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(objs_pks))
+        new_obj = TestModelA()
+        new_obj.save()
+        new_objs_pks = objs_pks + [new_obj.pk]
+        c.cmtm_a.add(new_obj)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set(new_objs_pks))
+        c.cmtm_a.remove(*objs)
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set([new_obj.pk]))
+        c.cmtm_a.clear()
+        c.save()
+        c = TestModelC.objects.get(pk=c_pk)
+        self.assertEquals(c.cmtm_a_cache, set())
+
+    def test_set_field(self):
+        sf = cachedmtmfield.SetField()
+
+    def test_lookup(self):
+        c = TestModelC()
+        c.save()
+        self.assertRaises(TypeError, lambda: TestModelC.objects.filter(cmtm_a_cache=1))
+        
+        

tests/test_project/apps/testapp/urls.py

+# -*- coding: utf-8 -*-
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('')
+
+

tests/test_project/settings.py

+# -*- coding: utf-8 -*-
+import os
+DEBUG = True
+DATABASE_ENGINE = 'sqlite3'
+DATABASE_NAME = '/tmp/djanog-customfields-test.db'
+INSTALLED_APPS = (
+    'django.contrib.auth', 
+    'django.contrib.contenttypes', 
+    'django.contrib.sessions', 
+    'django.contrib.sites',
+    'test_project.apps.testapp',
+)
+TEMPLATE_DIRS = (
+    os.path.join(os.path.dirname(__file__), 'templates'),
+)
+
+SITE_ID = 1
+ROOT_URLCONF = 'test_project.urls'
+
+MIDDLEWARE_CLASSES = (
+    'django.contrib.sessions.middleware.SessionMiddleware',
+    'django.contrib.auth.middleware.AuthenticationMiddleware',
+)

tests/test_project/urls.py

+from django.conf.urls.defaults import *
+
+
+urlpatterns = patterns('',
+    url(r'/', include('test_project.apps.testapp.urls'))
+)