Ejemplo n.º 1
0
    def __new__(cls, name, bases, attrs):
        if (models.Model in bases
                and attrs['Meta'].abstract) or len(bases) > 1:
            return super(ShareObjectMeta, cls).__new__(cls, name, bases, attrs)

        if hasattr(attrs.get('Meta'), 'db_table'):
            delattr(attrs['Meta'], 'db_table')

        version_attrs = {}
        for key, val in attrs.items():
            if isinstance(val, models.Field) and val.unique:
                val = copy.deepcopy(val)
                val._unique = False
            if key == 'Meta' and hasattr(val, 'unique_together'):
                val = copy.deepcopy(val)
                delattr(val, 'unique_together')
            version_attrs[key] = val

        # TODO Fix this in some non-horrid fashion
        if name != 'ExtraData':
            version_attrs['extra'] = fields.ShareForeignKey('ExtraData',
                                                            null=True)

        version = super(ShareObjectMeta, cls).__new__(
            cls, name + 'Version', cls.version_bases, {
                **version_attrs,
                **{k: v()
                   for k, v in cls.share_attrs.items()},
                '__qualname__':
                attrs['__qualname__'] + 'Version',
                'same_as':
                fields.ShareForeignKey(name, null=True, related_name='+'),
            })

        if name != 'ExtraData':
            attrs['extra'] = fields.ShareOneToOneField('ExtraData', null=True)

        concrete = super(ShareObjectMeta, cls).__new__(
            cls, name, (bases[0], ) + cls.concrete_bases, {
                **attrs,
                **{k: v()
                   for k, v in cls.share_attrs.items()},
                'VersionModel':
                version,
                'same_as':
                fields.ShareForeignKey(name, null=True, related_name='+'),
                'version':
                models.OneToOneField(
                    version,
                    editable=False,
                    related_name='%(app_label)s_%(class)s_version'),
            })

        # Inject <classname>Version into the module of the original class definition
        # Makes shell_plus work
        inspect.stack()[1].frame.f_globals.update(
            {concrete.VersionModel.__name__: concrete.VersionModel})

        return concrete
Ejemplo n.º 2
0
    def __new__(cls, name, bases, attrs):
        if (models.Model in bases and attrs['Meta'].abstract) or len(bases) > 1:
            attrs = {
                **{k: v() for k, v in cls.share_attrs.items()},
                **attrs
            }
            return super(ShareObjectMeta, cls).__new__(cls, name, bases, attrs)

        version_attrs = {}
        for key, val in attrs.items():
            if isinstance(val, models.Field) and (val.unique or val.db_index):
                val = copy.deepcopy(val)
                val._unique = False
                val.db_index = False
            if isinstance(val, models.Field) and val.is_relation:
                val = copy.deepcopy(val)
                if isinstance(val, models.ForeignKey) and not isinstance(val, fields.ShareForeignKey):
                    val.remote_field.related_name = '+'
                if isinstance(val, (fields.ShareForeignKey, fields.ShareManyToManyField, fields.ShareOneToOneField)):
                    val._kwargs = {**val._kwargs, 'related_name': '+', 'db_index': False}
            if key == 'Meta':
                val = type('VersionMeta', (val, ShareObjectVersion.Meta), {'unique_together': None, 'db_table': val.db_table + 'version' if hasattr(val, 'db_table') else None})
            version_attrs[key] = val

        # TODO Fix this in some non-horrid fashion
        if name != 'ExtraData':
            version_attrs['extra'] = fields.ShareForeignKey('ExtraData', null=True)

        version = super(ShareObjectMeta, cls).__new__(cls, name + 'Version', cls.version_bases, {
            **version_attrs,
            **cls.share_attrs,
            **{k: v() for k, v in cls.share_attrs.items()},  # Excluded sources from versions. They never get filled out
            'persistent_id': models.ForeignKey(name, db_column='persistent_id', related_name='versions', on_delete=DATABASE_CASCADE),
            '__qualname__': attrs['__qualname__'] + 'Version',
            'same_as': fields.ShareForeignKey(name, null=True, related_name='+'),
        })

        if name != 'ExtraData':
            attrs['extra'] = fields.ShareOneToOneField('ExtraData', null=True)

        concrete = super(ShareObjectMeta, cls).__new__(cls, name, (bases[0], ) + cls.concrete_bases, {
            **attrs,
            **{k: v() for k, v in cls.share_attrs.items()},
            'VersionModel': version,
            'same_as': fields.ShareForeignKey(name, null=True, related_name='+'),
            'version': models.OneToOneField(version, editable=False, related_name='%(app_label)s_%(class)s_version', on_delete=DATABASE_CASCADE),
            # TypedManyToManyField works just like a normal field but has some special code to handle proxy models (if the exist)
            # and makes the database use ON DELETE CASCADE as opposed to Djangos software cascade
            'sources': fields.TypedManyToManyField(settings.AUTH_USER_MODEL, related_name='source_%(class)s', editable=False),
        })

        # Inject <classname>Version into the module of the original class definition
        __import__(concrete.__module__)
        setattr(sys.modules[concrete.__module__], concrete.VersionModel.__name__, concrete.VersionModel)

        return concrete