evennia/docs/1.0-dev/Howtos/Beginner-Tutorial/Part1/Django-queries.html

525 lines
45 KiB
HTML
Raw Normal View History

2020-07-14 00:21:00 +02:00
<!DOCTYPE html>
2020-10-15 01:31:30 +02:00
<html>
2020-07-14 00:21:00 +02:00
<head>
<meta charset="utf-8" />
2021-05-16 00:06:01 +02:00
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
2022-02-06 18:34:09 +00:00
<title>12. Advanced searching - Django Database queries &#8212; Evennia 1.0-dev documentation</title>
2020-07-14 00:21:00 +02:00
<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>
<link rel="shortcut icon" href="../../../_static/favicon.ico"/>
<link rel="index" title="Index" href="../../../genindex.html" />
2020-10-19 22:46:24 +02:00
<link rel="search" title="Search" href="../../../search.html" />
2022-02-06 18:34:09 +00:00
<link rel="next" title="Part 2: What we want" href="../Part2/Beginner-Tutorial-Part2-Intro.html" />
<link rel="prev" title="11. Searching for things" href="Searching-Things.html" />
2020-07-14 00:21:00 +02:00
</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>
2020-10-19 22:46:24 +02:00
<li class="right" >
2022-02-06 18:34:09 +00:00
<a href="../Part2/Beginner-Tutorial-Part2-Intro.html" title="Part 2: What we want"
2020-10-19 22:46:24 +02:00
accesskey="N">next</a> |</li>
<li class="right" >
2022-02-06 18:34:09 +00:00
<a href="Searching-Things.html" title="11. Searching for things"
2020-10-19 22:46:24 +02:00
accesskey="P">previous</a> |</li>
2020-10-15 01:31:30 +02:00
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
2022-02-06 18:34:09 +00:00
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Intro.html" accesskey="U">Part 1: What we have</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>Advanced searching - Django Database queries</a></li>
2020-07-14 00:21:00 +02:00
</ul>
2021-06-23 18:58:26 +02:00
<div class="develop">develop branch</div>
2020-07-14 00:21:00 +02:00
</div>
<div class="document">
<div class="documentwrapper">
<div class="bodywrapper">
<div class="body" role="main">
2022-02-06 18:34:09 +00:00
<section class="tex2jax_ignore mathjax_ignore" id="advanced-searching-django-database-queries">
<h1><span class="section-number">12. </span>Advanced searching - Django Database queries<a class="headerlink" href="#advanced-searching-django-database-queries" title="Permalink to this headline"></a></h1>
2020-07-14 00:21:00 +02:00
<div class="admonition important">
<p class="admonition-title">Important</p>
<p>More advanced lesson!</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Learning about Django&#39;s queryset language is very useful once you start doing more advanced things
in Evennia. But it&#39;s not strictly needed out the box and can be a little overwhelming for a first
2020-07-14 00:21:00 +02:00
reading. So if you are new to Python and Evennia, feel free to just skim this lesson and refer
2021-10-26 21:41:11 +02:00
back to it later when you&#39;ve gained more experience.
</pre></div>
</div>
2020-07-14 00:21:00 +02:00
</div>
<p>The search functions and methods we used in the previous lesson are enough for most cases.
But sometimes you need to be more specific:</p>
<ul class="simple">
<li><p>You want to find all <code class="docutils literal notranslate"><span class="pre">Characters</span></code></p></li>
<li><p>… who are in Rooms tagged as <code class="docutils literal notranslate"><span class="pre">moonlit</span></code></p></li>
<li><p><em>and</em> who has the Attribute <code class="docutils literal notranslate"><span class="pre">lycantrophy</span></code> with a level higher than 2 …</p></li>
<li><p>… because theyll should immediately transform to werewolves!</p></li>
</ul>
<p>In principle you could achieve this with the existing search functions combined with a lot of loops
and if statements. But for something non-standard like this, querying the database directly will be
much more efficient.</p>
<p>Evennia uses <a class="reference external" href="https://www.djangoproject.com/">Django</a> to handle its connection to the database.
A <a class="reference external" href="https://docs.djangoproject.com/en/3.0/ref/models/querysets/">django queryset</a> represents
a database query. One can add querysets together to build ever-more complicated queries. Only when
you are trying to use the results of the queryset will it actually call the database.</p>
<p>The normal way to build a queryset is to define what class of entity you want to search by getting its
<code class="docutils literal notranslate"><span class="pre">.objects</span></code> resource, and then call various methods on that. Weve seen this one before:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_weapons = Weapon.objects.all()
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This is now a queryset representing all instances of <code class="docutils literal notranslate"><span class="pre">Weapon</span></code>. If <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> had a subclass <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> and we
only wanted the cannons, we would do</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>all_cannons = Cannon.objects.all()
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>Note that <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> and <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> are different typeclasses. You wont find any <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> instances in
the <code class="docutils literal notranslate"><span class="pre">all_weapon</span></code> result above, confusing as that may sound. To get instances of a Typeclass <em>and</em> the
instances of all its children classes you need to use <code class="docutils literal notranslate"><span class="pre">_family</span></code>:</p>
2021-05-16 00:06:01 +02:00
<aside class="sidebar">
2020-07-14 00:21:00 +02:00
<p class="sidebar-title">_family</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>The all_family, filter_family etc is an Evennia-specific
thing. It&#39;s not part of regular Django.
</pre></div>
</div>
2021-05-16 00:06:01 +02:00
</aside>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>really_all_weapons = Weapon.objects.all_family()
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This result now contains both <code class="docutils literal notranslate"><span class="pre">Weapon</span></code> and <code class="docutils literal notranslate"><span class="pre">Cannon</span></code> instances.</p>
<p>To limit your search by other criteria than the Typeclass you need to use <code class="docutils literal notranslate"><span class="pre">.filter</span></code>
(or <code class="docutils literal notranslate"><span class="pre">.filter_family</span></code>) instead:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>roses = Flower.objects.filter(db_key=&quot;rose&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This is a queryset representing all objects having a <code class="docutils literal notranslate"><span class="pre">db_key</span></code> equal to <code class="docutils literal notranslate"><span class="pre">&quot;rose&quot;</span></code>.
Since this is a queryset you can keep adding to it; this will act as an <code class="docutils literal notranslate"><span class="pre">AND</span></code> condition.</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_roses = roses.filter(db_location=myroom)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>We could also have written this in one statement:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_roses = Flower.objects.filter(db_key=&quot;rose&quot;, db_location=myroom)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>We can also <code class="docutils literal notranslate"><span class="pre">.exclude</span></code> something from results</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>local_non_red_roses = local_roses.exclude(db_key=&quot;red_rose&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>Only until we actually try to examine the result will the database be called. Here its called when we
try to loop over the queryset:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>for rose in local_non_red_roses:
print(rose)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>From now on, the queryset is <em>evaluated</em> and we cant keep adding more queries to it - wed need to
create a new queryset if we wanted to find some other result. Other ways to evaluate the queryset is to
print it, convert it to a list with <code class="docutils literal notranslate"><span class="pre">list()</span></code> and otherwise try to access its results.</p>
<p>Note how we use <code class="docutils literal notranslate"><span class="pre">db_key</span></code> and <code class="docutils literal notranslate"><span class="pre">db_location</span></code>. This is the actual names of these database fields. By convention
Evennia uses <code class="docutils literal notranslate"><span class="pre">db_</span></code> in front of every database field. When you use the normal Evennia search helpers and objects
you can skip the <code class="docutils literal notranslate"><span class="pre">db_</span></code> but here we are calling the database directly and need to use the real names.</p>
<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>
<blockquote>
<div><p>All of Evennia search functions use querysets under the hood. The <code class="docutils literal notranslate"><span class="pre">evennia.search_*</span></code> functions actually
return querysets, which means you could in principle keep adding queries to their results as well.</p>
</div></blockquote>
2021-05-16 00:06:01 +02:00
<section id="queryset-field-lookups">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.1. </span>Queryset field lookups<a class="headerlink" href="#queryset-field-lookups" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>Above we found roses with exactly the <code class="docutils literal notranslate"><span class="pre">db_key</span></code> <code class="docutils literal notranslate"><span class="pre">&quot;rose&quot;</span></code>. This is an <em>exact</em> match that is <em>case sensitive</em>,
so it would not find <code class="docutils literal notranslate"><span class="pre">&quot;Rose&quot;</span></code>.</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span># this is case-sensitive and the same as =
roses = Flower.objects.filter(db_key__exact=&quot;rose&quot;
2020-07-14 00:21:00 +02:00
2021-10-26 21:41:11 +02:00
# the i means it&#39;s case-insensitive
roses = Flower.objects.filter(db_key__iexact=&quot;rose&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>The Django field query language uses <code class="docutils literal notranslate"><span class="pre">__</span></code> in the same way as Python uses <code class="docutils literal notranslate"><span class="pre">.</span></code> to access resources. This
is because <code class="docutils literal notranslate"><span class="pre">.</span></code> is not allowed in a function keyword.</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>roses = Flower.objects.filter(db_key__icontains=&quot;rose&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This will find all flowers whose name contains the string <code class="docutils literal notranslate"><span class="pre">&quot;rose&quot;</span></code>, like <code class="docutils literal notranslate"><span class="pre">&quot;roses&quot;</span></code>, <code class="docutils literal notranslate"><span class="pre">&quot;wild</span> <span class="pre">rose&quot;</span></code> etc. The
<code class="docutils literal notranslate"><span class="pre">i</span></code> in the beginning makes the search case-insensitive. Other useful variations to use
2021-10-26 21:41:11 +02:00
are <code class="docutils literal notranslate"><span class="pre">__istartswith</span></code> and <code class="docutils literal notranslate"><span class="pre">__iendswith</span></code>. You can also use <code class="docutils literal notranslate"><span class="pre">__gt</span></code>, <code class="docutils literal notranslate"><span class="pre">__ge</span></code> for “greater-than”/“greater-or-equal-than”
2020-07-14 00:21:00 +02:00
comparisons (same for <code class="docutils literal notranslate"><span class="pre">__lt</span></code> and <code class="docutils literal notranslate"><span class="pre">__le</span></code>). There is also <code class="docutils literal notranslate"><span class="pre">__in</span></code>:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>swords = Weapons.objects.filter(db_key__in=(&quot;rapier&quot;, &quot;two-hander&quot;, &quot;shortsword&quot;))
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>One also uses <code class="docutils literal notranslate"><span class="pre">__</span></code> to access foreign objects like Tags. Lets for example assume this is how we identify mages:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>char.tags.add(&quot;mage&quot;, category=&quot;profession&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>Now, in this case we have an Evennia helper to do this search:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>mages = evennia.search_tags(&quot;mage&quot;, category=&quot;profession&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>But this will find all Objects with this tag+category. Maybe you are only looking for Vampire mages:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>sparkly_mages = Vampire.objects.filter(db_tags__db_key=&quot;mage&quot;, db_tags__db_category=&quot;profession&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This looks at the <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> field on the <code class="docutils literal notranslate"><span class="pre">Vampire</span></code> and filters on the values of each tags
<code class="docutils literal notranslate"><span class="pre">db_key</span></code> and <code class="docutils literal notranslate"><span class="pre">db_category</span></code> together.</p>
<p>For more field lookups, see the
<a class="reference external" href="https://docs.djangoproject.com/en/3.0/ref/models/querysets/#field-lookups">django docs</a> on the subject.</p>
2021-05-16 00:06:01 +02:00
</section>
<section id="get-that-werewolf">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.2. </span>Get that werewolf …<a class="headerlink" href="#get-that-werewolf" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>Lets see if we can make a query for the werewolves in the moonlight we mentioned at the beginning
of this section.</p>
<p>Firstly, we make ourselves and our current location match the criteria, so we can test:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>&gt; py here.tags.add(&quot;moonlit&quot;)
&gt; py me.db.lycantrophy = 3
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>This is an example of a more complex query. Well consider it an example of what is
possible.</p>
2021-05-16 00:06:01 +02:00
<aside class="sidebar">
2020-07-14 00:21:00 +02:00
<p class="sidebar-title">Line breaks</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Note the way of writing this code. It would have been very hard to read if we just wrote it in
one long line. But since we wrapped it in `(...)` we can spread it out over multiple lines
without worrying about line breaks!
</pre></div>
</div>
2021-05-16 00:06:01 +02:00
</aside>
2021-10-26 21:41:11 +02:00
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typeclasses.characters</span> <span class="kn">import</span> <span class="n">Character</span>
2020-07-14 00:21:00 +02:00
<span class="n">will_transform</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_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">&quot;moonlit&quot;</span><span class="p">,</span>
<span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">&quot;lycantrophy&quot;</span><span class="p">,</span>
<span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<ul class="simple">
<li><p><strong>Line 3</strong> - We want to find <code class="docutils literal notranslate"><span class="pre">Character</span></code>s, so we access <code class="docutils literal notranslate"><span class="pre">.objects</span></code> on the <code class="docutils literal notranslate"><span class="pre">Character</span></code> typeclass.</p></li>
<li><p><strong>Line 4</strong> - We start to filter …</p></li>
<li><p><strong>Line 5</strong></p>
<ul>
<li><p>… by accessing the <code class="docutils literal notranslate"><span class="pre">db_location</span></code> field (usually this is a Room)</p></li>
<li><p>… and on that location, we get the value of <code class="docutils literal notranslate"><span class="pre">db_tags</span></code> (this is a <em>many-to-many</em> database field
that we can treat like an object for this purpose; it references all Tags on the location)</p></li>
<li><p>… and from those <code class="docutils literal notranslate"><span class="pre">Tags</span></code>, we looking for <code class="docutils literal notranslate"><span class="pre">Tags</span></code> whose <code class="docutils literal notranslate"><span class="pre">db_key</span></code> is “monlit” (non-case sensitive).</p></li>
</ul>
</li>
<li><p><strong>Line 6</strong> - … We also want only Characters with <code class="docutils literal notranslate"><span class="pre">Attributes</span></code> whose <code class="docutils literal notranslate"><span class="pre">db_key</span></code> is exactly <code class="docutils literal notranslate"><span class="pre">&quot;lycantrophy&quot;</span></code></p></li>
<li><p><strong>Line 7</strong> - … at the same time as the <code class="docutils literal notranslate"><span class="pre">Attribute</span></code>s <code class="docutils literal notranslate"><span class="pre">db_value</span></code> is greater-than 2.</p></li>
</ul>
<p>Running this query makes our newly lycantrrophic Character appear in <code class="docutils literal notranslate"><span class="pre">will_transform</span></code>. Success!</p>
<blockquote>
2021-10-26 21:41:11 +02:00
<div><p>Dont confuse database fields with <a class="reference internal" href="../../../Components/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
2020-07-14 00:21:00 +02:00
<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.</p>
</div></blockquote>
2021-05-16 00:06:01 +02:00
</section>
<section id="complex-queries">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.3. </span>Complex queries<a class="headerlink" href="#complex-queries" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>All examples so far used <code class="docutils literal notranslate"><span class="pre">AND</span></code> relations. The arguments to <code class="docutils literal notranslate"><span class="pre">.filter</span></code> are added together with <code class="docutils literal notranslate"><span class="pre">AND</span></code>
2021-10-26 21:41:11 +02:00
(“we want tag room to be “monlit” <em>and</em> lycantrhopy be &gt; 2”).</p>
2020-07-14 00:21:00 +02:00
<p>For queries using <code class="docutils literal notranslate"><span class="pre">OR</span></code> and <code class="docutils literal notranslate"><span class="pre">NOT</span></code> we need Djangos
<a class="reference external" href="https://docs.djangoproject.com/en/1.11/topics/db/queries/#complex-lookups-with-q-objects">Q object</a>. It is
imported from Django directly:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>from django.db.models import Q
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>The <code class="docutils literal notranslate"><span class="pre">Q</span></code> is an object that is created with the same arguments as <code class="docutils literal notranslate"><span class="pre">.filter</span></code>, for example</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>Q(db_key=&quot;foo&quot;)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>You can then use this <code class="docutils literal notranslate"><span class="pre">Q</span></code> instance as argument in a <code class="docutils literal notranslate"><span class="pre">filter</span></code>:</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>q1 = Q(db_key=&quot;foo&quot;)
Character.objects.filter(q1)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>The useful thing about <code class="docutils literal notranslate"><span class="pre">Q</span></code> is that these objects can be chained together with special symbols (bit operators):
<code class="docutils literal notranslate"><span class="pre">|</span></code> for <code class="docutils literal notranslate"><span class="pre">OR</span></code> and <code class="docutils literal notranslate"><span class="pre">&amp;</span></code> for <code class="docutils literal notranslate"><span class="pre">AND</span></code>. A tilde <code class="docutils literal notranslate"><span class="pre">~</span></code> in front negates the expression inside the <code class="docutils literal notranslate"><span class="pre">Q</span></code> and thus
works like <code class="docutils literal notranslate"><span class="pre">NOT</span></code>.</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>q1 = Q(db_key=&quot;Dalton&quot;)
q2 = Q(db_location=prison)
Character.objects.filter(q1 | ~q2)
2020-07-14 00:21:00 +02:00
</pre></div>
</div>
<p>Would get all Characters that are either named “Dalton” <em>or</em> which is <em>not</em> in prison. The result is a mix
of Daltons and non-prisoners.</p>
<p>Let us expand our original werewolf query. Not only do we want to find all Characters in a moonlit room
with a certain level of <code class="docutils literal notranslate"><span class="pre">lycanthrophy</span></code>. Now we also want the full moon to immediately transform people who were
recently bitten, even if their <code class="docutils literal notranslate"><span class="pre">lycantrophy</span></code> level is not yet high enough (more dramatic this way!). Lets say there is
a Tag “recently_bitten” that controls this.</p>
<p>This is how wed change our query:</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<span class="n">will_transform</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">Q</span><span class="p">(</span><span class="n">db_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">&quot;moonlit&quot;</span><span class="p">)</span>
<span class="o">&amp;</span> <span class="p">(</span>
<span class="n">Q</span><span class="p">(</span><span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">&quot;lycantrophy&quot;</span><span class="p">,</span>
<span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="o">|</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">&quot;recently_bitten&quot;</span><span class="p">)</span>
<span class="p">))</span>
<span class="o">.</span><span class="n">distinct</span><span class="p">()</span>
<span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<p>Thats quite compact. It may be easier to see whats going on if written this way:</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<span class="n">q_moonlit</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_location__db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">&quot;moonlit&quot;</span><span class="p">)</span>
<span class="n">q_lycantropic</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_attributes__db_key</span><span class="o">=</span><span class="s2">&quot;lycantrophy&quot;</span><span class="p">,</span> <span class="n">db_attributes__db_value__gt</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span>
<span class="n">q_recently_bitten</span> <span class="o">=</span> <span class="n">Q</span><span class="p">(</span><span class="n">db_tags__db_key__iexact</span><span class="o">=</span><span class="s2">&quot;recently_bitten&quot;</span><span class="p">)</span>
<span class="n">will_transform</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">q_moonlit</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">q_lycantropic</span> <span class="o">|</span> <span class="n">q_recently_bitten</span><span class="p">))</span>
<span class="o">.</span><span class="n">distinct</span><span class="p">()</span>
<span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2021-05-16 00:06:01 +02:00
<aside class="sidebar">
2020-07-14 00:21:00 +02:00
<p class="sidebar-title">SQL</p>
2021-10-26 21:41:11 +02:00
<div class="highlight-none notranslate"><div class="highlight"><pre><span></span>These Python structures are internally converted to SQL, the native language of the database.
If you are familiar with SQL, these are many-to-many tables joined with `LEFT OUTER JOIN`,
which may lead to multiple merged rows combining the same object with different relations.
</pre></div>
</div>
2021-05-16 00:06:01 +02:00
</aside>
2020-07-14 00:21:00 +02:00
<p>This reads as “Find all Characters in a moonlit room that either has the Attribute <code class="docutils literal notranslate"><span class="pre">lycantrophy</span></code> higher
than two <em>or</em> which has the Tag <code class="docutils literal notranslate"><span class="pre">recently_bitten</span></code>”. With an OR-query like this its possible to find the
same Character via different paths, so we add <code class="docutils literal notranslate"><span class="pre">.distinct()</span></code> at the end. This makes sure that there is only
one instance of each Character in the result.</p>
2021-05-16 00:06:01 +02:00
</section>
<section id="annotations">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.4. </span>Annotations<a class="headerlink" href="#annotations" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<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> do it like this (dont actually do it this way!):</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
2021-10-26 21:41:11 +02:00
<span class="n">all_rooms</span> <span class="o">=</span> <span class="n">Rooms</span><span class="o">.</span><span class="n">objects</span><span class="o">.</span><span class="n">all</span><span class="p">()</span>
2020-07-14 00:21:00 +02:00
<span class="n">rooms_with_five_objects</span> <span class="o">=</span> <span class="p">[]</span>
2021-10-26 21:41:11 +02:00
<span class="k">for</span> <span class="n">room</span> <span class="ow">in</span> <span class="n">all_rooms</span><span class="p">:</span>
2020-07-14 00:21:00 +02:00
<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>
<span class="n">rooms_with_five_objects</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">room</span><span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<p>Above we get all rooms and then use <code class="docutils literal notranslate"><span class="pre">list.append()</span></code> to keep adding the right rooms
to an ever-growing list. This is <em>not</em> a good idea, once your database grows this will
be unnecessarily computing-intensive. The database is much more suitable for this.</p>
<p><em>Annotations</em> allow you to set a variable inside the query that you can
then access from other parts of the query. Lets do the same example as before directly in the database:</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<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">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="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<p><code class="docutils literal notranslate"><span class="pre">Count</span></code> is a Django class for counting the number of things in the database.</p>
<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>. It creates an in-database function
that will count the number of results inside the database.</p>
<blockquote>
<div><p>Note the 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>.</p>
</div></blockquote>
<p>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>
2021-05-16 00:06:01 +02:00
</section>
<section id="f-objects">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.5. </span>F-objects<a class="headerlink" href="#f-objects" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>What if we wanted to compare two dynamic 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 (silly example, but …)? This can be with Djangos
<a class="reference external" href="https://docs.djangoproject.com/en/1.11/ref/models/expressions/#f-expressions">F objects</a>.
So-called F expressions allow you to do a query that looks at a value of each object in the database.</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<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>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<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>
<span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<p>Here we used <code class="docutils literal notranslate"><span class="pre">.annotate</span></code> to create two in-query variables <code class="docutils literal notranslate"><span class="pre">num_objects</span></code> and <code class="docutils literal notranslate"><span class="pre">num_tags</span></code>. We then
directly use these results in the filter. Using <code class="docutils literal notranslate"><span class="pre">F()</span></code> allows for also the right-hand-side of the filter
condition to be calculated on the fly, completely within the database.</p>
2021-05-16 00:06:01 +02:00
</section>
<section id="grouping-and-returning-only-certain-properties">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.6. </span>Grouping and returning only certain properties<a class="headerlink" href="#grouping-and-returning-only-certain-properties" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>Suppose you used tags to mark someone belonging to an organization. Now you want to make a list and
need to get the membership count of every organization all at once.</p>
<p>The <code class="docutils literal notranslate"><span class="pre">.annotate</span></code>, <code class="docutils literal notranslate"><span class="pre">.values_list</span></code>, and <code class="docutils literal notranslate"><span class="pre">.order_by</span></code> queryset methods are useful for this. Normally when
you run a <code class="docutils literal notranslate"><span class="pre">.filter</span></code>, what you get back is a bunch of full typeclass instances, like roses or swords.
Using <code class="docutils literal notranslate"><span class="pre">.values_list</span></code> you can instead choose to only get back certain properties on objects.
The <code class="docutils literal notranslate"><span class="pre">.order_by</span></code> method finally allows for sorting the results according to some criterion:</p>
2021-10-26 21:41:11 +02:00
<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>
2020-07-14 00:21:00 +02:00
<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">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">annotate</span><span class="p">(</span><span class="n">tagcount</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;-tagcount&#39;</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="s2">&quot;tagcount&quot;</span><span class="p">)</span>
</pre></div>
2021-10-26 21:41:11 +02:00
</div>
2020-07-14 00:21:00 +02:00
<p>Here we fetch all Characters who …</p>
<ul class="simple">
<li><p>… has a tag of category “organization” on them</p></li>
<li><p>… along the way we count how many different Characters (each <code class="docutils literal notranslate"><span class="pre">id</span></code> is unique) we find for each organization
and store it in a variable <code class="docutils literal notranslate"><span class="pre">tagcount</span></code> using <code class="docutils literal notranslate"><span class="pre">.annotate</span></code> and <code class="docutils literal notranslate"><span class="pre">Count</span></code></p></li>
<li><p>… we use this count to sort the result in descending order of <code class="docutils literal notranslate"><span class="pre">tagcount</span></code> (descending because there is a minus sign,
default is increasing order but we want the most popular organization to be first).</p></li>
<li><p>… and finally we make sure to only return exactly the properties we want, namely the name of the organization tag
and how many matches we found for that organization.</p></li>
</ul>
<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>
2021-10-26 21:41:11 +02:00
<span class="p">(</span><span class="s1">&#39;Griatch&#39;</span><span class="n">s</span> <span class="n">poets</span> <span class="n">society</span><span class="s1">&#39;, 3872),</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>
2020-07-14 00:21:00 +02:00
<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 Glorious Misanthropes&quot;</span><span class="p">,</span> <span class="mi">1763</span><span class="p">)</span>
<span class="p">]</span>
</pre></div>
</div>
2021-05-16 00:06:01 +02:00
</section>
<section id="conclusions">
2022-02-06 18:34:09 +00:00
<h2><span class="section-number">12.7. </span>Conclusions<a class="headerlink" href="#conclusions" title="Permalink to this headline"></a></h2>
2020-07-14 00:21:00 +02:00
<p>We have covered a lot of ground in this lesson and covered several more complex topics. Knowing how to
query using Django is a powerful skill to have.</p>
<p>This concludes the first part of the Evennia starting tutorial - “What we have”. Now we have a good foundation
to understand how to plan what our tutorial game will be about.</p>
2021-05-16 00:06:01 +02:00
</section>
</section>
2020-07-14 00:21:00 +02:00
2020-10-15 01:31:30 +02:00
<div class="clearer"></div>
2020-07-14 00:21:00 +02:00
</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>
2022-02-06 18:34:09 +00:00
<li><a class="reference internal" href="#">12. Advanced searching - Django Database queries</a><ul>
<li><a class="reference internal" href="#queryset-field-lookups">12.1. Queryset field lookups</a></li>
<li><a class="reference internal" href="#get-that-werewolf">12.2. Get that werewolf …</a></li>
<li><a class="reference internal" href="#complex-queries">12.3. Complex queries</a></li>
<li><a class="reference internal" href="#annotations">12.4. Annotations</a></li>
<li><a class="reference internal" href="#f-objects">12.5. F-objects</a></li>
<li><a class="reference internal" href="#grouping-and-returning-only-certain-properties">12.6. Grouping and returning only certain properties</a></li>
<li><a class="reference internal" href="#conclusions">12.7. Conclusions</a></li>
2020-07-14 00:21:00 +02:00
</ul>
</li>
</ul>
2020-10-19 22:46:24 +02:00
<h4>Previous topic</h4>
<p class="topless"><a href="Searching-Things.html"
2022-02-06 18:34:09 +00:00
title="previous chapter"><span class="section-number">11. </span>Searching for things</a></p>
2020-10-19 22:46:24 +02:00
<h4>Next topic</h4>
2022-02-06 18:34:09 +00:00
<p class="topless"><a href="../Part2/Beginner-Tutorial-Part2-Intro.html"
title="next chapter">Part 2: What we want</a></p>
2020-07-14 00:21:00 +02:00
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
2022-02-06 18:34:09 +00:00
<li><a href="../../../_sources/Howtos/Beginner-Tutorial/Part1/Django-queries.md.txt"
2020-07-14 00:21:00 +02:00
rel="nofollow">Show Page Source</a></li>
</ul>
2021-03-06 01:37:43 +01:00
</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>
2021-08-06 00:47:51 +02:00
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
2021-08-06 00:53:44 +02:00
<a href="https://evennia.blogspot.com/">Blog</a>
2021-03-06 01:37:43 +01:00
</li>
</ul>
2020-07-14 00:21:00 +02:00
<h3>Versions</h3>
<ul>
<li><a href="Django-queries.html">1.0-dev (develop branch)</a></li>
2022-02-05 18:41:59 +00:00
<li><a href="../../../../0.9.5/index.html">0.9.5 (v0.9.5 branch)</a></li>
2020-07-14 00:21:00 +02:00
</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>
2020-10-19 22:46:24 +02:00
<li class="right" >
2022-02-06 18:34:09 +00:00
<a href="../Part2/Beginner-Tutorial-Part2-Intro.html" title="Part 2: What we want"
2020-10-19 22:46:24 +02:00
>next</a> |</li>
<li class="right" >
2022-02-06 18:34:09 +00:00
<a href="Searching-Things.html" title="11. Searching for things"
2020-10-19 22:46:24 +02:00
>previous</a> |</li>
2020-10-15 01:31:30 +02:00
<li class="nav-item nav-item-0"><a href="../../../index.html">Evennia 1.0-dev</a> &#187;</li>
2022-02-06 18:34:09 +00:00
<li class="nav-item nav-item-1"><a href="../../Howtos-Overview.html" >Tutorials and Howtos</a> &#187;</li>
<li class="nav-item nav-item-2"><a href="../Beginner-Tutorial-Intro.html" >Beginner Tutorial</a> &#187;</li>
<li class="nav-item nav-item-3"><a href="Beginner-Tutorial-Part1-Intro.html" >Part 1: What we have</a> &#187;</li>
<li class="nav-item nav-item-this"><a href=""><span class="section-number">12. </span>Advanced searching - Django Database queries</a></li>
2020-07-14 00:21:00 +02:00
</ul>
2021-06-23 18:58:26 +02:00
<div class="develop">develop branch</div>
2020-07-14 00:21:00 +02:00
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2020, The Evennia developer community.
2020-10-15 01:31:30 +02:00
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
2020-07-14 00:21:00 +02:00
</div>
</body>
</html>