Fixed and debugged object_search to more generically search for any attribute name except 'key' (issue110). Also included alias-search as a last-resort if normal searches fail. This is now also working for global searches (issue106).
This commit is contained in:
parent
7e736d19e2
commit
8bedd4d793
5 changed files with 184 additions and 176 deletions
|
|
@ -55,7 +55,7 @@ class ObjManipCommand(MuxCommand):
|
||||||
if ';' in objdef:
|
if ';' in objdef:
|
||||||
objdef, aliases = [str(part).strip()
|
objdef, aliases = [str(part).strip()
|
||||||
for part in objdef.split(';', 1)]
|
for part in objdef.split(';', 1)]
|
||||||
aliases = [str(alias).strip().lower()
|
aliases = [str(alias).strip()
|
||||||
for alias in aliases.split(';') if alias.strip()]
|
for alias in aliases.split(';') if alias.strip()]
|
||||||
lhs_objs.append({"name":objdef,
|
lhs_objs.append({"name":objdef,
|
||||||
'option': option, 'aliases': aliases})
|
'option': option, 'aliases': aliases})
|
||||||
|
|
@ -70,7 +70,7 @@ class ObjManipCommand(MuxCommand):
|
||||||
if ';' in objdef:
|
if ';' in objdef:
|
||||||
objdef, aliases = [str(part).strip()
|
objdef, aliases = [str(part).strip()
|
||||||
for part in objdef.split(';', 1)]
|
for part in objdef.split(';', 1)]
|
||||||
aliases = [str(alias).strip().lower()
|
aliases = [str(alias).strip()
|
||||||
for alias in aliases.split(';') if alias.strip()]
|
for alias in aliases.split(';') if alias.strip()]
|
||||||
rhs_objs.append({"name":objdef, 'option': option, 'aliases': aliases})
|
rhs_objs.append({"name":objdef, 'option': option, 'aliases': aliases})
|
||||||
|
|
||||||
|
|
@ -1026,7 +1026,7 @@ class CmdDig(ObjManipCommand):
|
||||||
caller = self.caller
|
caller = self.caller
|
||||||
|
|
||||||
if not self.lhs:
|
if not self.lhs:
|
||||||
string = "Usage: @dig[/teleport] roomname[:parent] [= exit_there"
|
string = "Usage: @dig[/teleport] roomname[;alias;alias...][:parent] [= exit_there"
|
||||||
string += "[;alias;alias..][:parent]] "
|
string += "[;alias;alias..][:parent]] "
|
||||||
string += "[, exit_back_here[;alias;alias..][:parent]]"
|
string += "[, exit_back_here[;alias;alias..][:parent]]"
|
||||||
caller.msg(string)
|
caller.msg(string)
|
||||||
|
|
@ -1426,7 +1426,6 @@ class CmdExamine(ObjManipCommand):
|
||||||
text = "%s[...]" % text[:line_width - headlen - 5]
|
text = "%s[...]" % text[:line_width - headlen - 5]
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def format_attributes(self, obj, attrname=None):
|
def format_attributes(self, obj, attrname=None):
|
||||||
"""
|
"""
|
||||||
Helper function that returns info about attributes and/or
|
Helper function that returns info about attributes and/or
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ except Exception:
|
||||||
from src.objects.object_search_funcs import object_multimatch_parser as IDPARSER
|
from src.objects.object_search_funcs import object_multimatch_parser as IDPARSER
|
||||||
|
|
||||||
#
|
#
|
||||||
# Helper function for the ObjectManger's search methods
|
# Helper functions for the ObjectManger's search methods
|
||||||
#
|
#
|
||||||
|
|
||||||
def match_list(searchlist, ostring, exact_match=True,
|
def match_list(searchlist, ostring, exact_match=True,
|
||||||
|
|
@ -24,28 +24,93 @@ def match_list(searchlist, ostring, exact_match=True,
|
||||||
Helper function.
|
Helper function.
|
||||||
does name/attribute matching through a list of objects.
|
does name/attribute matching through a list of objects.
|
||||||
"""
|
"""
|
||||||
ostring = ostring.lower()
|
|
||||||
if attribute_name:
|
|
||||||
#search an arbitrary attribute name for a value match.
|
|
||||||
if exact_match:
|
|
||||||
return [prospect for prospect in searchlist
|
|
||||||
if (hasattr(prospect, attribute_name) and
|
|
||||||
ostring == str(getattr(prospect, attribute_name)).lower()) \
|
|
||||||
or (ostring == str(prospect.get_attribute(attribute_name)).lower())]
|
|
||||||
else:
|
|
||||||
return [prospect for prospect in searchlist
|
|
||||||
if (hasattr(prospect, attribute_name) and
|
|
||||||
ostring in str(getattr(prospect, attribute_name)).lower()) \
|
|
||||||
or (ostring in (str(p).lower() for p in prospect.get_attribute(attribute_name)))]
|
|
||||||
else:
|
|
||||||
#search the default "key" attribute
|
|
||||||
|
|
||||||
|
if not ostring:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if not attribute_name:
|
||||||
|
attribute_name = "key"
|
||||||
|
|
||||||
|
if isinstance(ostring, basestring):
|
||||||
|
# strings are case-insensitive
|
||||||
|
ostring = ostring.lower()
|
||||||
if exact_match:
|
if exact_match:
|
||||||
return [prospect for prospect in searchlist
|
return [prospect for prospect in searchlist
|
||||||
if ostring == str(prospect.key).lower()]
|
if (hasattr(prospect, attribute_name) and
|
||||||
|
ostring == str(getattr(prospect, attribute_name)).lower())
|
||||||
|
or (prospect.has_attribute(attribute_name) and
|
||||||
|
ostring == str(prospect.get_attribute(attribute_name)).lower())]
|
||||||
else:
|
else:
|
||||||
return [prospect for prospect in searchlist
|
return [prospect for prospect in searchlist
|
||||||
if ostring in str(prospect.key).lower()]
|
if (hasattr(prospect, attribute_name) and
|
||||||
|
ostring in str(getattr(prospect, attribute_name)).lower())
|
||||||
|
or (prospect.has_attribute(attribute_name) and
|
||||||
|
ostring in str(prospect.get_attribute(attribute_name)).lower())]
|
||||||
|
else:
|
||||||
|
# If it's not a string, we don't convert to lowercase. This is also
|
||||||
|
# always treated as an exact match.
|
||||||
|
return [prospect for prospect in searchlist
|
||||||
|
if (hasattr(prospect, attribute_name) and
|
||||||
|
ostring == getattr(prospect, attribute_name))
|
||||||
|
or (prospect.has_attribute(attribute_name)
|
||||||
|
and ostring == prospect.get_attribute(attribute_name))]
|
||||||
|
|
||||||
|
|
||||||
|
def separable_search(ostring, searchlist,
|
||||||
|
attribute_name='db_key', exact_match=False):
|
||||||
|
"""
|
||||||
|
Searches a list for a object match to ostring or separator+keywords.
|
||||||
|
|
||||||
|
This version handles search criteria defined by IDPARSER. By default this
|
||||||
|
is of the type N-keyword, used to differentiate several objects of the
|
||||||
|
exact same name, e.g. 1-box, 2-box etc.
|
||||||
|
|
||||||
|
ostring: (string) The string to match against.
|
||||||
|
searchlist: (List of Objects) The objects to perform attribute comparisons on.
|
||||||
|
attribute_name: (string) attribute name to search.
|
||||||
|
exact_match: (bool) 'exact' or 'fuzzy' matching.
|
||||||
|
|
||||||
|
Note that the fuzzy matching gives precedence to exact matches; so if your
|
||||||
|
search query matches an object in the list exactly, it will be the only result.
|
||||||
|
This means that if the list contains [box,box11,box12], the search string 'box'
|
||||||
|
will only match the first entry since it is exact. The search 'box1' will however
|
||||||
|
match both box11 and box12 since neither is an exact match.
|
||||||
|
|
||||||
|
This method always returns a list, also for a single result.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Full search - this may return multiple matches.
|
||||||
|
results = match_list(searchlist, ostring, exact_match, attribute_name)
|
||||||
|
|
||||||
|
# Deal with results of search
|
||||||
|
match_number = None
|
||||||
|
if not results:
|
||||||
|
# if we have no match, check if we are dealing
|
||||||
|
# with a "N-keyword" query, if so, strip it out.
|
||||||
|
match_number, ostring = IDPARSER(ostring)
|
||||||
|
if match_number != None and ostring:
|
||||||
|
# Run the search again, without the match number
|
||||||
|
results = match_list(searchlist, ostring, exact_match, attribute_name)
|
||||||
|
elif not exact_match:
|
||||||
|
# we have results, but are using fuzzy matching; run
|
||||||
|
# second sweep in results to catch eventual exact matches
|
||||||
|
# (these are given precedence, so a search for 'ball' in
|
||||||
|
# ['ball', 'ball2'] will correctly return the first ball
|
||||||
|
# only).
|
||||||
|
exact_results = match_list(results, ostring, True, attribute_name)
|
||||||
|
if exact_results:
|
||||||
|
results = exact_results
|
||||||
|
|
||||||
|
if len(results) > 1 and match_number != None:
|
||||||
|
# We have multiple matches, but a N-type match number
|
||||||
|
# is available to separate them.
|
||||||
|
try:
|
||||||
|
results = [results[match_number]]
|
||||||
|
except IndexError:
|
||||||
|
pass
|
||||||
|
# this is always a list.
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ObjectManager(TypedObjectManager):
|
class ObjectManager(TypedObjectManager):
|
||||||
|
|
@ -62,6 +127,9 @@ class ObjectManager(TypedObjectManager):
|
||||||
# ObjectManager Get methods
|
# ObjectManager Get methods
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
|
# user/player related
|
||||||
|
|
||||||
@returns_typeclass
|
@returns_typeclass
|
||||||
def get_object_with_user(self, user):
|
def get_object_with_user(self, user):
|
||||||
"""
|
"""
|
||||||
|
|
@ -81,7 +149,7 @@ class ObjectManager(TypedObjectManager):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
# This returns typeclass since get_object_with_user and get_dbref does.
|
# This returns typeclass since get_object_with_user and get_dbref does.
|
||||||
def player_name_search(self, search_string):
|
def get_object_with_player(self, search_string):
|
||||||
"""
|
"""
|
||||||
Search for an object based on its player's name or dbref.
|
Search for an object based on its player's name or dbref.
|
||||||
This search
|
This search
|
||||||
|
|
@ -90,20 +158,25 @@ class ObjectManager(TypedObjectManager):
|
||||||
search_string: (string) The name or dbref to search for.
|
search_string: (string) The name or dbref to search for.
|
||||||
"""
|
"""
|
||||||
search_string = str(search_string).lstrip('*')
|
search_string = str(search_string).lstrip('*')
|
||||||
|
|
||||||
dbref = self.dbref(search_string)
|
dbref = self.dbref(search_string)
|
||||||
if dbref:
|
if not dbref:
|
||||||
# this is a valid dbref. Try to match it.
|
# not a dbref. Search by name.
|
||||||
dbref_match = self.dbref_search(dbref)
|
player_matches = User.objects.filter(username__iexact=search_string)
|
||||||
if dbref_match:
|
if player_matches:
|
||||||
return dbref_match
|
dbref = player_matches[0].id
|
||||||
|
# use the id to find the player
|
||||||
|
return self.get_object_with_user(dbref)
|
||||||
|
|
||||||
# not a dbref. Search by name.
|
|
||||||
player_matches = User.objects.filter(username__iexact=search_string)
|
# attr/property related
|
||||||
if player_matches:
|
|
||||||
uid = player_matches[0].id
|
@returns_typeclass_list
|
||||||
return self.get_object_with_user(uid)
|
def get_objs_with_attr(self, attribute_name):
|
||||||
return None
|
"""
|
||||||
|
Returns all objects having the given attribute_name defined at all.
|
||||||
|
"""
|
||||||
|
from src.objects.models import ObjAttribute
|
||||||
|
return [attr.obj for attr in ObjAttribute.objects.filter(db_key=attribute_name)]
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_attr_match(self, attribute_name, attribute_value):
|
def get_objs_with_attr_match(self, attribute_name, attribute_value):
|
||||||
|
|
@ -112,18 +185,30 @@ class ObjectManager(TypedObjectManager):
|
||||||
attrname set to the given value. Note that no conversion is made
|
attrname set to the given value. Note that no conversion is made
|
||||||
to attribute_value, and so it can accept also non-strings.
|
to attribute_value, and so it can accept also non-strings.
|
||||||
"""
|
"""
|
||||||
|
from src.objects.models import ObjAttribute
|
||||||
return [prospect for prospect in self.all()
|
return [attr.obj for attr in ObjAttribute.objects.filter(db_key=attribute_name)
|
||||||
if attribute_value
|
if attribute_value == attr.value]
|
||||||
and attribute_value == prospect.get_attribute(attribute_name)]
|
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_objs_with_attr(self, attribute_name):
|
def get_objs_with_db_property(self, property_name):
|
||||||
"""
|
"""
|
||||||
Returns all objects having the given attribute_name defined at all.
|
Returns all objects having a given db field property
|
||||||
"""
|
"""
|
||||||
return [prospect for prospect in self.all()
|
return [prospect for prospect in self.all()
|
||||||
if prospect.get_attribute(attribute_name)]
|
if hasattr(prospect, 'db_%s' % property_name)
|
||||||
|
or hasattr(prospect, property_name)]
|
||||||
|
|
||||||
|
@returns_typeclass_list
|
||||||
|
def get_objs_with_db_property_match(self, property_name, property_value):
|
||||||
|
"""
|
||||||
|
Returns all objects having a given db field property
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return eval("self.filter(db_%s=%s)" % (property_name, property_value))
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# main search methods and helper functions
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def get_contents(self, location, excludeobj=None):
|
def get_contents(self, location, excludeobj=None):
|
||||||
|
|
@ -148,94 +233,6 @@ class ObjectManager(TypedObjectManager):
|
||||||
matches.append(obj)
|
matches.append(obj)
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
@returns_typeclass_list
|
|
||||||
def separable_search(self, ostring, searchlist=None,
|
|
||||||
attribute_name=None, exact_match=False):
|
|
||||||
"""
|
|
||||||
Searches for a object hit for ostring.
|
|
||||||
|
|
||||||
This version handles search criteria of the type N-keyword, this is used
|
|
||||||
to differentiate several objects of the exact same name, e.g. 1-box, 2-box etc.
|
|
||||||
|
|
||||||
ostring: (string) The string to match against.
|
|
||||||
searchlist: (List of Objects) The objects to perform name comparisons on.
|
|
||||||
if not given, will search the database normally.
|
|
||||||
attribute_name: (string) attribute name to search, if None, object key is used.
|
|
||||||
exact_match: (bool) 'exact' or 'fuzzy' matching.
|
|
||||||
|
|
||||||
Note that the fuzzy matching gives precedence to exact matches; so if your
|
|
||||||
search query matches an object in the list exactly, it will be the only result.
|
|
||||||
This means that if the list contains [box,box11,box12], the search string 'box'
|
|
||||||
will only match the first entry since it is exact. The search 'box1' will however
|
|
||||||
match both box11 and box12 since neither is an exact match.
|
|
||||||
|
|
||||||
This method always returns a list, also for a single result.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def run_dbref_search(ostring):
|
|
||||||
"dbref matching only"
|
|
||||||
dbref = self.dbref(ostring)
|
|
||||||
if searchlist:
|
|
||||||
results = [prospect for prospect in searchlist
|
|
||||||
if prospect.id == dbref]
|
|
||||||
else:
|
|
||||||
results = self.filter(id=dbref)
|
|
||||||
return results
|
|
||||||
|
|
||||||
def run_full_search(ostring, searchlist, exact_match=False):
|
|
||||||
"full matching"
|
|
||||||
if searchlist:
|
|
||||||
results = match_list(searchlist, ostring,
|
|
||||||
exact_match, attribute_name)
|
|
||||||
elif attribute_name:
|
|
||||||
results = match_list(self.all(), ostring,
|
|
||||||
exact_match, attribute_name)
|
|
||||||
elif exact_match:
|
|
||||||
results = self.filter(db_key__iexact=ostring)
|
|
||||||
else:
|
|
||||||
results = self.filter(db_key__icontains=ostring)
|
|
||||||
return results
|
|
||||||
|
|
||||||
# Easiest case - dbref matching (always exact)
|
|
||||||
if self.dbref(ostring):
|
|
||||||
results = run_dbref_search(ostring)
|
|
||||||
if results:
|
|
||||||
return results
|
|
||||||
|
|
||||||
# Full search - this may return multiple matches.
|
|
||||||
results = run_full_search(ostring, searchlist, exact_match)
|
|
||||||
|
|
||||||
# Deal with results of full search
|
|
||||||
match_number = None
|
|
||||||
if not results:
|
|
||||||
# if we have no match, check if we are dealing
|
|
||||||
# with a "N-keyword" query, if so, strip it out.
|
|
||||||
match_number, ostring = IDPARSER(ostring)
|
|
||||||
if match_number != None and ostring:
|
|
||||||
# Run the search again, without the match number
|
|
||||||
results = run_full_search(ostring, searchlist, exact_match)
|
|
||||||
|
|
||||||
elif not exact_match:
|
|
||||||
# we have results, but are using fuzzy matching; run
|
|
||||||
# second sweep in results to catch eventual exact matches
|
|
||||||
# (these are given precedence, so a search for 'ball' in
|
|
||||||
# ['ball', 'ball2'] will correctly return the first ball
|
|
||||||
# only).
|
|
||||||
exact_results = run_full_search(ostring, results, True)
|
|
||||||
if exact_results:
|
|
||||||
results = exact_results
|
|
||||||
|
|
||||||
if len(results) > 1 and match_number != None:
|
|
||||||
# We have multiple matches, but a N-type match number
|
|
||||||
# is available to separate them.
|
|
||||||
try:
|
|
||||||
results = [results[match_number]]
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
# this is always a list.
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
@returns_typeclass_list
|
@returns_typeclass_list
|
||||||
def object_search(self, character, ostring,
|
def object_search(self, character, ostring,
|
||||||
global_search=False,
|
global_search=False,
|
||||||
|
|
@ -248,59 +245,68 @@ class ObjectManager(TypedObjectManager):
|
||||||
Can be a dbref. If name is appended by *, a player is searched for.
|
Can be a dbref. If name is appended by *, a player is searched for.
|
||||||
global_search: Search all objects, not just the current location/inventory
|
global_search: Search all objects, not just the current location/inventory
|
||||||
attribute_name: (string) Which attribute to search in each object.
|
attribute_name: (string) Which attribute to search in each object.
|
||||||
If None, the default 'name' attribute is used.
|
If None, the default 'key' attribute is used.
|
||||||
"""
|
"""
|
||||||
ostring = str(ostring).strip()
|
#ostring = str(ostring).strip()
|
||||||
|
|
||||||
if not ostring or not character:
|
if not ostring or not character:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
location = character.location
|
||||||
|
|
||||||
|
|
||||||
|
# Easiest case - dbref matching (always exact)
|
||||||
dbref = self.dbref(ostring)
|
dbref = self.dbref(ostring)
|
||||||
if dbref:
|
if dbref:
|
||||||
# this is a valid dbref. If it matches, we return directly.
|
|
||||||
dbref_match = self.dbref_search(dbref)
|
dbref_match = self.dbref_search(dbref)
|
||||||
if dbref_match:
|
if dbref_match:
|
||||||
return [dbref_match]
|
return [dbref_match]
|
||||||
|
|
||||||
location = character.location
|
# not a dbref. Search by attribute/property.
|
||||||
|
|
||||||
# If the search string is one of the following, return immediately with
|
if not attribute_name:
|
||||||
# the appropriate result.
|
# If the search string is one of the following, return immediately with
|
||||||
if location and ostring == 'here':
|
# the appropriate result.
|
||||||
return [location]
|
if location and ostring == 'here':
|
||||||
|
return [location]
|
||||||
|
if character and ostring in ['me', 'self']:
|
||||||
|
return [character]
|
||||||
|
if character and ostring in ['*me', '*self']:
|
||||||
|
return [character.player]
|
||||||
|
|
||||||
if character and ostring in ['me', 'self']:
|
attribute_name = 'key'
|
||||||
return [character]
|
|
||||||
if character and ostring in ['*me', '*self']:
|
|
||||||
return [character.player]
|
|
||||||
|
|
||||||
if ostring.startswith("*"):
|
if str(ostring).startswith("*"):
|
||||||
# Player search - search player base
|
# Player search - try to find obj by its player's name
|
||||||
player_string = ostring.lstrip("*")
|
player_string = ostring.lstrip("*")
|
||||||
player_match = self.player_name_search(player_string)
|
player_match = self.get_obj_with_player(player_string)
|
||||||
if player_match is not None:
|
if player_match is not None:
|
||||||
return [player_match]
|
return [player_match]
|
||||||
|
|
||||||
if global_search or not location:
|
# find suitable objects
|
||||||
# search all objects
|
|
||||||
return self.separable_search(ostring, None,
|
if global_search or not location:
|
||||||
attribute_name)
|
# search all objects in database
|
||||||
|
objlist = self.get_objs_with_db_property(attribute_name)
|
||||||
|
if not objlist:
|
||||||
|
objlist = self.get_objs_with_attr(attribute_name)
|
||||||
|
else:
|
||||||
|
# local search
|
||||||
|
objlist = character.contents
|
||||||
|
objlist.extend(location.contents)
|
||||||
|
objlist.append(location) #easy to forget!
|
||||||
|
if not objlist:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# do the search on the found objects
|
||||||
|
matches = separable_search(ostring, objlist,
|
||||||
|
attribute_name, exact_match=False)
|
||||||
|
|
||||||
|
if not matches and attribute_name in ('key', 'name'):
|
||||||
|
# No matches. If we tried to match a key/name field, we also try to
|
||||||
|
# see if an alias works better.
|
||||||
|
matches = self.alias_list_search(ostring, objlist)
|
||||||
|
|
||||||
# None of the above cases yielded a return, so we fall through to
|
|
||||||
# location/contents searches.
|
|
||||||
matches = []
|
|
||||||
local_objs = []
|
|
||||||
local_objs.extend(character.contents)
|
|
||||||
local_objs.extend(location.contents)
|
|
||||||
local_objs.append(location) #easy to forget!
|
|
||||||
if local_objs:
|
|
||||||
# normal key/attribute search (typedobject_search is
|
|
||||||
# found in class parent)
|
|
||||||
matches = self.separable_search(ostring, local_objs,
|
|
||||||
attribute_name, exact_match=False)
|
|
||||||
if not matches:
|
|
||||||
# no match, try an alias search
|
|
||||||
matches = self.alias_list_search(ostring, local_objs)
|
|
||||||
return matches
|
return matches
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -418,8 +418,8 @@ class ObjectDB(TypedObject):
|
||||||
(if None, uses default 'name')
|
(if None, uses default 'name')
|
||||||
use_nicks : Use nickname replace (off by default)
|
use_nicks : Use nickname replace (off by default)
|
||||||
ignore_errors : Don't display any error messages even
|
ignore_errors : Don't display any error messages even
|
||||||
if there are none/multiple matches -
|
if there are none/multiple matches -
|
||||||
just return the result as a list.
|
just return the result as a list.
|
||||||
|
|
||||||
Note - for multiple matches, the engine accepts a number
|
Note - for multiple matches, the engine accepts a number
|
||||||
linked to the key in order to separate the matches from
|
linked to the key in order to separate the matches from
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,8 @@ def object_multimatch_parser(ostring):
|
||||||
that the engine assumes this number to start with 1 (i.e. not
|
that the engine assumes this number to start with 1 (i.e. not
|
||||||
zero as in normal Python).
|
zero as in normal Python).
|
||||||
"""
|
"""
|
||||||
|
if not isinstance(ostring, basestring):
|
||||||
|
return (None, ostring)
|
||||||
if not '-' in ostring:
|
if not '-' in ostring:
|
||||||
return (None, ostring)
|
return (None, ostring)
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ class AttributeManager(models.Manager):
|
||||||
return self.filter(db_obj=obj).filter(
|
return self.filter(db_obj=obj).filter(
|
||||||
db_key__icontains=searchstr)
|
db_key__icontains=searchstr)
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# helper functions for the TypedObjectManager.
|
# helper functions for the TypedObjectManager.
|
||||||
#
|
#
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue