mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Correct admin in new location
This commit is contained in:
parent
8e02be23e4
commit
efe3a28343
15 changed files with 259 additions and 193 deletions
|
|
@ -109,8 +109,8 @@ class AccountDB(TypedObject, AbstractUser):
|
|||
__applabel__ = "accounts"
|
||||
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
|
||||
|
||||
# class Meta:
|
||||
# verbose_name = "Account"
|
||||
class Meta:
|
||||
verbose_name = "Account"
|
||||
|
||||
# cmdset_storage property
|
||||
# This seems very sensitive to caching, so leaving it be for now /Griatch
|
||||
|
|
|
|||
|
|
@ -378,7 +378,7 @@ class ObjectDB(TypedObject):
|
|||
)
|
||||
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
"""Define Django meta options"""
|
||||
|
||||
verbose_name = "Object"
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ class InMemoryAttribute(IAttribute):
|
|||
class Attribute(IAttribute, SharedMemoryModel):
|
||||
"""
|
||||
This attribute is stored via Django. Most Attributes will be using this class.
|
||||
|
||||
"""
|
||||
|
||||
#
|
||||
|
|
@ -219,7 +220,7 @@ class Attribute(IAttribute, SharedMemoryModel):
|
|||
|
||||
class Meta(object):
|
||||
"Define Django meta options"
|
||||
verbose_name = "Evennia Attribute"
|
||||
verbose_name = "Attribute"
|
||||
|
||||
# Wrapper properties to easily set database fields. These are
|
||||
# @property decorators that allows to access these fields using
|
||||
|
|
|
|||
18
evennia/typeclasses/migrations/0014_alter_tag_db_category.py
Normal file
18
evennia/typeclasses/migrations/0014_alter_tag_db_category.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.3 on 2021-05-17 06:17
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('typeclasses', '0013_auto_20191015_1922'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='tag',
|
||||
name='db_category',
|
||||
field=models.CharField(blank=True, db_index=True, help_text='tag category', max_length=64, null=True, verbose_name='category'),
|
||||
),
|
||||
]
|
||||
|
|
@ -341,7 +341,7 @@ class TypedObject(SharedMemoryModel):
|
|||
def nattributes(self):
|
||||
return AttributeHandler(self, InMemoryAttributeBackend)
|
||||
|
||||
class Meta(object):
|
||||
class Meta:
|
||||
"""
|
||||
Django setup info.
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class Tag(models.Model):
|
|||
"key", max_length=255, null=True, help_text="tag identifier", db_index=True
|
||||
)
|
||||
db_category = models.CharField(
|
||||
"category", max_length=64, null=True, help_text="tag category", db_index=True
|
||||
"category", max_length=64, null=True, blank=True, help_text="tag category", db_index=True
|
||||
)
|
||||
db_data = models.TextField(
|
||||
"data",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
"""
|
||||
Django-admin code for customizing the web admin for Evennia.
|
||||
|
||||
"""
|
||||
|
||||
# importing here are necessary for Django to find these, since it will only
|
||||
# look for `admin` in the web/ folder.
|
||||
|
||||
from .accounts import AccountAdmin
|
||||
from .objects import ObjectAdmin
|
||||
from .scripts import ScriptAdmin
|
||||
from .comms import ChannelAdmin, MsgAdmin
|
||||
from .help import HelpEntryAdmin
|
||||
from .tags import TagAdmin
|
||||
|
|
@ -19,14 +19,15 @@ from django.urls import path, reverse
|
|||
from django.contrib.auth import update_session_auth_hash
|
||||
|
||||
from evennia.accounts.models import AccountDB
|
||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
||||
from evennia.utils import create
|
||||
from .attributes import AttributeInline
|
||||
from .tags import TagInline
|
||||
|
||||
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
|
||||
|
||||
|
||||
# handle the custom User editor
|
||||
class AccountDBChangeForm(UserChangeForm):
|
||||
class AccountChangeForm(UserChangeForm):
|
||||
"""
|
||||
Modify the accountdb class.
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ class AccountDBChangeForm(UserChangeForm):
|
|||
return self.cleaned_data["username"]
|
||||
|
||||
|
||||
class AccountDBCreationForm(UserCreationForm):
|
||||
class AccountCreationForm(UserCreationForm):
|
||||
"""
|
||||
Create a new AccountDB instance.
|
||||
"""
|
||||
|
|
@ -214,14 +215,14 @@ class AccountAttributeInline(AttributeInline):
|
|||
|
||||
|
||||
@admin.register(AccountDB)
|
||||
class AccountDBAdmin(BaseUserAdmin):
|
||||
class AccountAdmin(BaseUserAdmin):
|
||||
"""
|
||||
This is the main creation screen for Users/accounts
|
||||
|
||||
"""
|
||||
list_display = ("username", "email", "is_staff", "is_superuser")
|
||||
form = AccountDBChangeForm
|
||||
add_form = AccountDBCreationForm
|
||||
form = AccountChangeForm
|
||||
add_form = AccountCreationForm
|
||||
inlines = [AccountTagInline, AccountAttributeInline]
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password", "email")}),
|
||||
|
|
@ -360,6 +361,3 @@ class AccountDBAdmin(BaseUserAdmin):
|
|||
from django.urls import reverse
|
||||
|
||||
return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id]))
|
||||
|
||||
|
||||
# admin.site.register(AccountDB, AccountDBAdmin)
|
||||
|
|
|
|||
|
|
@ -1,168 +1,21 @@
|
|||
"""
|
||||
Attribute admin.
|
||||
|
||||
Note that we don't present a separate admin for these, since they are only
|
||||
relevant together with a specific object.
|
||||
|
||||
"""
|
||||
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from django.contrib import admin
|
||||
from evennia.typeclasses.models import Tag
|
||||
from evennia.typeclasses.attributes import Attribute
|
||||
from django import forms
|
||||
|
||||
from evennia.utils.picklefield import PickledFormField
|
||||
from evennia.utils.dbserialize import from_pickle, _SaverSet
|
||||
|
||||
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
A django Admin wrapper for Tags.
|
||||
"""
|
||||
|
||||
search_fields = ("db_key", "db_category", "db_tagtype")
|
||||
list_display = ("db_key", "db_category", "db_tagtype", "db_data")
|
||||
fields = ("db_key", "db_category", "db_tagtype", "db_data")
|
||||
list_filter = ("db_tagtype",)
|
||||
|
||||
|
||||
class TagForm(forms.ModelForm):
|
||||
"""
|
||||
This form overrides the base behavior of the ModelForm that would be used for a
|
||||
Tag-through-model. Since the through-models only have access to the foreignkeys of the Tag and
|
||||
the Object that they're attached to, we need to spoof the behavior of it being a form that would
|
||||
correspond to its tag, or the creation of a tag. Instead of being saved, we'll call to the
|
||||
Object's handler, which will handle the creation, change, or deletion of a tag for us, as well
|
||||
as updating the handler's cache so that all changes are instantly updated in-game.
|
||||
"""
|
||||
|
||||
tag_key = forms.CharField(
|
||||
label="Tag Name", required=True, help_text="This is the main key identifier"
|
||||
)
|
||||
tag_category = forms.CharField(
|
||||
label="Category",
|
||||
help_text="Used for grouping tags. Unset (default) gives a category of None",
|
||||
required=False,
|
||||
)
|
||||
tag_type = forms.CharField(
|
||||
label="Type",
|
||||
help_text='Internal use. Either unset, "alias" or "permission"',
|
||||
required=False,
|
||||
)
|
||||
tag_data = forms.CharField(
|
||||
label="Data",
|
||||
help_text="Usually unused. Intended for eventual info about the tag itself",
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = ("tag_key", "tag_category", "tag_data", "tag_type")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
If we have a tag, then we'll prepopulate our instance with the fields we'd expect it
|
||||
to have based on the tag. tag_key, tag_category, tag_type, and tag_data all refer to
|
||||
the corresponding tag fields. The initial data of the form fields will similarly be
|
||||
populated.
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
tagkey = None
|
||||
tagcategory = None
|
||||
tagtype = None
|
||||
tagdata = None
|
||||
if hasattr(self.instance, "tag"):
|
||||
tagkey = self.instance.tag.db_key
|
||||
tagcategory = self.instance.tag.db_category
|
||||
tagtype = self.instance.tag.db_tagtype
|
||||
tagdata = self.instance.tag.db_data
|
||||
self.fields["tag_key"].initial = tagkey
|
||||
self.fields["tag_category"].initial = tagcategory
|
||||
self.fields["tag_type"].initial = tagtype
|
||||
self.fields["tag_data"].initial = tagdata
|
||||
self.instance.tag_key = tagkey
|
||||
self.instance.tag_category = tagcategory
|
||||
self.instance.tag_type = tagtype
|
||||
self.instance.tag_data = tagdata
|
||||
|
||||
def save(self, commit=True):
|
||||
"""
|
||||
One thing we want to do here is the or None checks, because forms are saved with an empty
|
||||
string rather than null from forms, usually, and the Handlers may handle empty strings
|
||||
differently than None objects. So for consistency with how things are handled in game,
|
||||
we'll try to make sure that empty form fields will be None, rather than ''.
|
||||
"""
|
||||
# we are spoofing a tag for the Handler that will be called
|
||||
# instance = super().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):
|
||||
"""
|
||||
The Formset handles all the inline forms that are grouped together on the change page of the
|
||||
corresponding object. All the tags will appear here, and we'll save them by overriding the
|
||||
formset's save method. The forms will similarly spoof their save methods to return an instance
|
||||
which hasn't been saved to the database, but have the relevant fields filled out based on the
|
||||
contents of the cleaned form. We'll then use that to call to the handler of the corresponding
|
||||
Object, where the handler is an AliasHandler, PermissionsHandler, or TagHandler, based on the
|
||||
type of tag.
|
||||
"""
|
||||
|
||||
def save(self, commit=True):
|
||||
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().save(commit=False)
|
||||
# self.deleted_objects is a list created when super of save is called, we'll remove those
|
||||
for obj in self.deleted_objects:
|
||||
handler = get_handler(obj)
|
||||
handler.remove(obj.tag_key, category=obj.tag_category)
|
||||
for instance in instances:
|
||||
handler = get_handler(instance)
|
||||
handler.add(instance.tag_key, category=instance.tag_category, data=instance.tag_data)
|
||||
|
||||
|
||||
class TagInline(admin.TabularInline):
|
||||
"""
|
||||
A handler for inline Tags. This class should be subclassed in the admin of your models,
|
||||
and the 'model' and 'related_field' class attributes must be set. model should be the
|
||||
through model (ObjectDB_db_tag', for example), while related field should be the name
|
||||
of the field on that through model which points to the model being used: 'objectdb',
|
||||
'msg', 'accountdb', etc.
|
||||
"""
|
||||
|
||||
# Set this to the through model of your desired M2M when subclassing.
|
||||
model = None
|
||||
form = TagForm
|
||||
formset = TagFormSet
|
||||
related_field = None # Must be 'objectdb', 'accountdb', 'msg', etc. Set when subclassing
|
||||
# raw_id_fields = ('tag',)
|
||||
# readonly_fields = ('tag',)
|
||||
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().get_formset(request, obj, **kwargs)
|
||||
|
||||
class ProxyFormset(formset):
|
||||
pass
|
||||
|
||||
ProxyFormset.related_field = self.related_field
|
||||
return ProxyFormset
|
||||
|
||||
|
||||
class AttributeForm(forms.ModelForm):
|
||||
"""
|
||||
This form overrides the base behavior of the ModelForm that would be used for a Attribute-through-model.
|
||||
|
|
@ -318,6 +171,7 @@ class AttributeInline(admin.TabularInline):
|
|||
|
||||
# Set this to the through model of your desired M2M when subclassing.
|
||||
model = None
|
||||
verbose_name = "Attribute"
|
||||
form = AttributeForm
|
||||
formset = AttributeFormSet
|
||||
related_field = None # Must be 'objectdb', 'accountdb', 'msg', etc. Set when subclassing
|
||||
|
|
@ -339,6 +193,3 @@ class AttributeInline(admin.TabularInline):
|
|||
|
||||
ProxyFormset.related_field = self.related_field
|
||||
return ProxyFormset
|
||||
|
||||
|
||||
admin.site.register(Tag, TagAdmin)
|
||||
|
|
@ -5,9 +5,11 @@ This defines how Comm models are displayed in the web admin interface.
|
|||
|
||||
from django.contrib import admin
|
||||
from evennia.comms.models import ChannelDB
|
||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
||||
from django.conf import settings
|
||||
|
||||
from .attributes import AttributeInline
|
||||
from .tags import TagInline
|
||||
|
||||
|
||||
class ChannelAttributeInline(AttributeInline):
|
||||
"""
|
||||
|
|
@ -63,7 +65,7 @@ class ChannelAdmin(admin.ModelAdmin):
|
|||
"""
|
||||
|
||||
inlines = [ChannelTagInline, ChannelAttributeInline]
|
||||
list_display = ("id", "db_key", "db_lock_storage", "subscriptions")
|
||||
list_display = ("id", "db_key", "no_of_subscribers", "db_lock_storage")
|
||||
list_display_links = ("id", "db_key")
|
||||
ordering = ["db_key"]
|
||||
search_fields = ["id", "db_key", "db_tags__db_key"]
|
||||
|
|
@ -95,6 +97,16 @@ class ChannelAdmin(admin.ModelAdmin):
|
|||
"""
|
||||
return ", ".join([str(sub) for sub in obj.subscriptions.all()])
|
||||
|
||||
def no_of_subscribers(self, obj):
|
||||
"""
|
||||
Get number of subs for a a channel .
|
||||
|
||||
Args:
|
||||
obj (Channel): The channel to get subs from.
|
||||
|
||||
"""
|
||||
return sum(1 for sub in obj.subscriptions.all())
|
||||
|
||||
def save_model(self, request, obj, form, change):
|
||||
"""
|
||||
Model-save hook.
|
||||
|
|
|
|||
|
|
@ -4,10 +4,11 @@ Admin views.
|
|||
"""
|
||||
|
||||
from django.contrib.admin.sites import site
|
||||
from evennia.accounts.models import AccountDB
|
||||
from django.shortcuts import render
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
|
||||
from evennia.accounts.models import AccountDB
|
||||
|
||||
|
||||
@staff_member_required
|
||||
def evennia_admin(request):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ This defines how to edit help entries in Admin.
|
|||
from django import forms
|
||||
from django.contrib import admin
|
||||
from evennia.help.models import HelpEntry
|
||||
from evennia.typeclasses.admin import TagInline
|
||||
|
||||
from .tags import TagInline
|
||||
|
||||
|
||||
class HelpTagInline(TagInline):
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@
|
|||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
||||
from evennia.objects.models import ObjectDB
|
||||
from django.contrib.admin.utils import flatten_fieldsets
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from evennia.objects.models import ObjectDB
|
||||
from .attributes import AttributeInline
|
||||
from .tags import TagInline
|
||||
|
||||
|
||||
class ObjectAttributeInline(AttributeInline):
|
||||
"""
|
||||
|
|
@ -86,7 +88,8 @@ class ObjectEditForm(ObjectCreateForm):
|
|||
)
|
||||
|
||||
|
||||
class ObjectDBAdmin(admin.ModelAdmin):
|
||||
@admin.register(ObjectDB)
|
||||
class ObjectAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Describes the admin page for Objects.
|
||||
|
||||
|
|
@ -143,7 +146,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
|||
|
||||
Args:
|
||||
request (Request): Incoming request.
|
||||
obj (ObjectDB, optional): Database object.
|
||||
obj (Object, optional): Database object.
|
||||
"""
|
||||
if not obj:
|
||||
return self.add_fieldsets
|
||||
|
|
@ -192,6 +195,3 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
|||
from django.urls import reverse
|
||||
|
||||
return HttpResponseRedirect(reverse("admin:objects_objectdb_change", args=[obj.id]))
|
||||
|
||||
|
||||
admin.site.register(ObjectDB, ObjectDBAdmin)
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@
|
|||
# in the web admin interface.
|
||||
#
|
||||
from django.conf import settings
|
||||
|
||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
||||
from django.contrib import admin
|
||||
|
||||
from evennia.scripts.models import ScriptDB
|
||||
from django.contrib import admin
|
||||
from .attributes import AttributeInline
|
||||
from .tags import TagInline
|
||||
|
||||
|
||||
class ScriptTagInline(TagInline):
|
||||
|
|
@ -30,7 +30,8 @@ class ScriptAttributeInline(AttributeInline):
|
|||
related_field = "scriptdb"
|
||||
|
||||
|
||||
class ScriptDBAdmin(admin.ModelAdmin):
|
||||
@admin.register(ScriptDB)
|
||||
class ScriptAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
Displaying the main Script page.
|
||||
|
||||
|
|
@ -86,6 +87,3 @@ class ScriptDBAdmin(admin.ModelAdmin):
|
|||
# adding a new object
|
||||
# have to call init with typeclass passed to it
|
||||
obj.set_class_from_typeclass(typeclass_path=obj.db_typeclass_path)
|
||||
|
||||
|
||||
admin.site.register(ScriptDB, ScriptDBAdmin)
|
||||
|
|
|
|||
172
evennia/web/admin/tags.py
Normal file
172
evennia/web/admin/tags.py
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
"""
|
||||
Tag admin
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from django.contrib import admin
|
||||
from evennia.typeclasses.tags import Tag
|
||||
from django import forms
|
||||
|
||||
from evennia.utils.picklefield import PickledFormField
|
||||
from evennia.utils.dbserialize import from_pickle, _SaverSet
|
||||
|
||||
|
||||
class TagForm(forms.ModelForm):
|
||||
"""
|
||||
This form overrides the base behavior of the ModelForm that would be used for a
|
||||
Tag-through-model. Since the through-models only have access to the foreignkeys of the Tag and
|
||||
the Object that they're attached to, we need to spoof the behavior of it being a form that would
|
||||
correspond to its tag, or the creation of a tag. Instead of being saved, we'll call to the
|
||||
Object's handler, which will handle the creation, change, or deletion of a tag for us, as well
|
||||
as updating the handler's cache so that all changes are instantly updated in-game.
|
||||
"""
|
||||
|
||||
tag_key = forms.CharField(
|
||||
label="Tag Name", required=True, help_text="This is the main key identifier"
|
||||
)
|
||||
tag_category = forms.CharField(
|
||||
label="Category",
|
||||
help_text="Used for grouping tags. Unset (default) gives a category of None",
|
||||
required=False,
|
||||
)
|
||||
tag_type = forms.CharField(
|
||||
label="Type",
|
||||
help_text='Internal use. Either unset, "alias" or "permission"',
|
||||
required=False,
|
||||
)
|
||||
tag_data = forms.CharField(
|
||||
label="Data",
|
||||
help_text="Usually unused. Intended for eventual info about the tag itself",
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
fields = ("tag_key", "tag_category", "tag_data", "tag_type")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""
|
||||
If we have a tag, then we'll prepopulate our instance with the fields we'd expect it
|
||||
to have based on the tag. tag_key, tag_category, tag_type, and tag_data all refer to
|
||||
the corresponding tag fields. The initial data of the form fields will similarly be
|
||||
populated.
|
||||
"""
|
||||
super().__init__(*args, **kwargs)
|
||||
tagkey = None
|
||||
tagcategory = None
|
||||
tagtype = None
|
||||
tagdata = None
|
||||
if hasattr(self.instance, "tag"):
|
||||
tagkey = self.instance.tag.db_key
|
||||
tagcategory = self.instance.tag.db_category
|
||||
tagtype = self.instance.tag.db_tagtype
|
||||
tagdata = self.instance.tag.db_data
|
||||
self.fields["tag_key"].initial = tagkey
|
||||
self.fields["tag_category"].initial = tagcategory
|
||||
self.fields["tag_type"].initial = tagtype
|
||||
self.fields["tag_data"].initial = tagdata
|
||||
self.instance.tag_key = tagkey
|
||||
self.instance.tag_category = tagcategory
|
||||
self.instance.tag_type = tagtype
|
||||
self.instance.tag_data = tagdata
|
||||
|
||||
def save(self, commit=True):
|
||||
"""
|
||||
One thing we want to do here is the or None checks, because forms are saved with an empty
|
||||
string rather than null from forms, usually, and the Handlers may handle empty strings
|
||||
differently than None objects. So for consistency with how things are handled in game,
|
||||
we'll try to make sure that empty form fields will be None, rather than ''.
|
||||
"""
|
||||
# we are spoofing a tag for the Handler that will be called
|
||||
# instance = super().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):
|
||||
"""
|
||||
The Formset handles all the inline forms that are grouped together on the change page of the
|
||||
corresponding object. All the tags will appear here, and we'll save them by overriding the
|
||||
formset's save method. The forms will similarly spoof their save methods to return an instance
|
||||
which hasn't been saved to the database, but have the relevant fields filled out based on the
|
||||
contents of the cleaned form. We'll then use that to call to the handler of the corresponding
|
||||
Object, where the handler is an AliasHandler, PermissionsHandler, or TagHandler, based on the
|
||||
type of tag.
|
||||
"""
|
||||
|
||||
def save(self, commit=True):
|
||||
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().save(commit=False)
|
||||
# self.deleted_objects is a list created when super of save is called, we'll remove those
|
||||
for obj in self.deleted_objects:
|
||||
handler = get_handler(obj)
|
||||
handler.remove(obj.tag_key, category=obj.tag_category)
|
||||
for instance in instances:
|
||||
handler = get_handler(instance)
|
||||
handler.add(instance.tag_key, category=instance.tag_category, data=instance.tag_data)
|
||||
|
||||
|
||||
class TagInline(admin.TabularInline):
|
||||
"""
|
||||
A handler for inline Tags. This class should be subclassed in the admin of your models,
|
||||
and the 'model' and 'related_field' class attributes must be set. model should be the
|
||||
through model (ObjectDB_db_tag', for example), while related field should be the name
|
||||
of the field on that through model which points to the model being used: 'objectdb',
|
||||
'msg', 'accountdb', etc.
|
||||
"""
|
||||
|
||||
# Set this to the through model of your desired M2M when subclassing.
|
||||
model = None
|
||||
verbose_name = "Tag"
|
||||
form = TagForm
|
||||
formset = TagFormSet
|
||||
related_field = None # Must be 'objectdb', 'accountdb', 'msg', etc. Set when subclassing
|
||||
# raw_id_fields = ('tag',)
|
||||
# readonly_fields = ('tag',)
|
||||
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().get_formset(request, obj, **kwargs)
|
||||
|
||||
class ProxyFormset(formset):
|
||||
pass
|
||||
|
||||
ProxyFormset.related_field = self.related_field
|
||||
return ProxyFormset
|
||||
|
||||
|
||||
@admin.register(Tag)
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
"""
|
||||
A django Admin wrapper for Tags.
|
||||
"""
|
||||
|
||||
search_fields = ("db_key", "db_category", "db_tagtype")
|
||||
list_display = ("db_key", "db_category", "db_tagtype", "db_model", "db_data")
|
||||
fields = ("db_key", "db_category", "db_tagtype", "db_model", "db_data")
|
||||
list_filter = ("db_tagtype", "db_category", "db_model")
|
||||
Loading…
Add table
Add a link
Reference in a new issue