evennia/docs/0.x/Tutorial-Searching-For-Objects.html

528 lines
53 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Tutorial Searching For Objects &#8212; Evennia 0.9.5 documentation</title>
<link rel="stylesheet" href="_static/nature.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="./" src="_static/documentation_options.js"></script>
<script src="_static/jquery.js"></script>
<script src="_static/underscore.js"></script>
<script src="_static/doctools.js"></script>
<script src="_static/language_data.js"></script>
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/x-mathjax-config">MathJax.Hub.Config({"tex2jax": {"processClass": "tex2jax_process|mathjax_process|math|output_area"}})</script>
<link rel="shortcut icon" href="_static/favicon.ico"/>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
</head><body>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Tutorial Searching For Objects</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="tutorial-searching-for-objects">
<h1>Tutorial Searching For Objects<a class="headerlink" href="#tutorial-searching-for-objects" title="Permalink to this headline"></a></h1>
<p>You will often want to operate on a specific object in the database. For example when a player
attacks a named target youll need to find that target so it can be attacked. Or when a rain storm
draws in you need to find all outdoor-rooms so you can show it raining in them. This tutorial
explains Evennias tools for searching.</p>
<section id="things-to-search-for">
<h2>Things to search for<a class="headerlink" href="#things-to-search-for" title="Permalink to this headline"></a></h2>
<p>The first thing to consider is the base type of the thing you are searching for. Evennia organizes
its database into a few main tables: <a class="reference internal" href="Objects.html"><span class="doc std std-doc">Objects</span></a>, <a class="reference internal" href="Accounts.html"><span class="doc std std-doc">Accounts</span></a>, <a class="reference internal" href="Scripts.html"><span class="doc std std-doc">Scripts</span></a>,
<a class="reference internal" href="Communications.html#channels"><span class="std std-doc">Channels</span></a>, <a class="reference internal" href="Communications.html#msg"><span class="std std-doc">Messages</span></a> and <a class="reference internal" href="Help-System.html"><span class="doc std std-doc">Help Entries</span></a>.
Most of the time youll likely spend your time searching for Objects and the occasional Accounts.</p>
<p>So to find an entity, what can be searched for?</p>
<ul class="simple">
<li><p>The <code class="docutils literal notranslate"><span class="pre">key</span></code> is the name of the entity. While you can get this from <code class="docutils literal notranslate"><span class="pre">obj.key</span></code> the <em>database field</em>
is actually named <code class="docutils literal notranslate"><span class="pre">obj.db_key</span></code> - this is useful to know only when you do <a class="reference internal" href="#queries-in-django"><span class="std std-doc">direct database
queries</span></a>. The one exception is <code class="docutils literal notranslate"><span class="pre">Accounts</span></code>, where
the database field for <code class="docutils literal notranslate"><span class="pre">.key</span></code> is instead named <code class="docutils literal notranslate"><span class="pre">username</span></code> (this is a Django requirement). When you
dont specify search-type, youll usually search based on key. <em>Aliases</em> are extra names given to
Objects using something like <code class="docutils literal notranslate"><span class="pre">&#64;alias</span></code> or <code class="docutils literal notranslate"><span class="pre">obj.aliases.add('name')</span></code>. The main search functions (see
below) will automatically search for aliases whenever you search by-key.</p></li>
<li><p><a class="reference internal" href="Tags.html"><span class="doc std std-doc">Tags</span></a> are the main way to group and identify objects in Evennia. Tags can most often be
used (sometimes together with keys) to uniquely identify an object. For example, even though you
have two locations with the same name, you can separate them by their tagging (this is how Evennia
implements zones seen in other systems). Tags can also have categories, to further organize your
data for quick lookups.</p></li>
<li><p>An objects <a class="reference internal" href="Attributes.html"><span class="doc std std-doc">Attributes</span></a> can also used to find an object. This can be very useful but
since Attributes can store almost any data they are far less optimized to search for than Tags or
keys.</p></li>
<li><p>The objects <a class="reference internal" href="Typeclasses.html"><span class="doc std std-doc">Typeclass</span></a> indicate the sub-type of entity. A Character, Flower or
Sword are all types of Objects. A Bot is a kind of Account. The database field is called
<code class="docutils literal notranslate"><span class="pre">typeclass_path</span></code> and holds the full Python-path to the class. You can usually specify the
<code class="docutils literal notranslate"><span class="pre">typeclass</span></code> as an argument to Evennias search functions as well as use the class directly to limit
queries.</p></li>
<li><p>The <code class="docutils literal notranslate"><span class="pre">location</span></code> is only relevant for <a class="reference internal" href="Objects.html"><span class="doc std std-doc">Objects</span></a> but is a very common way to weed down the
number of candidates before starting to search. The reason is that most in-game commands tend to
operate on things nearby (in the same room) so the choices can be limited from the start.</p></li>
<li><p>The database id or the #dbref is unique (and never re-used) within each database table. So while
there is one and only one Object with dbref <code class="docutils literal notranslate"><span class="pre">#42</span></code> there could also be an Account or Script with the
dbref <code class="docutils literal notranslate"><span class="pre">#42</span></code> at the same time. In almost all search methods you can replace the “key” search
criterion with <code class="docutils literal notranslate"><span class="pre">&quot;#dbref&quot;</span></code> to search for that id. This can occasionally be practical and may be what
you are used to from other code bases. But it is considered <em>bad practice</em> in Evennia to rely on
hard-coded #dbrefs to do your searches. It makes your code tied to the exact layout of the database.
Its also not very maintainable to have to remember abstract numbers. Passing the actual objects
around and searching by Tags and/or keys will usually get you what you need.</p></li>
</ul>
</section>
<section id="getting-objects-inside-another">
<h2>Getting objects inside another<a class="headerlink" href="#getting-objects-inside-another" title="Permalink to this headline"></a></h2>
<p>All in-game <a class="reference internal" href="Objects.html"><span class="doc std std-doc">Objects</span></a> have a <code class="docutils literal notranslate"><span class="pre">.contents</span></code> property that returns all objects inside them
(that is, all objects which has its <code class="docutils literal notranslate"><span class="pre">.location</span></code> property set to that object. This is a simple way to
get everything in a room and is also faster since this lookup is cached and wont hit the database.</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">roomobj.contents</span></code> returns a list of all objects inside <code class="docutils literal notranslate"><span class="pre">roomobj</span></code>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj.contents</span></code> same as for a room, except this usually represents the objects inventory</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj.location.contents</span></code> gets everything in <code class="docutils literal notranslate"><span class="pre">obj</span></code>s location (including <code class="docutils literal notranslate"><span class="pre">obj</span></code> itself).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">roomobj.exits</span></code> returns all exits starting from <code class="docutils literal notranslate"><span class="pre">roomobj</span></code> (Exits are here defined as Objects with
their <code class="docutils literal notranslate"><span class="pre">destination</span></code> field set).</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">obj.location.contents_get(exclude=obj)</span></code> - this helper method returns all objects in <code class="docutils literal notranslate"><span class="pre">obj</span></code>s
location except <code class="docutils literal notranslate"><span class="pre">obj</span></code>.</p></li>
</ul>
</section>
<section id="searching-using-object-search">
<h2>Searching using <code class="docutils literal notranslate"><span class="pre">Object.search</span></code><a class="headerlink" href="#searching-using-object-search" title="Permalink to this headline"></a></h2>
<p>Say you have a <a class="reference internal" href="Commands.html"><span class="doc std std-doc">command</span></a>, and you want it to do something to a target. You might be
wondering how you retrieve that target in code, and thats where Evennias search utilities come in.
In the most common case, youll often use the <code class="docutils literal notranslate"><span class="pre">search</span></code> method of the <code class="docutils literal notranslate"><span class="pre">Object</span></code> or <code class="docutils literal notranslate"><span class="pre">Account</span></code>
typeclasses. In a command, the <code class="docutils literal notranslate"><span class="pre">.caller</span></code> property will refer back to the object using the command
(usually a <code class="docutils literal notranslate"><span class="pre">Character</span></code>, which is a type of <code class="docutils literal notranslate"><span class="pre">Object</span></code>) while <code class="docutils literal notranslate"><span class="pre">.args</span></code> will contain Commands arguments:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># e.g. in file mygame/commands/command.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
<span class="k">class</span> <span class="nc">CmdPoke</span><span class="p">(</span><span class="n">Command</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;</span>
<span class="sd"> Pokes someone.</span>
<span class="sd"> Usage: poke &lt;target&gt;</span>
<span class="sd"> &quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;poke&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Executes poke command&quot;&quot;&quot;</span>
<span class="n">target</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">args</span><span class="o">.</span><span class="n">lstrip</span><span class="p">())</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">target</span><span class="p">:</span>
<span class="c1"># we didn&#39;t find anyone, but search has already let the</span>
<span class="c1"># caller know. We&#39;ll just return, since we&#39;re done</span>
<span class="k">return</span>
<span class="c1"># we found a target! we&#39;ll do stuff to them.</span>
<span class="n">target</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;</span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="si">}</span><span class="s2"> pokes you.&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;You poke </span><span class="si">{</span><span class="n">target</span><span class="si">}</span><span class="s2">.&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>By default, the search method of a Character will attempt to find a unique object match for the
string sent to it (<code class="docutils literal notranslate"><span class="pre">self.args</span></code>, in this case, which is the arguments passed to the command by the
player) in the surroundings of the Character - the room or their inventory. If there is no match
found, the return value (which is assigned to <code class="docutils literal notranslate"><span class="pre">target</span></code>) will be <code class="docutils literal notranslate"><span class="pre">None</span></code>, and an appropriate failure
message will be sent to the Character. If theres not a unique match, <code class="docutils literal notranslate"><span class="pre">None</span></code> will again be returned,
and a different error message will be sent asking them to disambiguate the multi-match. By default,
the user can then pick out a specific match using with a number and dash preceding the name of the
object: <code class="docutils literal notranslate"><span class="pre">character.search(&quot;2-pink</span> <span class="pre">unicorn&quot;)</span></code> will try to find the second pink unicorn in the room.</p>
<p>The search method has many <a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia.objects.objects#defaultcharactersearch">arguments</a> that
allow you to refine the search, such as by designating the location to search in or only matching
specific typeclasses.</p>
</section>
<section id="searching-using-utils-search">
<h2>Searching using <code class="docutils literal notranslate"><span class="pre">utils.search</span></code><a class="headerlink" href="#searching-using-utils-search" title="Permalink to this headline"></a></h2>
<p>Sometimes you will want to find something that isnt tied to the search methods of a character or
account. In these cases, Evennia provides a <a class="reference external" href="https://github.com/evennia/evennia/blob/master/evennia.utils.search">utility module with a number of search
functions</a>. For example, suppose you want a command that will find and
display all the rooms that are tagged as a hangout, for people to gather by. Heres a simple
Command to do this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># e.g. in file mygame/commands/command.py</span>
<span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">default_cmds</span>
<span class="kn">from</span> <span class="nn">evennia.utils.search</span> <span class="kn">import</span> <span class="n">search_tag</span>
<span class="k">class</span> <span class="nc">CmdListHangouts</span><span class="p">(</span><span class="n">default_cmds</span><span class="o">.</span><span class="n">MuxCommand</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Lists hangouts&quot;&quot;&quot;</span>
<span class="n">key</span> <span class="o">=</span> <span class="s2">&quot;hangouts&quot;</span>
<span class="k">def</span> <span class="nf">func</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="sd">&quot;&quot;&quot;Executes &#39;hangouts&#39; command&quot;&quot;&quot;</span>
<span class="n">hangouts</span> <span class="o">=</span> <span class="n">search_tag</span><span class="p">(</span><span class="n">key</span><span class="o">=</span><span class="s2">&quot;hangout&quot;</span><span class="p">,</span> <span class="n">category</span><span class="o">=</span><span class="s2">&quot;location tags&quot;</span><span class="p">)</span>
<span class="bp">self</span><span class="o">.</span><span class="n">caller</span><span class="o">.</span><span class="n">msg</span><span class="p">(</span><span class="sa">f</span><span class="s2">&quot;Hangouts available: </span><span class="si">{</span><span class="s1">&#39;, &#39;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">ob</span><span class="p">)</span> <span class="k">for</span> <span class="n">ob</span> <span class="ow">in</span> <span class="n">hangouts</span><span class="p">)</span><span class="si">}</span><span class="s2">&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>This uses the <code class="docutils literal notranslate"><span class="pre">search_tag</span></code> function to find all objects previously tagged with <a class="reference internal" href="Tags.html"><span class="doc std std-doc">Tags</span></a>
“hangout” and with category “location tags”.</p>
<p>Other important search methods in <code class="docutils literal notranslate"><span class="pre">utils.search</span></code> are</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">search_object</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_account</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_scripts</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_channel</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_message</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_help</span></code></p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_tag</span></code> - find Objects with a given Tag. <a class="reference internal" href="Tags.html#searching-for-objects-with-a-given-tag"><span class="std std-doc">See also here for how to search by
tag</span></a>.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_account_tag</span></code> - find Accounts with a given Tag.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_script_tag</span></code> - find Scripts with a given Tag.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_channel_tag</span></code> - find Channels with a given Tag.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_object_attribute</span></code> - find Objects with a given Attribute.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_account_attribute</span></code> - find Accounts with a given Attribute.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">search_attribute_object</span></code> - this returns the actual Attribute, not the object it sits on.</p></li>
</ul>
<blockquote>
<div><p>Note: All search functions return a Django <code class="docutils literal notranslate"><span class="pre">queryset</span></code> which is technically a list-like
representation of the database-query its about to do. Only when you convert it to a real list, loop
over it or try to slice or access any of its contents will the datbase-lookup happen. This means you
could yourself customize the query further if you know what you are doing (see the next section).</p>
</div></blockquote>
</section>
<section id="queries-in-django">
<h2>Queries in Django<a class="headerlink" href="#queries-in-django" title="Permalink to this headline"></a></h2>
<p><em>This is an advanced topic.</em></p>
<p>Evennias search methods should be sufficient for the vast majority of situations. But eventually
you might find yourself trying to figure out how to get searches for unusual circumstances: Maybe
you want to find all characters who are <em>not</em> in rooms tagged as hangouts <em>and</em> have the lycanthrope
tag <em>and</em> whose names start with a vowel, but <em>not</em> with Ab, and <em>only if</em> they have 3 or more
objects in their inventory … You could in principle use one of the earlier search methods to find
all candidates and then loop over them with a lot of if statements in raw Python. But you can do
this much more efficiently by querying the database directly.</p>
<p>Enter <a class="reference external" href="https://docs.djangoproject.com/en/1.11/ref/models/querysets/">djangos querysets</a>. A QuerySet
is the representation of a database query and can be modified as desired. Only once one tries to
retrieve the data of that query is it <em>evaluated</em> and does an actual database request. This is
useful because it means you can modify a query as much as you want (even pass it around) and only
hit the database once you are happy with it.
Evennias search functions are themselves an even higher level wrapper around Djangos queries, and
many search methods return querysets. That means that you could get the result from a search
function and modify the resulting query to your own ends to further tweak what you search for.</p>
<p>Evaluated querysets can either contain objects such as Character objects, or lists of values derived
from the objects. Queries usually use the manager object of a class, which by convention is the
<code class="docutils literal notranslate"><span class="pre">.objects</span></code> attribute of a class. For example, a query of Accounts that contain the letter a could
be:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span> <span class="kn">from</span> <span class="nn">typeclasses.accounts</span> <span class="kn">import</span> <span class="n">Account</span>
<span class="n">queryset</span> <span class="o">=</span> <span class="n">Account</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">username__contains</span><span class="o">=</span><span class="s1">&#39;a&#39;</span><span class="p">)</span>
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">filter</span></code> method of a manager takes arguments that allow you to define the query, and you can
continue to refine the query by calling additional methods until you evaluate the queryset, causing
the query to be executed and return a result. For example, if you have the result above, you could,
without causing the queryset to be evaluated yet, get rid of matches that contain the letter e by
doing this:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">queryset</span> <span class="o">=</span> <span class="n">result</span><span class="o">.</span><span class="n">exclude</span><span class="p">(</span><span class="n">username__contains</span><span class="o">=</span><span class="s1">&#39;e&#39;</span><span class="p">)</span>
</pre></div>
</div>
<blockquote>
<div><p>You could also have chained <code class="docutils literal notranslate"><span class="pre">.exclude</span></code> directly to the end of the previous line.</p>
</div></blockquote>
<p>Once you try to access the result, the queryset will be evaluated automatically under the hood:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">accounts</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">queryset</span><span class="p">)</span> <span class="c1"># this fills list with matches</span>
<span class="k">for</span> <span class="n">account</span> <span class="ow">in</span> <span class="n">queryset</span><span class="p">:</span>
<span class="c1"># do something with account</span>
<span class="n">accounts</span> <span class="o">=</span> <span class="n">queryset</span><span class="p">[:</span><span class="mi">4</span><span class="p">]</span> <span class="c1"># get first four matches</span>
<span class="n">account</span> <span class="o">=</span> <span class="n">queryset</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="c1"># get first match</span>
<span class="c1"># etc</span>
</pre></div>
</div>
<section id="limiting-by-typeclass">
<h3>Limiting by typeclass<a class="headerlink" href="#limiting-by-typeclass" title="Permalink to this headline"></a></h3>
<p>Although <code class="docutils literal notranslate"><span class="pre">Character</span></code>s, <code class="docutils literal notranslate"><span class="pre">Exit</span></code>s, <code class="docutils literal notranslate"><span class="pre">Room</span></code>s, and other children of <code class="docutils literal notranslate"><span class="pre">DefaultObject</span></code> all shares the same
underlying database table, Evennia provides a shortcut to do more specific queries only for those
typeclasses. For example, to find only <code class="docutils literal notranslate"><span class="pre">Character</span></code>s whose names start with A, you might do:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">Character</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__startswith</span><span class="o">=</span><span class="s2">&quot;A&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>If Character has a subclass <code class="docutils literal notranslate"><span class="pre">Npc</span></code> and you wanted to find only Npcs youd instead do</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">Npc</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__startswith</span><span class="o">=</span><span class="s2">&quot;A&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>If you wanted to search both Characters and all its subclasses (like Npc) you use the <code class="docutils literal notranslate"><span class="pre">*_family</span></code>
method which is added by Evennia:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">Character</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter_family</span><span class="p">(</span><span class="n">db_key__startswith</span><span class="o">=</span><span class="s2">&quot;A&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>The higher up in the inheritance hierarchy you go the more objects will be included in these
searches. There is one special case, if you really want to include <em>everything</em> from a given
database table. You do that by searching on the database model itself. These are named <code class="docutils literal notranslate"><span class="pre">ObjectDB</span></code>,
<code class="docutils literal notranslate"><span class="pre">AccountDB</span></code>, <code class="docutils literal notranslate"><span class="pre">ScriptDB</span></code> etc.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">evennia</span> <span class="kn">import</span> <span class="n">AccountDB</span>
<span class="c1"># all Accounts in the database, regardless of typeclass</span>
<span class="nb">all</span> <span class="o">=</span> <span class="n">AccountDB</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
</pre></div>
</div>
<p>Here are the most commonly used methods to use with the <code class="docutils literal notranslate"><span class="pre">objects</span></code> managers:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">filter</span></code> - query for a listing of objects based on search criteria. Gives empty queryset if none
were found.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">get</span></code> - query for a single match - raises exception if none were found, or more than one was
found.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">all</span></code> - get all instances of the particular type.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">filter_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">filter</span></code>, but search all sub classes as well.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">get_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">get</span></code>, but search all sub classes as well.</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">all_family</span></code> - like <code class="docutils literal notranslate"><span class="pre">all</span></code>, but return entities of all subclasses as well.</p></li>
</ul>
</section>
</section>
<section id="multiple-conditions">
<h2>Multiple conditions<a class="headerlink" href="#multiple-conditions" title="Permalink to this headline"></a></h2>
<p>If you pass more than one keyword argument to a query method, the query becomes an <code class="docutils literal notranslate"><span class="pre">AND</span></code>
relationship. For example, if we want to find characters whose names start with “A” <em>and</em> are also
werewolves (have the <code class="docutils literal notranslate"><span class="pre">lycanthrope</span></code> tag), we might do:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">queryset</span> <span class="o">=</span> <span class="n">Character</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_key__startswith</span><span class="o">=</span><span class="s2">&quot;A&quot;</span><span class="p">,</span> <span class="n">db_tags__db_key</span><span class="o">=</span><span class="s2">&quot;lycanthrope&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>To exclude lycanthropes currently in rooms tagged as hangouts, we might tack on an <code class="docutils literal notranslate"><span class="pre">.exclude</span></code> as
before:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">queryset</span> <span class="o">=</span> <span class="n">quersyet</span><span class="o">.</span><span class="n">exclude</span><span class="p">(</span><span class="n">db_location__db_tags__db_key</span><span class="o">=</span><span class="s2">&quot;hangout&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>Note the syntax of the keywords in building the queryset. For example, <code class="docutils literal notranslate"><span class="pre">db_location</span></code> is the name of
the database field sitting on (in this case) the <code class="docutils literal notranslate"><span class="pre">Character</span></code> (Object). Double underscore <code class="docutils literal notranslate"><span class="pre">__</span></code> works
like dot-notation in normal Python (its used since dots are not allowed in keyword names). So the
instruction <code class="docutils literal notranslate"><span class="pre">db_location__db_tags__db_key=&quot;hangout&quot;</span></code> should be read as such:</p>
<ol class="simple">
<li><p>“On the <code class="docutils literal notranslate"><span class="pre">Character</span></code> object … (this comes from us building this queryset using the
<code class="docutils literal notranslate"><span class="pre">Character.objects</span></code> manager)</p></li>
<li><p>… get the value of the <code class="docutils literal notranslate"><span class="pre">db_location</span></code> field … (this references a Room object, normally)</p></li>
<li><p>… on that location, get the value of the <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> field … (this is a many-to-many field that
can be treated like an object for this purpose. It references all tags on the location)</p></li>
<li><p>… through the <code class="docutils literal notranslate"><span class="pre">db_tag</span></code> manager, find all Tags having a field <code class="docutils literal notranslate"><span class="pre">db_key</span></code> set to the value
“hangout”.”</p></li>
</ol>
<p>This may seem a little complex at first, but this syntax will work the same for all queries. Just
remember that all <em>database-fields</em> in Evennia are prefaced with <code class="docutils literal notranslate"><span class="pre">db_</span></code>. So even though Evennia is
nice enough to alias the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> field so you can normally just do <code class="docutils literal notranslate"><span class="pre">char.key</span></code> to get a characters
name, the database field is actually called <code class="docutils literal notranslate"><span class="pre">db_key</span></code> and the real name must be used for the purpose
of building a query.</p>
<blockquote>
<div><p>Dont confuse database fields with <a class="reference internal" href="Attributes.html"><span class="doc std std-doc">Attributes</span></a> you set via <code class="docutils literal notranslate"><span class="pre">obj.db.attr</span> <span class="pre">=</span> <span class="pre">'foo'</span></code> or
<code class="docutils literal notranslate"><span class="pre">obj.attributes.add()</span></code>. Attributes are custom database entities <em>linked</em> to an object. They are not
separate fields <em>on</em> that object like <code class="docutils literal notranslate"><span class="pre">db_key</span></code> or <code class="docutils literal notranslate"><span class="pre">db_location</span></code> are. You can get attached Attributes
manually through the <code class="docutils literal notranslate"><span class="pre">db_attributes</span></code> many-to-many field in the same way as <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> above.</p>
</div></blockquote>
<section id="complex-queries">
<h3>Complex queries<a class="headerlink" href="#complex-queries" title="Permalink to this headline"></a></h3>
<p>What if you want to have a query with with <code class="docutils literal notranslate"><span class="pre">OR</span></code> conditions or negated requirements (<code class="docutils literal notranslate"><span class="pre">NOT</span></code>)? Enter
Djangos Complex Query object,
<a class="reference external" href="https://docs.djangoproject.com/en/1.11/topics/db/queries/#complex-lookups-with-q-objects">Q</a>. <code class="docutils literal notranslate"><span class="pre">Q()</span></code>
objects take a normal django keyword query as its arguments. The special thing is that these Q
objects can then be chained together with set operations: <code class="docutils literal notranslate"><span class="pre">|</span></code> for OR, <code class="docutils literal notranslate"><span class="pre">&amp;</span></code> for AND, and preceded with
<code class="docutils literal notranslate"><span class="pre">~</span></code> for NOT to build a combined, complex query.</p>
<p>In our original Lycanthrope example we wanted our werewolves to have names that could start with any
vowel except for the specific beginning “ab”.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Q</span>
<span class="kn">from</span> <span class="nn">typeclasses.characters</span> <span class="kn">import</span> <span class="n">Character</span>
<span class="n">query</span> <span class="o">=</span> <span class="n">Q</span><span class="p">()</span>
<span class="k">for</span> <span class="n">letter</span> <span class="ow">in</span> <span class="p">(</span><span class="s2">&quot;aeiouy&quot;</span><span class="p">):</span>
<span class="n">query</span> <span class="o">|=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_key__istartswith</span><span class="o">=</span><span class="n">letter</span><span class="p">)</span>
<span class="n">query</span> <span class="o">&amp;=</span> <span class="o">~</span><span class="n">Q</span><span class="p">(</span><span class="n">db_key__istartswith</span><span class="o">=</span><span class="s2">&quot;ab&quot;</span><span class="p">)</span>
<span class="n">query</span> <span class="o">=</span> <span class="n">Character</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
<span class="n">list_of_lycanthropes</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">query</span><span class="p">)</span>
</pre></div>
</div>
<p>In the above example, we construct our query our of several Q objects that each represent one part
of the query. We iterate over the list of vowels, and add an <code class="docutils literal notranslate"><span class="pre">OR</span></code> condition to the query using <code class="docutils literal notranslate"><span class="pre">|=</span></code>
(this is the same idea as using <code class="docutils literal notranslate"><span class="pre">+=</span></code> which may be more familiar). Each <code class="docutils literal notranslate"><span class="pre">OR</span></code> condition checks that
the name starts with one of the valid vowels. Afterwards, we add (using <code class="docutils literal notranslate"><span class="pre">&amp;=</span></code>) an <code class="docutils literal notranslate"><span class="pre">AND</span></code> condition
that is negated with the <code class="docutils literal notranslate"><span class="pre">~</span></code> symbol. In other words we require that any match should <em>not</em> start
with the string “ab”. Note that we dont actually hit the database until we convert the query to a
list at the end (we didnt need to do that either, but could just have kept the query until we
needed to do something with the matches).</p>
</section>
<section id="annotations-and-f-objects">
<h3>Annotations and <code class="docutils literal notranslate"><span class="pre">F</span></code> objects<a class="headerlink" href="#annotations-and-f-objects" title="Permalink to this headline"></a></h3>
<p>What if we wanted to filter on some condition that isnt represented easily by a field on the
object? Maybe we want to find rooms only containing five or more objects?</p>
<p>We <em>could</em> retrieve all interesting candidates and run them through a for-loop to get and count
their <code class="docutils literal notranslate"><span class="pre">.content</span></code> properties. Wed then just return a list of only those objects with enough
contents. It would look something like this (note: dont actually do this!):</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="c1"># probably not a good idea to do it this way</span>
<span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
<span class="n">queryset</span> <span class="o">=</span> <span class="n">Room</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span> <span class="c1"># get all Rooms</span>
<span class="n">rooms</span> <span class="o">=</span> <span class="p">[</span><span class="n">room</span> <span class="k">for</span> <span class="n">room</span> <span class="ow">in</span> <span class="n">queryset</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">room</span><span class="o">.</span><span class="n">contents</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">]</span>
</pre></div>
</div>
<p>Once the number of rooms in your game increases, this could become quite expensive. Additionally, in
some particular contexts, like when using the web features of Evennia, you must have the result as a
queryset in order to use it in operations, such as in Djangos admin interface when creating list
filters.</p>
<p>Enter <a class="reference external" href="https://docs.djangoproject.com/en/1.11/ref/models/expressions/#f-expressions">F objects</a> and
<em>annotations</em>. So-called F expressions allow you to do a query that looks at a value of each object
in the database, while annotations allow you to calculate and attach a value to a query. So, lets
do the same example as before directly in the database:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
<span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Count</span>
<span class="n">room_count</span> <span class="o">=</span> <span class="n">Room</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span><span class="n">num_objects</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">&#39;locations_set&#39;</span><span class="p">))</span>
<span class="n">queryset</span> <span class="o">=</span> <span class="n">room_count</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">num_objects__gte</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span>
<span class="n">rooms</span> <span class="o">=</span> <span class="p">(</span><span class="n">Room</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span><span class="n">num_objects</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">&#39;locations_set&#39;</span><span class="p">))</span>
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">num_objects__gte</span><span class="o">=</span><span class="mi">5</span><span class="p">))</span>
<span class="n">rooms</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">rooms</span><span class="p">)</span>
</pre></div>
</div>
<p>Here we first create an annotation <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> of type <code class="docutils literal notranslate"><span class="pre">Count</span></code>, which is a Django class. Note that
use of <code class="docutils literal notranslate"><span class="pre">location_set</span></code> in that <code class="docutils literal notranslate"><span class="pre">Count</span></code>. The <code class="docutils literal notranslate"><span class="pre">*_set</span></code> is a back-reference automatically created by
Django. In this case it allows you to find all objects that <em>has the current object as location</em>.
Once we have those, they are counted.
Next we filter on this annotation, using the name <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> as something we can filter for. We
use <code class="docutils literal notranslate"><span class="pre">num_objects__gte=5</span></code> which means that <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> should be greater than 5. This is a little
harder to get ones head around but much more efficient than lopping over all objects in Python.</p>
<p>What if we wanted to compare two parameters against one another in a query? For example, what if
instead of having 5 or more objects, we only wanted objects that had a bigger inventory than they
had tags? Here an F-object comes in handy:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">django.db.models</span> <span class="kn">import</span> <span class="n">Count</span><span class="p">,</span> <span class="n">F</span>
<span class="kn">from</span> <span class="nn">typeclasses.rooms</span> <span class="kn">import</span> <span class="n">Room</span>
<span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">Room</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">annotate</span><span class="p">(</span><span class="n">num_objects</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">&#39;locations_set&#39;</span><span class="p">),</span>
<span class="n">num_tags</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">&#39;db_tags&#39;</span><span class="p">))</span>
<span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">num_objects__gt</span><span class="o">=</span><span class="n">F</span><span class="p">(</span><span class="s1">&#39;num_tags&#39;</span><span class="p">)))</span>
</pre></div>
</div>
<p>F-objects allows for wrapping an annotated structure on the right-hand-side of the expression. It
will be evaluated on-the-fly as needed.</p>
</section>
<section id="grouping-by-and-values">
<h3>Grouping By and Values<a class="headerlink" href="#grouping-by-and-values" title="Permalink to this headline"></a></h3>
<p>Suppose you used tags to mark someone belonging an organization. Now you want to make a list and
need to get the membership count of every organization all at once. Thats where annotations and the
<code class="docutils literal notranslate"><span class="pre">.values_list</span></code> queryset method come in. Values/Values Lists are an alternate way of returning a
queryset - instead of objects, you get a list of dicts or tuples that hold selected properties from
the the matches. It also allows you a way to group up queries for returning information. For
example, to get a display about each tag per Character and the names of the tag:</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">result</span> <span class="o">=</span> <span class="p">(</span><span class="n">Character</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">filter</span><span class="p">(</span><span class="n">db_tags__db_category</span><span class="o">=</span><span class="s2">&quot;organization&quot;</span><span class="p">)</span>
<span class="o">.</span><span class="n">values_list</span><span class="p">(</span><span class="s1">&#39;db_tags__db_key&#39;</span><span class="p">)</span>
<span class="o">.</span><span class="n">annotate</span><span class="p">(</span><span class="n">cnt</span><span class="o">=</span><span class="n">Count</span><span class="p">(</span><span class="s1">&#39;id&#39;</span><span class="p">))</span>
<span class="o">.</span><span class="n">order_by</span><span class="p">(</span><span class="s1">&#39;-cnt&#39;</span><span class="p">))</span>
</pre></div>
</div>
<p>The result queryset will be a list of tuples ordered in descending order by the number of matches,
in a format like the following:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[(</span><span class="s1">&#39;Griatch Fanclub&#39;</span><span class="p">,</span> <span class="mi">3872</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;Chainsol&#39;s Ainneve Testers&quot;</span><span class="p">,</span> <span class="mi">2076</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;Blaufeuer&#39;s Whitespace Fixers&quot;</span><span class="p">,</span>
<span class="mi">1903</span><span class="p">),</span>
<span class="p">(</span><span class="s2">&quot;Volund&#39;s Bikeshed Design Crew&quot;</span><span class="p">,</span> <span class="mi">1764</span><span class="p">),</span> <span class="p">(</span><span class="s2">&quot;Tehom&#39;s Misanthropes&quot;</span><span class="p">,</span> <span class="mi">1</span><span class="p">)]</span>
</pre></div>
</div>
</section>
</section>
</section>
<div class="clearer"></div>
</div>
</div>
</div>
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="index.html">
<img class="logo" src="_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<p><h3><a href="index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Tutorial Searching For Objects</a><ul>
<li><a class="reference internal" href="#things-to-search-for">Things to search for</a></li>
<li><a class="reference internal" href="#getting-objects-inside-another">Getting objects inside another</a></li>
<li><a class="reference internal" href="#searching-using-object-search">Searching using <code class="docutils literal notranslate"><span class="pre">Object.search</span></code></a></li>
<li><a class="reference internal" href="#searching-using-utils-search">Searching using <code class="docutils literal notranslate"><span class="pre">utils.search</span></code></a></li>
<li><a class="reference internal" href="#queries-in-django">Queries in Django</a><ul>
<li><a class="reference internal" href="#limiting-by-typeclass">Limiting by typeclass</a></li>
</ul>
</li>
<li><a class="reference internal" href="#multiple-conditions">Multiple conditions</a><ul>
<li><a class="reference internal" href="#complex-queries">Complex queries</a></li>
<li><a class="reference internal" href="#annotations-and-f-objects">Annotations and <code class="docutils literal notranslate"><span class="pre">F</span></code> objects</a></li>
<li><a class="reference internal" href="#grouping-by-and-values">Grouping By and Values</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="_sources/Tutorial-Searching-For-Objects.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com">Home page</a> </li>
<li><a href="https://github.com/evennia/evennia">Evennia Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li><a href="http://webchat.freenode.net/?channels=evennia&uio=MT1mYWxzZSY5PXRydWUmMTE9MTk1JjEyPXRydWUbb">IRC</a> -
<a href="https://discord.gg/NecFePw">Discord</a> -
<a href="https://groups.google.com/forum/#%21forum/evennia">Forums</a>
</li>
<li><a href="http://evennia.blogspot.com/">Evennia Dev blog</a> </li>
</ul>
<h3>Versions</h3>
<ul>
<li><a href="../1.0-dev/index.html">1.0-dev (develop branch)</a></li>
<li><a href="Tutorial-Searching-For-Objects.html">0.9.5 (v0.9.5 branch)</a></li>
</ul>
</div>
</div>
<div class="clearer"></div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="nav-item nav-item-0"><a href="index.html">Evennia 0.9.5</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Tutorial Searching For Objects</a></li>
</ul>
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>