From 6a851750f31199708188ea15609e2ea299b85811 Mon Sep 17 00:00:00 2001 From: Tehom Date: Mon, 7 Nov 2016 09:00:26 -0500 Subject: [PATCH] Custom form/formset for adding/removing tags using the handler. Each TagAdmin used by the different typeclasses are now also required to specify a related_field, which seems redundant but is very hard to get around otherwise. Still to do - make sure that all the adds are working properly, and extend this to attributes as well, and make Tag searches still sort of useful in the admin so people can at least check names and so on --- evennia/objects/admin.py | 1 + evennia/players/admin.py | 1 + evennia/scripts/admin.py | 1 + evennia/typeclasses/admin.py | 77 +++++++++++++++++++++++++++++++++++- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/evennia/objects/admin.py b/evennia/objects/admin.py index 821804bf2e..7ad78a2f2a 100644 --- a/evennia/objects/admin.py +++ b/evennia/objects/admin.py @@ -26,6 +26,7 @@ class ObjectTagInline(TagInline): """ model = ObjectDB.db_tags.through + related_field = "objectdb" class ObjectCreateForm(forms.ModelForm): diff --git a/evennia/players/admin.py b/evennia/players/admin.py index ce214fa928..94ccabd547 100644 --- a/evennia/players/admin.py +++ b/evennia/players/admin.py @@ -173,6 +173,7 @@ class PlayerTagInline(TagInline): """ model = PlayerDB.db_tags.through + related_field = "playerdb" class PlayerAttributeInline(AttributeInline): diff --git a/evennia/scripts/admin.py b/evennia/scripts/admin.py index a0bafe6455..b3084da58e 100644 --- a/evennia/scripts/admin.py +++ b/evennia/scripts/admin.py @@ -14,6 +14,7 @@ class ScriptTagInline(TagInline): """ model = ScriptDB.db_tags.through + related_field = "scriptdb" class ScriptAttributeInline(AttributeInline): diff --git a/evennia/typeclasses/admin.py b/evennia/typeclasses/admin.py index 2f326a9b74..74c84bf80e 100644 --- a/evennia/typeclasses/admin.py +++ b/evennia/typeclasses/admin.py @@ -2,6 +2,7 @@ from django.contrib import admin from django.contrib.admin import ModelAdmin from django.core.urlresolvers import reverse from evennia.typeclasses.models import Attribute, Tag +from django import forms class TagAdmin(admin.ModelAdmin): @@ -15,6 +16,60 @@ class TagAdmin(admin.ModelAdmin): list_filter = ('db_tagtype',) +class TagForm(forms.ModelForm): + tag_key = forms.CharField(label='Tag Name') + tag_category = forms.CharField(label="Category", required=False) + tag_type = forms.CharField(label="Type", required=False) + tag_data = forms.CharField(label="Data", required=False) + + def __init__(self, *args, **kwargs): + super(TagForm, self).__init__(*args, **kwargs) + if hasattr(self.instance, 'tag'): + self.fields['tag_key'].initial = self.instance.tag.db_key + self.fields['tag_category'].initial = self.instance.tag.db_category + self.fields['tag_type'].initial = self.instance.tag.db_tagtype + self.fields['tag_data'].initial = self.instance.tag.db_data + + def save(self, commit=True): + # we are spoofing a tag for the Handler that will be called + #instance = super(TagForm, self).save(commit=False) + instance = self.instance + instance.tag_key = self.cleaned_data['tag_key'] + instance.tag_category = self.cleaned_data['tag_category'] or None + instance.tag_type = self.cleaned_data['tag_type'] or None + instance.tag_data = self.cleaned_data['tag_data'] or None + return instance + + +class TagFormSet(forms.BaseInlineFormSet): + def save(self, commit=True): + print "inside TagFormSet" + def get_handler(finished_object): + related = getattr(finished_object, self.related_field) + try: + tagtype = finished_object.tag_type + except AttributeError: + tagtype = finished_object.tag.db_tagtype + if tagtype == "alias": + handler_name = "aliases" + elif tagtype == "permission": + handler_name = "permissions" + else: + handler_name = "tags" + return getattr(related, handler_name) + instances = super(TagFormSet, self).save(commit=False) + for obj in self.deleted_objects: + handler = get_handler(obj) + try: + tagkey = obj.tag_key + except AttributeError: + tagkey = obj.tag.db_key + handler.remove(tagkey) + for instance in instances: + handler = get_handler(instance) + handler.add(instance.tag_key) + + class TagInline(admin.TabularInline): """ A handler for inline Tags. @@ -22,10 +77,28 @@ class TagInline(admin.TabularInline): """ # Set this to the through model of your desired M2M when subclassing. model = None - fields = ('tag', 'key', 'category', 'data', 'tagtype') + form = TagForm + formset = TagFormSet + related_field = None + #fields = ('tag', 'key', 'category', 'data', 'tagtype') raw_id_fields = ('tag',) - readonly_fields = ('key', 'category', 'data', 'tagtype') + readonly_fields = ('tag',) + #readonly_fields = ('key', 'category', 'data', 'tagtype') extra = 0 + + def get_formset(self, request, obj=None, **kwargs): + """ + get_formset has to return a class, but we need to make the class that we return + know about the related_field that we'll use. Returning the class itself rather than + a proxy isn't threadsafe, since it'd be the base class and would change if multiple + people used the admin at the same time + """ + formset = super(TagInline, self).get_formset(request, obj, **kwargs) + class ProxyFormset(formset): + pass + ProxyFormset.related_field = self.related_field + return ProxyFormset + def key(self, instance): if not instance.id: return "Not yet set or saved."