Skip to content

Instantly share code, notes, and snippets.

@Sovetnikov
Last active July 30, 2019 20:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Sovetnikov/ebd27eb1d783f9d66a205ac139f9f14b to your computer and use it in GitHub Desktop.
Save Sovetnikov/ebd27eb1d783f9d66a205ac139f9f14b to your computer and use it in GitHub Desktop.
django-fsm-log admin model inline for Django 1.7.7
from django.contrib.admin import TabularInline
from django.contrib.admin.utils import flatten_fieldsets
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.forms import BaseInlineFormSet, BaseModelFormSet
from django_fsm_log.models import StateLog
# Making possible to show django inline model admin without actual foreign key to parent model
# Works by making new model that has required FK (just to pass startup checks) and overriding actual queryset generated in FormSet
# For Django 1.7.7
# Use as:
# class ParentModelStateLogInlineAdmin(FSMStateInlineClass(ParentModel)):
# pass
# class ParentModelAdmin(ModelAdmin):
# model = ParentModel
# inlines = (ParentModelStateLogInlineAdmin,)
__all__ = ('FSMStateInlineClass',)
class FSMStateInlineFormset(BaseInlineFormSet):
def __init__(self, data=None, files=None, instance=None,
save_as_new=False, prefix=None, queryset=None, **kwargs):
if instance is None:
self.instance = self.fk.rel.to()
else:
self.instance = instance
self.save_as_new = save_as_new
# Just creating queryset for StateLog, ignoring given in args
qs = self.get_queryset()
# Skipping BaseInlineFormSet.__init__ right into BaseModelFormSet.__init__
BaseModelFormSet.__init__(self, data, files, prefix=prefix,
queryset=qs, **kwargs)
def get_queryset(self):
ct = ContentType.objects.get_for_model(self.instance)
if self.instance.pk is not None:
# Resulting queryset
# Ordering is here, one can make code to respect ordering from InlineClass
qs = StateLog.objects.filter(content_type=ct, object_id=self.instance.pk).order_by('-timestamp')
else:
qs = StateLog.objects.none()
return qs
def FSMStateInlineClass(to):
name = '{0}StateLog'.format(to.__name__)
class Meta:
app_label = ''
managed = True
pass
attrs = {'__module__': __name__, 'Meta': Meta}
fields = {
# FK that django admin inlines check requires
'orig_object_link': models.ForeignKey(to=to, related_name=None),
# Parent link is required to inherited model
'state_log': models.OneToOneField(to=StateLog, parent_link=True, related_name=None)
}
attrs.update(fields)
new_model = type(name, (StateLog,), attrs)
# Fix for django-reversion
setattr(to, '{0}_set'.format(name.lower()), StateLog.objects.none())
class StateInline(TabularInline):
model = new_model
can_delete = False
def has_add_permission(self, request):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
formset = FSMStateInlineFormset
fields = ('timestamp', 'by', 'state_display')
readonly_fields = ('state_display',)
def state_display(self, log):
try:
return log.get_state_display()
except Exception as e:
return str(e)
state_display.short_description = 'State'
# All fields is readonly
def get_readonly_fields(self, request, obj=None):
if self.declared_fieldsets:
readonly = flatten_fieldsets(self.declared_fieldsets)
else:
readonly = list(set(
[field.name for field in self.opts.local_fields]
))
readonly += list(self.readonly_fields)
return readonly
return StateInline
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment