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"
|
__applabel__ = "accounts"
|
||||||
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
|
__settingsclasspath__ = settings.BASE_SCRIPT_TYPECLASS
|
||||||
|
|
||||||
# class Meta:
|
class Meta:
|
||||||
# verbose_name = "Account"
|
verbose_name = "Account"
|
||||||
|
|
||||||
# cmdset_storage property
|
# cmdset_storage property
|
||||||
# This seems very sensitive to caching, so leaving it be for now /Griatch
|
# 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()]
|
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||||
|
|
||||||
class Meta(object):
|
class Meta:
|
||||||
"""Define Django meta options"""
|
"""Define Django meta options"""
|
||||||
|
|
||||||
verbose_name = "Object"
|
verbose_name = "Object"
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,7 @@ class InMemoryAttribute(IAttribute):
|
||||||
class Attribute(IAttribute, SharedMemoryModel):
|
class Attribute(IAttribute, SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
This attribute is stored via Django. Most Attributes will be using this class.
|
This attribute is stored via Django. Most Attributes will be using this class.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -219,7 +220,7 @@ class Attribute(IAttribute, SharedMemoryModel):
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"Define Django meta options"
|
"Define Django meta options"
|
||||||
verbose_name = "Evennia Attribute"
|
verbose_name = "Attribute"
|
||||||
|
|
||||||
# Wrapper properties to easily set database fields. These are
|
# Wrapper properties to easily set database fields. These are
|
||||||
# @property decorators that allows to access these fields using
|
# @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):
|
def nattributes(self):
|
||||||
return AttributeHandler(self, InMemoryAttributeBackend)
|
return AttributeHandler(self, InMemoryAttributeBackend)
|
||||||
|
|
||||||
class Meta(object):
|
class Meta:
|
||||||
"""
|
"""
|
||||||
Django setup info.
|
Django setup info.
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ class Tag(models.Model):
|
||||||
"key", max_length=255, null=True, help_text="tag identifier", db_index=True
|
"key", max_length=255, null=True, help_text="tag identifier", db_index=True
|
||||||
)
|
)
|
||||||
db_category = models.CharField(
|
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(
|
db_data = models.TextField(
|
||||||
"data",
|
"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 django.contrib.auth import update_session_auth_hash
|
||||||
|
|
||||||
from evennia.accounts.models import AccountDB
|
from evennia.accounts.models import AccountDB
|
||||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
|
||||||
from evennia.utils import create
|
from evennia.utils import create
|
||||||
|
from .attributes import AttributeInline
|
||||||
|
from .tags import TagInline
|
||||||
|
|
||||||
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
|
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
|
||||||
|
|
||||||
|
|
||||||
# handle the custom User editor
|
# handle the custom User editor
|
||||||
class AccountDBChangeForm(UserChangeForm):
|
class AccountChangeForm(UserChangeForm):
|
||||||
"""
|
"""
|
||||||
Modify the accountdb class.
|
Modify the accountdb class.
|
||||||
|
|
||||||
|
|
@ -61,7 +62,7 @@ class AccountDBChangeForm(UserChangeForm):
|
||||||
return self.cleaned_data["username"]
|
return self.cleaned_data["username"]
|
||||||
|
|
||||||
|
|
||||||
class AccountDBCreationForm(UserCreationForm):
|
class AccountCreationForm(UserCreationForm):
|
||||||
"""
|
"""
|
||||||
Create a new AccountDB instance.
|
Create a new AccountDB instance.
|
||||||
"""
|
"""
|
||||||
|
|
@ -214,14 +215,14 @@ class AccountAttributeInline(AttributeInline):
|
||||||
|
|
||||||
|
|
||||||
@admin.register(AccountDB)
|
@admin.register(AccountDB)
|
||||||
class AccountDBAdmin(BaseUserAdmin):
|
class AccountAdmin(BaseUserAdmin):
|
||||||
"""
|
"""
|
||||||
This is the main creation screen for Users/accounts
|
This is the main creation screen for Users/accounts
|
||||||
|
|
||||||
"""
|
"""
|
||||||
list_display = ("username", "email", "is_staff", "is_superuser")
|
list_display = ("username", "email", "is_staff", "is_superuser")
|
||||||
form = AccountDBChangeForm
|
form = AccountChangeForm
|
||||||
add_form = AccountDBCreationForm
|
add_form = AccountCreationForm
|
||||||
inlines = [AccountTagInline, AccountAttributeInline]
|
inlines = [AccountTagInline, AccountAttributeInline]
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(None, {"fields": ("username", "password", "email")}),
|
(None, {"fields": ("username", "password", "email")}),
|
||||||
|
|
@ -360,6 +361,3 @@ class AccountDBAdmin(BaseUserAdmin):
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse("admin:accounts_accountdb_change", args=[obj.id]))
|
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
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from evennia.typeclasses.models import Tag
|
from evennia.typeclasses.attributes import Attribute
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
from evennia.utils.picklefield import PickledFormField
|
from evennia.utils.picklefield import PickledFormField
|
||||||
from evennia.utils.dbserialize import from_pickle, _SaverSet
|
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):
|
class AttributeForm(forms.ModelForm):
|
||||||
"""
|
"""
|
||||||
This form overrides the base behavior of the ModelForm that would be used for a Attribute-through-model.
|
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.
|
# Set this to the through model of your desired M2M when subclassing.
|
||||||
model = None
|
model = None
|
||||||
|
verbose_name = "Attribute"
|
||||||
form = AttributeForm
|
form = AttributeForm
|
||||||
formset = AttributeFormSet
|
formset = AttributeFormSet
|
||||||
related_field = None # Must be 'objectdb', 'accountdb', 'msg', etc. Set when subclassing
|
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
|
ProxyFormset.related_field = self.related_field
|
||||||
return ProxyFormset
|
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 django.contrib import admin
|
||||||
from evennia.comms.models import ChannelDB
|
from evennia.comms.models import ChannelDB
|
||||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
|
from .attributes import AttributeInline
|
||||||
|
from .tags import TagInline
|
||||||
|
|
||||||
|
|
||||||
class ChannelAttributeInline(AttributeInline):
|
class ChannelAttributeInline(AttributeInline):
|
||||||
"""
|
"""
|
||||||
|
|
@ -63,7 +65,7 @@ class ChannelAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
inlines = [ChannelTagInline, ChannelAttributeInline]
|
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")
|
list_display_links = ("id", "db_key")
|
||||||
ordering = ["db_key"]
|
ordering = ["db_key"]
|
||||||
search_fields = ["id", "db_key", "db_tags__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()])
|
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):
|
def save_model(self, request, obj, form, change):
|
||||||
"""
|
"""
|
||||||
Model-save hook.
|
Model-save hook.
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ Admin views.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.contrib.admin.sites import site
|
from django.contrib.admin.sites import site
|
||||||
from evennia.accounts.models import AccountDB
|
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
|
|
||||||
|
from evennia.accounts.models import AccountDB
|
||||||
|
|
||||||
|
|
||||||
@staff_member_required
|
@staff_member_required
|
||||||
def evennia_admin(request):
|
def evennia_admin(request):
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,8 @@ This defines how to edit help entries in Admin.
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from evennia.help.models import HelpEntry
|
from evennia.help.models import HelpEntry
|
||||||
from evennia.typeclasses.admin import TagInline
|
|
||||||
|
from .tags import TagInline
|
||||||
|
|
||||||
|
|
||||||
class HelpTagInline(TagInline):
|
class HelpTagInline(TagInline):
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
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.contrib.admin.utils import flatten_fieldsets
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
from evennia.objects.models import ObjectDB
|
||||||
|
from .attributes import AttributeInline
|
||||||
|
from .tags import TagInline
|
||||||
|
|
||||||
|
|
||||||
class ObjectAttributeInline(AttributeInline):
|
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.
|
Describes the admin page for Objects.
|
||||||
|
|
||||||
|
|
@ -143,7 +146,7 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
request (Request): Incoming request.
|
request (Request): Incoming request.
|
||||||
obj (ObjectDB, optional): Database object.
|
obj (Object, optional): Database object.
|
||||||
"""
|
"""
|
||||||
if not obj:
|
if not obj:
|
||||||
return self.add_fieldsets
|
return self.add_fieldsets
|
||||||
|
|
@ -192,6 +195,3 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
return HttpResponseRedirect(reverse("admin:objects_objectdb_change", args=[obj.id]))
|
return HttpResponseRedirect(reverse("admin:objects_objectdb_change", args=[obj.id]))
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(ObjectDB, ObjectDBAdmin)
|
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,11 @@
|
||||||
# in the web admin interface.
|
# in the web admin interface.
|
||||||
#
|
#
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.contrib import admin
|
||||||
from evennia.typeclasses.admin import AttributeInline, TagInline
|
|
||||||
|
|
||||||
from evennia.scripts.models import ScriptDB
|
from evennia.scripts.models import ScriptDB
|
||||||
from django.contrib import admin
|
from .attributes import AttributeInline
|
||||||
|
from .tags import TagInline
|
||||||
|
|
||||||
|
|
||||||
class ScriptTagInline(TagInline):
|
class ScriptTagInline(TagInline):
|
||||||
|
|
@ -30,7 +30,8 @@ class ScriptAttributeInline(AttributeInline):
|
||||||
related_field = "scriptdb"
|
related_field = "scriptdb"
|
||||||
|
|
||||||
|
|
||||||
class ScriptDBAdmin(admin.ModelAdmin):
|
@admin.register(ScriptDB)
|
||||||
|
class ScriptAdmin(admin.ModelAdmin):
|
||||||
"""
|
"""
|
||||||
Displaying the main Script page.
|
Displaying the main Script page.
|
||||||
|
|
||||||
|
|
@ -86,6 +87,3 @@ class ScriptDBAdmin(admin.ModelAdmin):
|
||||||
# adding a new object
|
# adding a new object
|
||||||
# have to call init with typeclass passed to it
|
# have to call init with typeclass passed to it
|
||||||
obj.set_class_from_typeclass(typeclass_path=obj.db_typeclass_path)
|
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