<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Django Ninja Tutorial on CS 347</title>
    <link>/activities/django-ninja-tutorial/</link>
    <description>Recent content in Django Ninja Tutorial on CS 347</description>
    <generator>Hugo</generator>
    <language>en</language>
    <atom:link href="/activities/django-ninja-tutorial/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Part 1: Project setup &amp; layout</title>
      <link>/activities/django-ninja-tutorial/part-1/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-1/</guid>
      <description>&lt;p&gt;Over eight parts, you&amp;rsquo;ll build &lt;strong&gt;TaskFlow&lt;/strong&gt;, a task tracker for teams: projects, members, tasks, labels, comments and file attachments. This first part sets up a project layout that stays tidy as the API grows. Each app gets its own router and schemas, and one small module wires them together.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to lay out a multi-app project: one &lt;code&gt;Router&lt;/code&gt; per app, one &lt;code&gt;NinjaAPI&lt;/code&gt; for the project&lt;/li&gt;&#xA;&lt;li&gt;How to mount routers with &lt;code&gt;add_router()&lt;/code&gt; and group them in the docs with tags&lt;/li&gt;&#xA;&lt;li&gt;How to generate input and output schemas with &lt;code&gt;ModelSchema&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to nest one app&amp;rsquo;s endpoints under another app&amp;rsquo;s URL, with &lt;code&gt;Path[...]&lt;/code&gt; for prefix parameters&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 2: Relations &amp; computed fields</title>
      <link>/activities/django-ninja-tutorial/part-2/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-2/</guid>
      <description>&lt;p&gt;In Part 1, a task was a flat record whose project came back as a bare id. Real tasks have an assignee and labels, and clients want to see who and what those are without making a request for each one. In this part you&amp;rsquo;ll add those relations, return them as nested objects, compute a few values the model doesn&amp;rsquo;t store, and check how many queries each list costs.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to return related objects as nested schemas, while clients send plain ids&lt;/li&gt;&#xA;&lt;li&gt;How to set a many-to-many relation after creating an object&lt;/li&gt;&#xA;&lt;li&gt;How to compute a field with a &lt;code&gt;resolve_&amp;lt;field&amp;gt;&lt;/code&gt; static method&lt;/li&gt;&#xA;&lt;li&gt;How to read ORM annotations and reverse relations into a schema&lt;/li&gt;&#xA;&lt;li&gt;How to spot N+1 queries and fix them with &lt;code&gt;select_related&lt;/code&gt; and &lt;code&gt;prefetch_related&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 3: Validation &amp; workflows</title>
      <link>/activities/django-ninja-tutorial/part-3/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-3/</guid>
      <description>&lt;p&gt;So far, TaskFlow accepts almost anything a client sends: a title made of spaces is a valid task, and there&amp;rsquo;s no way to say a task is finished. In this part you&amp;rsquo;ll give tasks a status, a priority and a due date, validate input with field and model validators, add a &lt;code&gt;PATCH&lt;/code&gt; endpoint for partial updates, and move tasks through a small workflow that rejects invalid status changes.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How Django &lt;code&gt;choices&lt;/code&gt; become enums in your schemas and in OpenAPI&lt;/li&gt;&#xA;&lt;li&gt;How to write field validators and model validators, with your own error messages&lt;/li&gt;&#xA;&lt;li&gt;What a &lt;code&gt;422&lt;/code&gt; validation error looks like, and how to read it&lt;/li&gt;&#xA;&lt;li&gt;How to accept partial updates with &lt;code&gt;PatchDict&lt;/code&gt;, and what it changes about validation&lt;/li&gt;&#xA;&lt;li&gt;How to reject an invalid state change with &lt;code&gt;HttpError&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 4: Auth &amp; permissions</title>
      <link>/activities/django-ninja-tutorial/part-4/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-4/</guid>
      <description>&lt;p&gt;Right now anyone who can reach the API can read and change every project. In this part you&amp;rsquo;ll add JSON Web Tokens, written from scratch with the standard library, require them on every endpoint, and then decide what each user may do: project memberships with roles, queries scoped to the user&amp;rsquo;s projects, and comments that only their author can edit.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to issue and verify JWT access and refresh tokens with nothing but the standard library&lt;/li&gt;&#xA;&lt;li&gt;How to protect a whole API with an &lt;code&gt;HttpBearer&lt;/code&gt; authenticator, and open up a few endpoints with &lt;code&gt;auth=None&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to read the current user from &lt;code&gt;request.auth&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to write one permission helper that returns &lt;code&gt;404&lt;/code&gt; to non-members and &lt;code&gt;403&lt;/code&gt; to members without the right role&lt;/li&gt;&#xA;&lt;li&gt;How to scope every query to the user&amp;rsquo;s projects, and add object-level rules for comments&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 5: Filtering, search, ordering &amp; pagination</title>
      <link>/activities/django-ninja-tutorial/part-5/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-5/</guid>
      <description>&lt;p&gt;&lt;code&gt;GET /taskflow/api/projects/{project_id}/tasks/&lt;/code&gt; returns every task of a project in one response. That&amp;rsquo;s fine for a handful, but a real team wants the tasks assigned to them, the overdue ones, or the ones mentioning &amp;ldquo;checkout&amp;rdquo;, and it doesn&amp;rsquo;t want a thousand of them at once. In this part you&amp;rsquo;ll add filters and search with a &lt;code&gt;FilterSchema&lt;/code&gt;, let clients pick the sort order from a fixed list, and paginate tasks by page number and comments with a cursor.&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 6: File uploads &amp; forms</title>
      <link>/activities/django-ninja-tutorial/part-6/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-6/</guid>
      <description>&lt;p&gt;Tasks often come with files: a design mockup, a screenshot of a bug, a PDF from the client. In this part you&amp;rsquo;ll let members attach files to tasks. An upload is a &lt;code&gt;multipart/form-data&lt;/code&gt; request instead of JSON, so you&amp;rsquo;ll combine a file parameter with form fields, validate the file&amp;rsquo;s size and type, store it with a plain &lt;code&gt;FileField&lt;/code&gt;, and return an absolute URL for it.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to accept a file together with form fields using &lt;code&gt;File[...]&lt;/code&gt; and &lt;code&gt;Form[...]&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to validate an upload with a reusable annotated type, so errors come back as &lt;code&gt;422&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to configure &lt;code&gt;MEDIA_ROOT&lt;/code&gt; and serve uploaded files during development&lt;/li&gt;&#xA;&lt;li&gt;How to build absolute URLs in a response with a resolver that reads the request&lt;/li&gt;&#xA;&lt;li&gt;How to delete a file together with its database row, behind a permission check&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 7: Testing</title>
      <link>/activities/django-ninja-tutorial/part-7/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-7/</guid>
      <description>&lt;p&gt;So far you&amp;rsquo;ve checked every feature by hand, with curl and the shell. In this part you&amp;rsquo;ll turn those checks into a pytest suite that runs in about half a second. Django Ninja&amp;rsquo;s &lt;code&gt;TestClient&lt;/code&gt; calls your operations directly, so tests send JSON, form data, files and tokens without a running server.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to set up pytest and pytest-django for a Django Ninja project&lt;/li&gt;&#xA;&lt;li&gt;How to write fixtures for users, projects with memberships, and logged-in clients&lt;/li&gt;&#xA;&lt;li&gt;How &lt;code&gt;TestClient&lt;/code&gt; calls an API, and how to send tokens, query strings and files with it&lt;/li&gt;&#xA;&lt;li&gt;How to test validation errors, permissions and filters, and cover many cases with &lt;code&gt;parametrize&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;How to keep uploads and password hashing from slowing down or littering your tests&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
    <item>
      <title>Part 8: OpenAPI polish &amp; versioning</title>
      <link>/activities/django-ninja-tutorial/part-8/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>/activities/django-ninja-tutorial/part-8/</guid>
      <description>&lt;p&gt;TaskFlow works and is tested. Other people will build against it, though, and for them the interactive docs are the API. In this last part you&amp;rsquo;ll make the generated schema say more: what the API is for, what each operation does, and what each field expects. Then you&amp;rsquo;ll ship a breaking change the safe way, as a version 2 that runs next to version 1.&lt;/p&gt;&#xA;&lt;div class=&#34;alert alert-info&#34; role=&#34;alert&#34;&gt;&lt;div class=&#34;h4 alert-heading&#34; role=&#34;heading&#34;&gt;What you&#39;ll learn&lt;/div&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;How to set the API&amp;rsquo;s title, version and description&lt;/li&gt;&#xA;&lt;li&gt;How summaries, docstrings and &lt;code&gt;Field(description=..., examples=...)&lt;/code&gt; show up in the schema&lt;/li&gt;&#xA;&lt;li&gt;How to hide an internal endpoint and mark old ones as deprecated&lt;/li&gt;&#xA;&lt;li&gt;How to run two &lt;code&gt;NinjaAPI&lt;/code&gt; versions side by side, sharing the routers that didn&amp;rsquo;t change&lt;/li&gt;&#xA;&lt;li&gt;How URL namespaces keep &lt;code&gt;reverse()&lt;/code&gt; working with more than one API&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; about 15 minutes&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
