-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
40 lines (19 loc) · 59.5 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Creating a web API with FastAPI and Django</title>
<link href="/categories/django-pydantic/django-pydantic-2022-05-15-Creating-a-web-API-with-FastAPI-and-Django/"/>
<url>/categories/django-pydantic/django-pydantic-2022-05-15-Creating-a-web-API-with-FastAPI-and-Django/</url>
<content type="html"><![CDATA[<h1 id="FastAPI"><a href="#FastAPI" class="headerlink" title="FastAPI"></a>FastAPI</h1><p>FastAPI is based in Starlette and Pydantic.</p><p>Starlette is a lightweight ASGI framework, it has a <a href="https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=fortune&l=zijzen-sf">impressive performance</a>, supports async and its simplicity allow us to easily write scalable web systems.</p><p>Pydantic provides data validation and serialization using python type annotations, it enforces type hints at runtime, provides user friendly errors when data is invalid, is fast (it <a href="https://pydantic-docs.helpmanual.io/benchmarks/">claims to be 12x faster than DRF</a>).</p><p>The union of Starlette and Pydantic added to automatic OpenAPI schemas generation and swagger gives us a great toolset to quickly develop a (Fast) API. Type annotations enforcement results in an amazing developer experience.</p><p>FastAPI is database agnostic and easily integrable with any Python ORM (here enters Django ORM in this example).</p><h1 id="Django"><a href="#Django" class="headerlink" title="Django"></a>Django</h1><h2 id="Advantages"><a href="#Advantages" class="headerlink" title="Advantages"></a>Advantages</h2><p>Django provides a great toolbox to quickly develop a web application, as from their logo: <code>"The web framework for perfectionists with deadlines"</code>.</p><p>Django’s ORM is simple, allow us to easily write clean code, is powerful to translate different types of queries without use of raw SQL and has a great migration control system.</p><p>Django’s admin is great tool to manage data with minimum development effort.</p><h2 id="Disadvantages"><a href="#Disadvantages" class="headerlink" title="Disadvantages"></a>Disadvantages</h2><p>Django Rest Framework (DRF) serializers are way slower than Pydantic validators/serializer. </p><p>Django is built based in metaclasses, this results in missing type hints in several objects you manipulate.</p><p>Django ORM is not able to operate safely in an async environment. But discussions about supporting async are active and hopefully Psycopg3 implementation will enable this support. Use of <a href="https://docs.djangoproject.com/en/4.0/topics/async/#async-views">asgiref library</a> solves this problem until this is implemented.</p><h1 id="Uniting-forces"><a href="#Uniting-forces" class="headerlink" title="Uniting forces"></a>Uniting forces</h1><p>Starlette will provide a light weight ASGI framework to server our API endpoints.</p><p>Pydantic will provide a toolset to validate and serialize our payloads, while enforcing type hints in our code base.</p><p>FastAPI’s OpenAPI support will provide a toolset to generate a nice API documentation.</p><p>Django will provide a powerful ORM, migration control system and the admin page to manage our data.</p><p>Djantic will be the bridge between Pydantic schemas and Django models.</p><h1 id="Requirements"><a href="#Requirements" class="headerlink" title="Requirements"></a>Requirements</h1><p>This tutorial expects you to have some familiarity of:</p><ul><li>Python</li><li>Django (part 1 and 2 of the <a href="https://docs.djangoproject.com/en/4.0/intro/tutorial01/">official tutorial</a> are enough)</li><li>FastAPI (<a href="https://fastapi.tiangolo.com/tutorial/first-steps/">official first steps</a> guide are enough)</li></ul><h1 id="Packages-installation"><a href="#Packages-installation" class="headerlink" title="Packages installation"></a>Packages installation</h1><p>Install the following dependencies. Fell free to use your favorite package/environment manager instead of pip.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">pip install fastapi uvicorn django djantic django-extensions djangorestframework-simplejwt python-multipart</span><br></pre></td></tr></table></figure><h1 id="Django-project"><a href="#Django-project" class="headerlink" title="Django project"></a>Django project</h1><p>Start a new Django project. We could start as well from a cookiecutter template for FastAPI and add Django models and admin endpoint.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">django-admin startproject fastdjango .</span><br></pre></td></tr></table></figure><p>Here is our directory tree:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">├── fastdjango</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── asgi.py</span><br><span class="line">│ ├── settings.py</span><br><span class="line">│ ├── urls.py</span><br><span class="line">│ └── wsgi.py</span><br><span class="line">└── manage.py</span><br></pre></td></tr></table></figure><p>We are able to run the Django project:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uvicorn fastdjango.asgi:application --reload</span><br></pre></td></tr></table></figure><h1 id="Defining-the-ASGI-module-Django-FastAPI"><a href="#Defining-the-ASGI-module-Django-FastAPI" class="headerlink" title="Defining the ASGI module (Django + FastAPI)"></a>Defining the ASGI module (Django + FastAPI)</h1><p>Refactoring the ASGI module to integrate FastAPI application.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir fastdjango/asgi; mv fastdjango/asgi.py fastdjango/asgi/django.py; touch fastdjango/asgi/__init__.py fastdjango/asgi/fastapi.py</span><br></pre></td></tr></table></figure><p>Directory tree:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">├── fastdjango</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── asgi</span><br><span class="line">│ │ ├── __init__.py</span><br><span class="line">│ │ ├── django.py</span><br><span class="line">│ │ └── fastapi.py</span><br><span class="line">│ ├── settings.py</span><br><span class="line">│ ├── urls.py</span><br><span class="line">│ └── wsgi.py</span><br><span class="line">└── manage.py</span><br></pre></td></tr></table></figure><p>Editing the new ASGI files:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/asgi/fastapi.py </span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/asgi/__init__.py </span></span><br><span class="line"><span class="keyword">from</span> fastapi.staticfiles <span class="keyword">import</span> StaticFiles</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .django <span class="keyword">import</span> application</span><br><span class="line"><span class="keyword">from</span> .fastapi <span class="keyword">import</span> app</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"app"</span>]</span><br><span class="line"></span><br><span class="line">app.mount(<span class="string">"/django/static"</span>, StaticFiles(directory=<span class="string">"static"</span>), name=<span class="string">"static"</span>)</span><br><span class="line">app.mount(<span class="string">"/django"</span>, application)</span><br></pre></td></tr></table></figure><p>Add this to settings files:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/settings.py</span></span><br><span class="line">...</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line">STATIC_URL = <span class="string">'static/'</span></span><br><span class="line">STATIC_ROOT = os.path.join(BASE_DIR, <span class="string">"static"</span>)</span><br></pre></td></tr></table></figure><p>And then run collectstatic to generate Django’s static files and migrate to create our database:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py collectstatic; ./manage.py migrate</span><br></pre></td></tr></table></figure><p>Running our application now will serve both Django and FastAPI:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uvicorn fastdjango.asgi:app --reload</span><br></pre></td></tr></table></figure><p>FastAPI docs (no endpoint for now):</p><ul><li><a href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a></li></ul><p>Django Admin and Django (no endpoint neither):</p><ul><li><a href="http://127.0.0.1:8000/django/admin/">http://127.0.0.1:8000/django/admin/</a></li><li><a href="http://127.0.0.1:8000/django">http://127.0.0.1:8000/django</a></li></ul><h1 id="User-stories"><a href="#User-stories" class="headerlink" title="User stories"></a>User stories</h1><p>For this example we will create an API for a forum where people can create and read posts.</p><ul><li>As a anonymous user I want to be able to create a new User account.</li><li>As a anonymous user I want to be able to login.</li><li>As an authentified User I want to list all the posts.</li><li>As an authentified User I want to post a new post.</li><li>As an authentified User I want to list my posts.</li></ul><h1 id="Models"><a href="#Models" class="headerlink" title="Models"></a>Models</h1><h2 id="Users"><a href="#Users" class="headerlink" title="Users"></a>Users</h2><p>Start the users app:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py startapp users</span><br></pre></td></tr></table></figure><p>File tree:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">├── fastdjango</span><br><span class="line">├── manage.py</span><br><span class="line">└── users</span><br><span class="line"> ├── __init__.py</span><br><span class="line"> ├── admin.py</span><br><span class="line"> ├── apps.py</span><br><span class="line"> ├── migrations</span><br><span class="line"> │ └── __init__.py</span><br><span class="line"> ├── models.py</span><br><span class="line"> ├── tests.py</span><br><span class="line"> └── views.py</span><br></pre></td></tr></table></figure><p>Settings:</p><p>Add users.apps.UsersConfig to INSTALLED_APPS:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/settings.py</span></span><br><span class="line">...</span><br><span class="line">INSTALLED_APPS = [</span><br><span class="line"> <span class="string">'django.contrib.admin'</span>,</span><br><span class="line"> <span class="string">'django.contrib.auth'</span>,</span><br><span class="line"> <span class="string">'django.contrib.contenttypes'</span>,</span><br><span class="line"> <span class="string">'django.contrib.sessions'</span>,</span><br><span class="line"> <span class="string">'django.contrib.messages'</span>,</span><br><span class="line"> <span class="string">'django.contrib.staticfiles'</span>,</span><br><span class="line"> <span class="string">'users.apps.UsersConfig'</span>,</span><br><span class="line">]</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>User model:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># users/models.py</span></span><br><span class="line"><span class="keyword">import</span> uuid</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.contrib.auth.models <span class="keyword">import</span> AbstractUser</span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">User</span>(<span class="params">AbstractUser</span>):</span></span><br><span class="line"> pubkey = models.UUIDField(</span><br><span class="line"> default=uuid.uuid4, db_index=<span class="literal">True</span>, editable=<span class="literal">False</span>, unique=<span class="literal">True</span></span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> EMAIL_FIELD = <span class="string">"username"</span></span><br><span class="line"> USERNAME_FIELD = <span class="string">"username"</span></span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__str__</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">f"<span class="subst">{self.username}</span> - <span class="subst">{self.first_name}</span> <span class="subst">{self.last_name}</span>"</span></span><br></pre></td></tr></table></figure><p>Settings:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/settings.py</span></span><br><span class="line">USER_MODEL = <span class="string">"users.User"</span></span><br><span class="line">AUTH_USER_MODEL = <span class="string">"users.User"</span></span><br></pre></td></tr></table></figure><h2 id="Forum"><a href="#Forum" class="headerlink" title="Forum"></a>Forum</h2><p>Start the forum app:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py startapp forum</span><br></pre></td></tr></table></figure><p>Settings:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/settings.py</span></span><br><span class="line">INSTALLED_APPS = [</span><br><span class="line"> <span class="string">"django.contrib.admin"</span>,</span><br><span class="line"> <span class="string">"django.contrib.auth"</span>,</span><br><span class="line"> <span class="string">"django.contrib.contenttypes"</span>,</span><br><span class="line"> <span class="string">"django.contrib.sessions"</span>,</span><br><span class="line"> <span class="string">"django.contrib.messages"</span>,</span><br><span class="line"> <span class="string">"django.contrib.staticfiles"</span>,</span><br><span class="line"> <span class="string">"users.apps.UsersConfig"</span>,</span><br><span class="line"> <span class="string">"forum.apps.ForumConfig"</span>,</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>File tree:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── fastdjango</span><br><span class="line">├── forum</span><br><span class="line">│ ├── __init__.py</span><br><span class="line">│ ├── admin.py</span><br><span class="line">│ ├── apps.py</span><br><span class="line">│ ├── migrations</span><br><span class="line">│ │ └── __init__.py</span><br><span class="line">│ ├── models.py</span><br><span class="line">│ ├── tests.py</span><br><span class="line">│ └── views.py</span><br><span class="line">├── manage.py</span><br><span class="line">└── users</span><br></pre></td></tr></table></figure><p>Forum models:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># forum/models.py</span></span><br><span class="line"><span class="keyword">import</span> uuid</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.conf <span class="keyword">import</span> settings</span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Post</span>(<span class="params">models.Model</span>):</span></span><br><span class="line"> pubkey = models.UUIDField(</span><br><span class="line"> default=uuid.uuid4, db_index=<span class="literal">True</span>, editable=<span class="literal">False</span>, unique=<span class="literal">True</span></span><br><span class="line"> )</span><br><span class="line"> title = models.CharField(max_length=<span class="number">100</span>)</span><br><span class="line"> content = models.TextField()</span><br><span class="line"> created_at = models.DateTimeField(auto_now_add=<span class="literal">True</span>)</span><br><span class="line"> updated_at = models.DateTimeField(auto_now=<span class="literal">True</span>)</span><br><span class="line"> author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__str__</span>(<span class="params">self</span>):</span></span><br><span class="line"> <span class="keyword">return</span> self.title</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Meta</span>:</span></span><br><span class="line"> ordering = [<span class="string">"-created_at"</span>]</span><br><span class="line"> get_latest_by = <span class="string">"-created_at"</span></span><br></pre></td></tr></table></figure><p>Migrate:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">./manage.py makemigrations; ./manage.py migrate</span><br></pre></td></tr></table></figure><h1 id="FastAPI-endpoints"><a href="#FastAPI-endpoints" class="headerlink" title="FastAPI endpoints"></a>FastAPI endpoints</h1><p>We will now create the FastAPI endpoints.</p><h2 id="User-registration"><a href="#User-registration" class="headerlink" title="User registration"></a>User registration</h2><p>There will be endpoints for registration and authentication:</p><ul><li>/register</li><li>/login</li></ul><h3 id="Schemas"><a href="#Schemas" class="headerlink" title="Schemas:"></a>Schemas:</h3><p>We will use Djantic to automatically generate the Pydantic schemas basing in our User model.<br>We will also define the JWTPairSchema to serialize the JWT in the /login endpoint. Let’s keep it simple and not define the JWT refresh endpoint for now.</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># users/schemas.py</span></span><br><span class="line"><span class="keyword">from</span> djantic <span class="keyword">import</span> ModelSchema</span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> users.models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"JWTPairSchema"</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">JWTPairSchema</span>(<span class="params">BaseModel</span>):</span></span><br><span class="line"> refresh_token: <span class="built_in">str</span></span><br><span class="line"> access_token: <span class="built_in">str</span></span><br><span class="line"> token_type: <span class="built_in">str</span> = <span class="string">"bearer"</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CreateUserSchema</span>(<span class="params">ModelSchema</span>):</span></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Config</span>:</span></span><br><span class="line"> model = User</span><br><span class="line"> include = (</span><br><span class="line"> <span class="string">"username"</span>,</span><br><span class="line"> <span class="string">"password"</span>,</span><br><span class="line"> <span class="string">"first_name"</span>,</span><br><span class="line"> <span class="string">"last_name"</span>,</span><br><span class="line"> )</span><br></pre></td></tr></table></figure><h3 id="Views"><a href="#Views" class="headerlink" title="Views:"></a>Views:</h3><p>We will use Django’s authentication system and DRF Simple JWT for the authentication.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">```python</span><br><span class="line"># users/views.py</span><br><span class="line">from django.contrib.auth import authenticate</span><br><span class="line">from django.db import IntegrityError</span><br><span class="line">from fastapi import APIRouter, Depends, HTTPException</span><br><span class="line">from fastapi.security import OAuth2PasswordRequestForm</span><br><span class="line">from rest_framework_simplejwt.tokens import RefreshToken</span><br><span class="line"></span><br><span class="line">from .models import User</span><br><span class="line">from .schemas import CreateUserSchema, JWTPairSchema</span><br><span class="line"></span><br><span class="line">router = APIRouter()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">def get_jwt(user: User) -> JWTPairSchema:</span><br><span class="line"> refresh = RefreshToken.for_user(user)</span><br><span class="line"> return JWTPairSchema(</span><br><span class="line"> refresh_token=str(refresh),</span><br><span class="line"> access_token=str(refresh.access_token),</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@router.post("/register", response_model=JWTPairSchema)</span><br><span class="line">def register_user(new_user: CreateUserSchema):</span><br><span class="line"> try:</span><br><span class="line"> user = User.objects.create_user(**new_user.dict())</span><br><span class="line"> return get_jwt(user)</span><br><span class="line"> except IntegrityError:</span><br><span class="line"> raise HTTPException(detail="Cannot create user.", status_code=400)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">@router.post("/login", response_model=JWTPairSchema)</span><br><span class="line">def login(form_data: OAuth2PasswordRequestForm = Depends()):</span><br><span class="line"> user = authenticate(username=form_data.username, password=form_data.password)</span><br><span class="line"> if user is not None:</span><br><span class="line"> return get_jwt(user)</span><br><span class="line"> raise HTTPException(status_code=400, detail="Incorrect username or password.")</span><br></pre></td></tr></table></figure><h3 id="ASGI"><a href="#ASGI" class="headerlink" title="ASGI:"></a>ASGI:</h3><p>Include the user views in the ASGI application:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/asgi/fastapi.py</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> users.views <span class="keyword">import</span> router <span class="keyword">as</span> users_router</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line">app.include_router(users_router, tags=[<span class="string">"auth"</span>], prefix=<span class="string">""</span>)</span><br></pre></td></tr></table></figure><p>Now you can run the server:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uvicorn fastdjango.asgi:app --reload</span><br></pre></td></tr></table></figure><p>And access the swagger at <a href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a>. You can register an account and then login with the same credentials.</p><h2 id="User-authentification"><a href="#User-authentification" class="headerlink" title="User authentification"></a>User authentification</h2><p>We will now create an util to authenticate the user:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p utils/auth; touch utils/__init__.py utils/auth/__init__.py utils/auth/user.py</span><br></pre></td></tr></table></figure><p>We will use FastAPI OAuth2 package and DRF Simple JWT for the authentication:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utils/auth/user.py</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> Depends, HTTPException, status</span><br><span class="line"><span class="keyword">from</span> fastapi.security <span class="keyword">import</span> OAuth2PasswordBearer</span><br><span class="line"><span class="keyword">from</span> rest_framework_simplejwt.authentication <span class="keyword">import</span> JWTAuthentication</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> users.models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"UserAuthentified"</span>]</span><br><span class="line"></span><br><span class="line">oauth2_scheme = OAuth2PasswordBearer(tokenUrl=<span class="string">"/login"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_authentified_user</span>(<span class="params">token: <span class="built_in">str</span> = Depends(<span class="params">oauth2_scheme</span>)</span>) -> User:</span></span><br><span class="line"> jwt_authenticator = JWTAuthentication()</span><br><span class="line"> validated_token = jwt_authenticator.get_validated_token(token)</span><br><span class="line"></span><br><span class="line"> user = jwt_authenticator.get_user(validated_token)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> user:</span><br><span class="line"> <span class="keyword">raise</span> HTTPException(</span><br><span class="line"> status_code=status.HTTP_401_UNAUTHORIZED,</span><br><span class="line"> detail=<span class="string">"Invalid authentication credentials"</span>,</span><br><span class="line"> headers={<span class="string">"WWW-Authenticate"</span>: <span class="string">"Bearer"</span>},</span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">return</span> user</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">UserAuthentified: User = Depends(get_authentified_user)</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utils/auth/__init__.py</span></span><br><span class="line"><span class="keyword">from</span> .user <span class="keyword">import</span> UserAuthentified</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"UserAuthentified"</span>]</span><br></pre></td></tr></table></figure><p>Now we can use UserAuthentified to ensure the user is authenticated and get the user instance in our views.</p><h2 id="Pagination"><a href="#Pagination" class="headerlink" title="Pagination"></a>Pagination</h2><p>We will also define the pagination util using Django paginator so we can limit the number of items in the response.</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkdir -p utils/page; touch utils/page/__init__.py utils/page/paginator.py utils/page/schemas.py</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utils/page/schemas.py</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Generic</span>, <span class="type">Optional</span>, TypeVar</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> pydantic <span class="keyword">import</span> BaseModel, conint</span><br><span class="line"><span class="keyword">from</span> pydantic.generics <span class="keyword">import</span> GenericModel</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.conf <span class="keyword">import</span> settings</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Link</span>(<span class="params">BaseModel</span>):</span></span><br><span class="line"> first: conint(ge=<span class="number">1</span>)</span><br><span class="line"> last: conint(ge=<span class="number">1</span>)</span><br><span class="line"> current: conint(ge=<span class="number">1</span>)</span><br><span class="line"> <span class="built_in">next</span>: conint(ge=<span class="number">1</span>) | <span class="literal">None</span></span><br><span class="line"> prev: conint(ge=<span class="number">1</span>) | <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">DataT = TypeVar(<span class="string">"DataT"</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PageResponse</span>(<span class="params">GenericModel, <span class="type">Generic</span>[DataT]</span>):</span></span><br><span class="line"> data: <span class="type">Optional</span>[<span class="built_in">list</span>[DataT]]</span><br><span class="line"> pages: Link</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PageRequest</span>(<span class="params">BaseModel</span>):</span></span><br><span class="line"> page: conint(ge=<span class="number">1</span>) = <span class="number">1</span></span><br><span class="line"> size: conint(ge=<span class="number">1</span>, le=settings.PAGINATION[<span class="string">"max_size"</span>]) = settings.PAGINATION[</span><br><span class="line"> <span class="string">"default_size"</span></span><br><span class="line"> ]</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utils/page/paginator.py</span></span><br><span class="line"><span class="keyword">from</span> typing <span class="keyword">import</span> <span class="type">Any</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.core.paginator <span class="keyword">import</span> EmptyPage, Paginator</span><br><span class="line"><span class="keyword">from</span> django.db.models <span class="keyword">import</span> QuerySet</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .schemas <span class="keyword">import</span> Link, PageRequest, PageResponse</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">next_page_number</span>(<span class="params">page</span>):</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> page.next_page_number()</span><br><span class="line"> <span class="keyword">except</span> EmptyPage:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">previous_page_number</span>(<span class="params">page</span>):</span></span><br><span class="line"> <span class="keyword">try</span>:</span><br><span class="line"> <span class="keyword">return</span> page.previous_page_number()</span><br><span class="line"> <span class="keyword">except</span> EmptyPage:</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">paginate</span>(<span class="params">page_request: PageRequest, data: QuerySet | <span class="built_in">list</span></span>) -> <span class="built_in">dict</span>[<span class="built_in">str</span>, <span class="type">Any</span>]:</span></span><br><span class="line"> paginator = Paginator(data, page_request.size)</span><br><span class="line"> page = paginator.get_page(page_request.page)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> PageResponse(</span><br><span class="line"> data=<span class="built_in">list</span>(page.object_list),</span><br><span class="line"> pages=Link(</span><br><span class="line"> current=page.number,</span><br><span class="line"> first=<span class="number">1</span>,</span><br><span class="line"> last=paginator.num_pages,</span><br><span class="line"> <span class="built_in">next</span>=next_page_number(page),</span><br><span class="line"> prev=previous_page_number(page),</span><br><span class="line"> ),</span><br><span class="line"> )</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># utils/page/__init__.py</span></span><br><span class="line"><span class="keyword">from</span> .paginator <span class="keyword">import</span> paginate</span><br><span class="line"><span class="keyword">from</span> .schemas <span class="keyword">import</span> PageRequest, PageResponse</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"paginate"</span>, <span class="string">"PageRequest"</span>, <span class="string">"PageResponse"</span>]</span><br></pre></td></tr></table></figure><h2 id="Forum-1"><a href="#Forum-1" class="headerlink" title="Forum"></a>Forum</h2><h3 id="Schemas-1"><a href="#Schemas-1" class="headerlink" title="Schemas:"></a>Schemas:</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># forum/schemas.py</span></span><br><span class="line"><span class="keyword">from</span> djantic <span class="keyword">import</span> ModelSchema</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Post</span><br><span class="line"></span><br><span class="line">__all__ = [<span class="string">"CreatePostSchema"</span>, <span class="string">"PostSchema"</span>]</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostSchema</span>(<span class="params">ModelSchema</span>):</span></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Config</span>:</span></span><br><span class="line"> model = Post</span><br><span class="line"> exclude = (<span class="string">"id"</span>,)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CreatePostSchema</span>(<span class="params">ModelSchema</span>):</span></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Config</span>:</span></span><br><span class="line"> model = Post</span><br><span class="line"> include = (</span><br><span class="line"> <span class="string">"title"</span>,</span><br><span class="line"> <span class="string">"content"</span>,</span><br><span class="line"> )</span><br></pre></td></tr></table></figure><h3 id="Views-1"><a href="#Views-1" class="headerlink" title="Views:"></a>Views:</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># forum/views.py</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> APIRouter</span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> APIRouter, Depends</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> users.models <span class="keyword">import</span> User</span><br><span class="line"><span class="keyword">from</span> utils.auth <span class="keyword">import</span> UserAuthentified</span><br><span class="line"><span class="keyword">from</span> utils.page <span class="keyword">import</span> PageRequest, PageResponse, paginate</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Post</span><br><span class="line"><span class="keyword">from</span> .schemas <span class="keyword">import</span> CreatePostSchema, PostSchema</span><br><span class="line"></span><br><span class="line">router = APIRouter()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.post(<span class="params"><span class="string">"/posts"</span>, response_model=PostSchema</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">list_user_posts</span>(<span class="params">post: CreatePostSchema, user: User = UserAuthentified</span>):</span></span><br><span class="line"> posts = Post.objects.create(author=user, **post.<span class="built_in">dict</span>())</span><br><span class="line"> <span class="keyword">return</span> PostSchema.from_django(posts)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.get(<span class="params"><span class="string">"/posts/mine"</span>, response_model=PageResponse[PostSchema]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">list_user_posts</span>(<span class="params">page: PageRequest = Depends(<span class="params"></span>), user: User = UserAuthentified</span>):</span></span><br><span class="line"> posts = Post.objects.<span class="built_in">filter</span>(author=user)</span><br><span class="line"> <span class="keyword">return</span> paginate(page, posts)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@router.get(<span class="params"><span class="string">"/posts"</span>, response_model=PageResponse[PostSchema]</span>)</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">list_posts</span>(<span class="params">page: PageRequest = Depends(<span class="params"></span>), user: User = UserAuthentified</span>):</span></span><br><span class="line"> posts = Post.objects.<span class="built_in">all</span>()</span><br><span class="line"> <span class="keyword">return</span> paginate(page, posts)</span><br></pre></td></tr></table></figure><h3 id="ASGI-1"><a href="#ASGI-1" class="headerlink" title="ASGI:"></a>ASGI:</h3><p>Add forum router to FastAPI ASGI:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># fastdjango/asgi/fastapi.py</span></span><br><span class="line"><span class="keyword">from</span> fastapi <span class="keyword">import</span> FastAPI</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> forum.views <span class="keyword">import</span> router <span class="keyword">as</span> forum_router</span><br><span class="line"><span class="keyword">from</span> users.views <span class="keyword">import</span> router <span class="keyword">as</span> users_router</span><br><span class="line"></span><br><span class="line">app = FastAPI()</span><br><span class="line"></span><br><span class="line">app.include_router(users_router, tags=[<span class="string">"auth"</span>], prefix=<span class="string">""</span>)</span><br><span class="line">app.include_router(forum_router, tags=[<span class="string">"forum"</span>], prefix=<span class="string">"/forum"</span>)</span><br></pre></td></tr></table></figure><h2 id="Django-Admin"><a href="#Django-Admin" class="headerlink" title="Django Admin"></a>Django Admin</h2><p>We can register our models to Django’s admin:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># users/admin.py</span></span><br><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> User</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@admin.register(<span class="params">User</span>)</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">UserAdmin</span>(<span class="params">admin.ModelAdmin</span>):</span></span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># forum/admin.py</span></span><br><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> Post</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="meta">@admin.register(<span class="params">Post</span>)</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PostAdmin</span>(<span class="params">admin.ModelAdmin</span>):</span></span><br><span class="line"> <span class="keyword">pass</span></span><br></pre></td></tr></table></figure><p>Now you can go to <a href="http://127.0.0.1:8000/django/admin">http://127.0.0.1:8000/django/admin</a> to manage users and forum posts.</p><h1 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h1><p>We developed an API and you can access the swagger (<a href="http://127.0.0.1:8000/docs">http://127.0.0.1:8000/docs</a>) and Django admin (<a href="http://127.0.0.1:8000/django/admin">http://127.0.0.1:8000/django/admin</a>).</p><p>FastAPI provides better performance than Django REST Framework, enforces type hints in the code and has minimal boilerplate code. Django ORM is a great choice for abstracting database operations and managing database migrations.</p><h2 id="Async"><a href="#Async" class="headerlink" title="Async"></a>Async</h2><p>We didn’t cover the use of async in this tutorial, Django ORM doesn’t safely support async yet. But we can <a href="https://docs.djangoproject.com/en/4.0/topics/async/#async-views">use the asgiref library</a> to await Django DB transactions until support for async is released. FastAPI is fully compatible with async views.</p><h2 id="Out-of-the-shell-solutions"><a href="#Out-of-the-shell-solutions" class="headerlink" title="Out of the shell solutions"></a>Out of the shell solutions</h2><p>Instead of combining FastAPI and Django in a project like in this example, there are other options like <a href="https://github.com/vitalik/django-ninja">Django Ninja</a> that has almost 3k stars in Github (as of May 2022). But I recomend directly using FastAPI because most of the <a href="https://github.com/vitalik/django-ninja/blob/master/ninja/main.py">code for the ASGI framework of Django Ninja</a> is an adaptation of <a href="https://github.com/tiangolo/fastapi/blob/master/fastapi/applications.py">FastAPI’s code</a>. FastAPI is wider adapted and will probably provide more comunity support and maintenability.</p><h2 id="Source-code"><a href="#Source-code" class="headerlink" title="Source code"></a>Source code</h2><p>You can find the source code of this project at:<br><a href="https://github.com/phbernardes/fastapi-django">https://github.com/phbernardes/fastapi-django</a></p>]]></content>
<categories>
<category> django-pydantic </category>
</categories>
<tags>
<tag> python </tag>
<tag> django </tag>
<tag> fastapi </tag>
<tag> pydantic </tag>
<tag> djantic </tag>
</tags>
</entry>
</search>