Start with final load/save/spawn nodes of menu
This commit is contained in:
parent
194eb8e42f
commit
01af170eae
3 changed files with 179 additions and 79 deletions
|
|
@ -4,6 +4,7 @@ OLC Prototype menu nodes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
from ast import literal_eval
|
from ast import literal_eval
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from evennia.utils.evmenu import EvMenu, list_node
|
from evennia.utils.evmenu import EvMenu, list_node
|
||||||
|
|
@ -132,10 +133,16 @@ def _set_property(caller, raw_string, **kwargs):
|
||||||
|
|
||||||
caller.ndb._menutree.olc_prototype = prototype
|
caller.ndb._menutree.olc_prototype = prototype
|
||||||
|
|
||||||
out = [" Set {prop} to {value} ({typ}).".format(prop=prop, value=value, typ=type(value))]
|
try:
|
||||||
|
# TODO simple way to get rid of the u'' markers in list reprs, remove this when on py3.
|
||||||
|
repr_value = json.dumps(value)
|
||||||
|
except Exception:
|
||||||
|
repr_value = value
|
||||||
|
|
||||||
|
out = [" Set {prop} to {value} ({typ}).".format(prop=prop, value=repr_value, typ=type(value))]
|
||||||
|
|
||||||
if kwargs.get("test_parse", True):
|
if kwargs.get("test_parse", True):
|
||||||
out.append(" Simulating parsing ...")
|
out.append(" Simulating prototype-func parsing ...")
|
||||||
err, parsed_value = protlib.protfunc_parser(value, testing=True)
|
err, parsed_value = protlib.protfunc_parser(value, testing=True)
|
||||||
if err:
|
if err:
|
||||||
out.append(" |yPython `literal_eval` warning: {}|n".format(err))
|
out.append(" |yPython `literal_eval` warning: {}|n".format(err))
|
||||||
|
|
@ -143,7 +150,7 @@ def _set_property(caller, raw_string, **kwargs):
|
||||||
out.append(" |g(Example-)value when parsed ({}):|n {}".format(
|
out.append(" |g(Example-)value when parsed ({}):|n {}".format(
|
||||||
type(parsed_value), parsed_value))
|
type(parsed_value), parsed_value))
|
||||||
else:
|
else:
|
||||||
out.append(" |gNo change.")
|
out.append(" |gNo change when parsed.")
|
||||||
|
|
||||||
caller.msg("\n".join(out))
|
caller.msg("\n".join(out))
|
||||||
|
|
||||||
|
|
@ -185,7 +192,8 @@ def _path_cropper(pythonpath):
|
||||||
def node_index(caller):
|
def node_index(caller):
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
|
|
||||||
text = ("|c --- Prototype wizard --- |n\n\n"
|
text = (
|
||||||
|
"|c --- Prototype wizard --- |n\n\n"
|
||||||
"Define the |yproperties|n of the prototype. All prototype values can be "
|
"Define the |yproperties|n of the prototype. All prototype values can be "
|
||||||
"over-ridden at the time of spawning an instance of the prototype, but some are "
|
"over-ridden at the time of spawning an instance of the prototype, but some are "
|
||||||
"required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used "
|
"required.\n\n'|wprototype-'-properties|n are not used in the prototype itself but are used "
|
||||||
|
|
@ -197,11 +205,11 @@ def node_index(caller):
|
||||||
options.append(
|
options.append(
|
||||||
{"desc": "|WPrototype-Key|n|n{}".format(_format_option_value("Key", True, prototype, None)),
|
{"desc": "|WPrototype-Key|n|n{}".format(_format_option_value("Key", True, prototype, None)),
|
||||||
"goto": "node_prototype_key"})
|
"goto": "node_prototype_key"})
|
||||||
for key in ('Prototype', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
|
for key in ('Typeclass', 'Prototype-parent', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
|
||||||
'Permissions', 'Location', 'Home', 'Destination'):
|
'Permissions', 'Location', 'Home', 'Destination'):
|
||||||
required = False
|
required = False
|
||||||
cropper = None
|
cropper = None
|
||||||
if key in ("Prototype", "Typeclass"):
|
if key in ("Prototype-parent", "Typeclass"):
|
||||||
required = "prototype" not in prototype and "typeclass" not in prototype
|
required = "prototype" not in prototype and "typeclass" not in prototype
|
||||||
if key == 'Typeclass':
|
if key == 'Typeclass':
|
||||||
cropper = _path_cropper
|
cropper = _path_cropper
|
||||||
|
|
@ -215,6 +223,12 @@ def node_index(caller):
|
||||||
{"desc": "|WPrototype-{}|n|n{}".format(
|
{"desc": "|WPrototype-{}|n|n{}".format(
|
||||||
key, _format_option_value(key, required, prototype, None)),
|
key, _format_option_value(key, required, prototype, None)),
|
||||||
"goto": "node_prototype_{}".format(key.lower())})
|
"goto": "node_prototype_{}".format(key.lower())})
|
||||||
|
for key in ("Load", "Save", "Spawn"):
|
||||||
|
options.append(
|
||||||
|
{"key": ("|w{}|W{}".format(key[0], key[1:]), key[0]),
|
||||||
|
"desc": "|W{}|n".format(
|
||||||
|
key, _format_option_value(key, required, prototype, None)),
|
||||||
|
"goto": "node_prototype_{}".format(key.lower())})
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
@ -429,54 +443,82 @@ def _caller_attrs(caller):
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
def _attrparse(caller, attr_string):
|
def _display_attribute(attr_tuple):
|
||||||
"attr is entering on the form 'attr = value'"
|
"""Pretty-print attribute tuple"""
|
||||||
|
attrkey, value, category, locks, default_access = attr_tuple
|
||||||
|
value = protlib.protfunc_parser(value)
|
||||||
|
typ = type(value)
|
||||||
|
out = ("Attribute key: '{attrkey}' (category: {category}, "
|
||||||
|
"locks: {locks})\n"
|
||||||
|
"Value (parsed to {typ}): {value}").format(
|
||||||
|
attrkey=attrkey,
|
||||||
|
category=category, locks=locks,
|
||||||
|
typ=typ, value=value)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _add_attr(caller, attr_string, **kwargs):
|
||||||
|
"""
|
||||||
|
Add new attrubute, parsing input.
|
||||||
|
attr is entered on these forms
|
||||||
|
attr = value
|
||||||
|
attr;category = value
|
||||||
|
attr;category;lockstring = value
|
||||||
|
|
||||||
|
"""
|
||||||
|
attrname = ''
|
||||||
|
category = None
|
||||||
|
locks = ''
|
||||||
|
|
||||||
if '=' in attr_string:
|
if '=' in attr_string:
|
||||||
attrname, value = (part.strip() for part in attr_string.split('=', 1))
|
attrname, value = (part.strip() for part in attr_string.split('=', 1))
|
||||||
attrname = attrname.lower()
|
attrname = attrname.lower()
|
||||||
if attrname:
|
nameparts = attrname.split(";", 2)
|
||||||
try:
|
nparts = len(nameparts)
|
||||||
value = literal_eval(value)
|
if nparts == 2:
|
||||||
except SyntaxError:
|
attrname, category = nameparts
|
||||||
caller.msg(_MENU_ATTR_LITERAL_EVAL_ERROR)
|
elif nparts > 2:
|
||||||
else:
|
attrname, category, locks = nameparts
|
||||||
return attrname, value
|
attr_tuple = (attrname, category, locks)
|
||||||
else:
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
|
|
||||||
def _add_attr(caller, attr_string, **kwargs):
|
|
||||||
attrname, value = _attrparse(caller, attr_string)
|
|
||||||
if attrname:
|
if attrname:
|
||||||
prot = _get_menu_prototype(caller)
|
prot = _get_menu_prototype(caller)
|
||||||
prot['attrs'][attrname] = value
|
attrs = prot.get('attrs', [])
|
||||||
_set_prototype_value(caller, "prototype", prot)
|
|
||||||
text = "Added"
|
try:
|
||||||
|
# replace existing attribute with the same name in the prototype
|
||||||
|
ind = [tup[0] for tup in attrs].index(attrname)
|
||||||
|
attrs[ind] = attr_tuple
|
||||||
|
except IndexError:
|
||||||
|
attrs.append(attr_tuple)
|
||||||
|
|
||||||
|
_set_prototype_value(caller, "attrs", attrs)
|
||||||
|
|
||||||
|
text = kwargs.get('text')
|
||||||
|
if not text:
|
||||||
|
if 'edit' in kwargs:
|
||||||
|
text = "Edited " + _display_attribute(attr_tuple)
|
||||||
else:
|
else:
|
||||||
text = "Attribute must be given as 'attrname = <value>' where <value> uses valid Python."
|
text = "Added " + _display_attribute(attr_tuple)
|
||||||
|
else:
|
||||||
|
text = "Attribute must be given as 'attrname[;category;locks] = <value>'."
|
||||||
|
|
||||||
options = {"key": "_default",
|
options = {"key": "_default",
|
||||||
"goto": lambda caller: None}
|
"goto": lambda caller: None}
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _edit_attr(caller, attrname, new_value, **kwargs):
|
def _edit_attr(caller, attrname, new_value, **kwargs):
|
||||||
attrname, value = _attrparse("{}={}".format(caller, attrname, new_value))
|
|
||||||
if attrname:
|
attr_string = "{}={}".format(attrname, new_value)
|
||||||
prot = _get_menu_prototype(caller)
|
|
||||||
prot['attrs'][attrname] = value
|
return _add_attr(caller, attr_string, edit=True)
|
||||||
text = "Edited Attribute {} = {}".format(attrname, value)
|
|
||||||
else:
|
|
||||||
text = "Attribute value must be valid Python."
|
|
||||||
options = {"key": "_default",
|
|
||||||
"goto": lambda caller: None}
|
|
||||||
return text, options
|
|
||||||
|
|
||||||
|
|
||||||
def _examine_attr(caller, selection):
|
def _examine_attr(caller, selection):
|
||||||
prot = _get_menu_prototype(caller)
|
prot = _get_menu_prototype(caller)
|
||||||
value = prot['attrs'][selection]
|
attr_tuple = prot['attrs'][selection]
|
||||||
return "Attribute {} = {}".format(selection, value)
|
return _display_attribute(attr_tuple)
|
||||||
|
|
||||||
|
|
||||||
@list_node(_caller_attrs)
|
@list_node(_caller_attrs)
|
||||||
|
|
@ -484,8 +526,12 @@ def node_attrs(caller):
|
||||||
prot = _get_menu_prototype(caller)
|
prot = _get_menu_prototype(caller)
|
||||||
attrs = prot.get("attrs")
|
attrs = prot.get("attrs")
|
||||||
|
|
||||||
text = ["Set the prototype's |yAttributes|n. Separate multiple attrs with commas. "
|
text = ["Set the prototype's |yAttributes|n. Enter attributes on one of these forms:\n"
|
||||||
"Will retain case sensitivity."]
|
" attrname=value\n attrname;category=value\n attrname;category;lockstring=value\n"
|
||||||
|
"To give an attribute without a category but with a lockstring, leave that spot empty "
|
||||||
|
"(attrname;;lockstring=value)."
|
||||||
|
"Separate multiple attrs with commas. Use quotes to escape inputs with commas and "
|
||||||
|
"semi-colon."]
|
||||||
if attrs:
|
if attrs:
|
||||||
text.append("Current attrs are '|y{attrs}|n'.".format(attrs=attrs))
|
text.append("Current attrs are '|y{attrs}|n'.".format(attrs=attrs))
|
||||||
else:
|
else:
|
||||||
|
|
@ -506,46 +552,78 @@ def _caller_tags(caller):
|
||||||
return tags
|
return tags
|
||||||
|
|
||||||
|
|
||||||
|
def _display_tag(tag_tuple):
|
||||||
|
"""Pretty-print attribute tuple"""
|
||||||
|
tagkey, category, data = tag_tuple
|
||||||
|
out = ("Tag: '{tagkey}' (category: {category}{})".format(
|
||||||
|
tagkey=tagkey, category=category, data=", data: {}".format(data) if data else ""))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
def _add_tag(caller, tag, **kwargs):
|
def _add_tag(caller, tag, **kwargs):
|
||||||
|
"""
|
||||||
|
Add tags to the system, parsing this syntax:
|
||||||
|
tagname
|
||||||
|
tagname;category
|
||||||
|
tagname;category;data
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
tag = tag.strip().lower()
|
tag = tag.strip().lower()
|
||||||
prototype = _get_menu_prototype(caller)
|
category = None
|
||||||
tags = prototype.get('tags', [])
|
data = ""
|
||||||
if tags:
|
|
||||||
if tag not in tags:
|
tagtuple = tag.split(";", 2)
|
||||||
tags.append(tag)
|
ntuple = len(tagtuple)
|
||||||
else:
|
|
||||||
tags = [tag]
|
if ntuple == 2:
|
||||||
prototype['tags'] = tags
|
tag, category = tagtuple
|
||||||
_set_prototype_value(caller, "prototype", prototype)
|
elif ntuple > 2:
|
||||||
text = kwargs.get("text")
|
tag, category, data = tagtuple
|
||||||
|
|
||||||
|
tag_tuple = (tag, category, data)
|
||||||
|
|
||||||
|
if tag:
|
||||||
|
prot = _get_menu_prototype(caller)
|
||||||
|
tags = prot.get('tags', [])
|
||||||
|
|
||||||
|
old_tag = kwargs.get("edit", None)
|
||||||
|
|
||||||
|
if old_tag:
|
||||||
|
# editing a tag means removing the old and replacing with new
|
||||||
|
try:
|
||||||
|
ind = [tup[0] for tup in tags].index(old_tag)
|
||||||
|
del tags[ind]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
tags.append(tag_tuple)
|
||||||
|
|
||||||
|
_set_prototype_value(caller, "tags", tags)
|
||||||
|
|
||||||
|
text = kwargs.get('text')
|
||||||
if not text:
|
if not text:
|
||||||
text = "Added tag {}. (return to continue)".format(tag)
|
if 'edit' in kwargs:
|
||||||
|
text = "Edited " + _display_tag(tag_tuple)
|
||||||
|
else:
|
||||||
|
text = "Added " + _display_tag(tag_tuple)
|
||||||
|
else:
|
||||||
|
text = "Tag must be given as 'tag[;category;data]."
|
||||||
|
|
||||||
options = {"key": "_default",
|
options = {"key": "_default",
|
||||||
"goto": lambda caller: None}
|
"goto": lambda caller: None}
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
def _edit_tag(caller, old_tag, new_tag, **kwargs):
|
def _edit_tag(caller, old_tag, new_tag, **kwargs):
|
||||||
prototype = _get_menu_prototype(caller)
|
return _add_tag(caller, new_tag, edit=old_tag)
|
||||||
tags = prototype.get('tags', [])
|
|
||||||
|
|
||||||
old_tag = old_tag.strip().lower()
|
|
||||||
new_tag = new_tag.strip().lower()
|
|
||||||
tags[tags.index(old_tag)] = new_tag
|
|
||||||
prototype['tags'] = tags
|
|
||||||
_set_prototype_value(caller, 'prototype', prototype)
|
|
||||||
|
|
||||||
text = kwargs.get('text')
|
|
||||||
if not text:
|
|
||||||
text = "Changed tag {} to {}.".format(old_tag, new_tag)
|
|
||||||
options = {"key": "_default",
|
|
||||||
"goto": lambda caller: None}
|
|
||||||
return text, options
|
|
||||||
|
|
||||||
|
|
||||||
@list_node(_caller_tags)
|
@list_node(_caller_tags)
|
||||||
def node_tags(caller):
|
def node_tags(caller):
|
||||||
text = "Set the prototype's |yTags|n."
|
text = ("Set the prototype's |yTags|n. Enter tags on one of the following forms:\n"
|
||||||
|
" tag\n tag;category\n tag;category;data\n"
|
||||||
|
"Note that 'data' is not commonly used.")
|
||||||
options = _wizard_options("tags", "attrs", "locks")
|
options = _wizard_options("tags", "attrs", "locks")
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
@ -650,7 +728,7 @@ def node_destination(caller):
|
||||||
def node_prototype_desc(caller):
|
def node_prototype_desc(caller):
|
||||||
|
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
text = ["The |wMeta-Description|n briefly describes the prototype for viewing in listings."]
|
text = ["The |wPrototype-Description|n briefly describes the prototype for viewing in listings."]
|
||||||
desc = prototype.get("prototype_desc", None)
|
desc = prototype.get("prototype_desc", None)
|
||||||
|
|
||||||
if desc:
|
if desc:
|
||||||
|
|
@ -670,7 +748,7 @@ def node_prototype_desc(caller):
|
||||||
|
|
||||||
def node_prototype_tags(caller):
|
def node_prototype_tags(caller):
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
text = ["|wMeta-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. "
|
text = ["|wPrototype-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. "
|
||||||
"Separate multiple by tags by commas."]
|
"Separate multiple by tags by commas."]
|
||||||
tags = prototype.get('prototype_tags', [])
|
tags = prototype.get('prototype_tags', [])
|
||||||
|
|
||||||
|
|
@ -691,15 +769,15 @@ def node_prototype_tags(caller):
|
||||||
|
|
||||||
def node_prototype_locks(caller):
|
def node_prototype_locks(caller):
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
text = ["Set |wMeta-Locks|n on the prototype. There are two valid lock types: "
|
text = ["Set |wPrototype-Locks|n on the prototype. There are two valid lock types: "
|
||||||
"'edit' (who can edit the prototype) and 'use' (who can apply the prototype)\n"
|
"'edit' (who can edit the prototype) and 'spawn' (who can spawn new objects with this "
|
||||||
"(If you are unsure, leave as default.)"]
|
"prototype)\n(If you are unsure, leave as default.)"]
|
||||||
locks = prototype.get('prototype_locks', '')
|
locks = prototype.get('prototype_locks', '')
|
||||||
if locks:
|
if locks:
|
||||||
text.append("Current lock is |w'{lockstring}'|n".format(lockstring=locks))
|
text.append("Current lock is |w'{lockstring}'|n".format(lockstring=locks))
|
||||||
else:
|
else:
|
||||||
text.append("Lock unset - if not changed the default lockstring will be set as\n"
|
text.append("Lock unset - if not changed the default lockstring will be set as\n"
|
||||||
" |w'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
|
" |w'spawn:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
|
||||||
text = "\n\n".join(text)
|
text = "\n\n".join(text)
|
||||||
options = _wizard_options("prototype_locks", "prototype_tags", "index")
|
options = _wizard_options("prototype_locks", "prototype_tags", "index")
|
||||||
options.append({"key": "_default",
|
options.append({"key": "_default",
|
||||||
|
|
@ -710,6 +788,21 @@ def node_prototype_locks(caller):
|
||||||
return text, options
|
return text, options
|
||||||
|
|
||||||
|
|
||||||
|
def node_prototype_load(caller):
|
||||||
|
# load prototype from storage
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def node_prototype_save(caller):
|
||||||
|
# save current prototype to disk
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def node_prototype_spawn(caller):
|
||||||
|
# spawn an instance of this prototype
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OLCMenu(EvMenu):
|
class OLCMenu(EvMenu):
|
||||||
"""
|
"""
|
||||||
A custom EvMenu with a different formatting for the options.
|
A custom EvMenu with a different formatting for the options.
|
||||||
|
|
@ -766,5 +859,8 @@ def start_olc(caller, session=None, prototype=None):
|
||||||
"node_prototype_desc": node_prototype_desc,
|
"node_prototype_desc": node_prototype_desc,
|
||||||
"node_prototype_tags": node_prototype_tags,
|
"node_prototype_tags": node_prototype_tags,
|
||||||
"node_prototype_locks": node_prototype_locks,
|
"node_prototype_locks": node_prototype_locks,
|
||||||
|
"node_prototype_load": node_prototype_load,
|
||||||
|
"node_prototype_save": node_prototype_save,
|
||||||
|
"node_prototype_spawn": node_prototype_spawn
|
||||||
}
|
}
|
||||||
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype)
|
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,11 @@ from evennia.utils.evtable import EvTable
|
||||||
|
|
||||||
_MODULE_PROTOTYPE_MODULES = {}
|
_MODULE_PROTOTYPE_MODULES = {}
|
||||||
_MODULE_PROTOTYPES = {}
|
_MODULE_PROTOTYPES = {}
|
||||||
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
_PROTOTYPE_META_NAMES = (
|
||||||
|
"prototype_key", "prototype_desc", "prototype_tags", "prototype_locks", "prototype_parent")
|
||||||
|
_PROTOTYPE_RESERVED_KEYS = _PROTOTYPE_META_NAMES + (
|
||||||
|
"key", "aliases", "typeclass", "location", "home", "destination",
|
||||||
|
"permissions", "locks", "exec", "tags", "attrs")
|
||||||
_PROTOTYPE_TAG_CATEGORY = "from_prototype"
|
_PROTOTYPE_TAG_CATEGORY = "from_prototype"
|
||||||
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
|
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
|
||||||
_PROT_FUNCS = {}
|
_PROT_FUNCS = {}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ Possible keywords are:
|
||||||
supported are 'edit' and 'use'.
|
supported are 'edit' and 'use'.
|
||||||
prototype_tags(list, optional): List of tags or tuples (tag, category) used to group prototype
|
prototype_tags(list, optional): List of tags or tuples (tag, category) used to group prototype
|
||||||
in listings
|
in listings
|
||||||
|
|
||||||
prototype_parent (str, tuple or callable, optional): name (prototype_key) of eventual parent prototype, or
|
prototype_parent (str, tuple or callable, optional): name (prototype_key) of eventual parent prototype, or
|
||||||
a list of parents, for multiple left-to-right inheritance.
|
a list of parents, for multiple left-to-right inheritance.
|
||||||
prototype: Deprecated. Same meaning as 'parent'.
|
prototype: Deprecated. Same meaning as 'parent'.
|
||||||
|
|
||||||
typeclass (str or callable, optional): if not set, will use typeclass of parent prototype or use
|
typeclass (str or callable, optional): if not set, will use typeclass of parent prototype or use
|
||||||
`settings.BASE_OBJECT_TYPECLASS`
|
`settings.BASE_OBJECT_TYPECLASS`
|
||||||
key (str or callable, optional): the name of the spawned object. If not given this will set to a
|
key (str or callable, optional): the name of the spawned object. If not given this will set to a
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue