<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Basstardd</title>
    <description>The latest articles on DEV Community by Basstardd (@bastarrd).</description>
    <link>https://dev.to/bastarrd</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F601174%2Fd316f32b-c656-40fa-bbeb-d6f3ebab5a42.jpeg</url>
      <title>DEV Community: Basstardd</title>
      <link>https://dev.to/bastarrd</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bastarrd"/>
    <language>en</language>
    <item>
      <title>The Security Lindy Effect: What Smart Contracts Can Teach Us About Software Security in the Age of AI</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Tue, 14 Apr 2026 08:14:37 +0000</pubDate>
      <link>https://dev.to/bastarrd/the-security-lindy-effect-what-smart-contracts-can-teach-us-about-software-security-in-the-age-of-2nb0</link>
      <guid>https://dev.to/bastarrd/the-security-lindy-effect-what-smart-contracts-can-teach-us-about-software-security-in-the-age-of-2nb0</guid>
      <description>&lt;p&gt;There has always been something uniquely harsh about smart contracts.&lt;/p&gt;

&lt;p&gt;The obvious reason is that mistakes are extremely costly. Once deployed, bugs are not just bugs—they are financial vulnerabilities that hurts a lot!&lt;/p&gt;

&lt;p&gt;Smart contracts operate in one of the most hostile environments in software engineering. From the moment they go live, they are exposed to a global pool of adversaries with unlimited time and strong incentives to break them. Every line of code is continuously tested by attackers. There is no gradual feedback loop. You do not get gentle warnings. You only find out when it is too late.&lt;/p&gt;

&lt;p&gt;This creates a form of evolutionary pressure on software. Contracts that survive in the wild, unexploited over time, gain credibility and a bit of fame (think Uniswap contracts). Those that fail disappear quickly. In that sense, they follow something close to a security Lindy effect: survival becomes a proxy for correctness.&lt;/p&gt;

&lt;p&gt;It is a brutal way to build reliable systems, but it works.&lt;/p&gt;

&lt;p&gt;What is changing now is that this kind of pressure is no longer limited to smart contracts or a small subset of critical open-source systems.&lt;/p&gt;

&lt;p&gt;With the rise of AI, the attack surface of software is being fundamentally transformed. Systems are no longer probed only by human actors. Instead, they are increasingly exposed to automated, persistent, and scalable analysis. Autonomous agents can scan codebases, test APIs, explore edge cases, and search for vulnerabilities continuously. What used to require time, skill, and coordination can now be executed at scale.&lt;/p&gt;

&lt;p&gt;This effectively turns most software into something closer to a smart contract environment. Constant exposure. Continuous probing. No safe assumptions.&lt;/p&gt;

&lt;p&gt;This shift understandably creates concern. It increases the likelihood that weak systems will be discovered and exploited faster. It raises the cost of building and maintaining secure software. It removes the illusion that systems can remain partially broken and still function indefinitely.&lt;/p&gt;

&lt;p&gt;However, there is another side to this transformation.&lt;/p&gt;

&lt;p&gt;The same capabilities that enable offensive pressure can also be used defensively. AI does not only scale attacks; it also scales review, analysis, and improvement. It allows teams to continuously inspect their own systems with a level of persistence and breadth that was previously unrealistic.&lt;/p&gt;

&lt;p&gt;In practice, this means that hardening can become an ongoing process rather than a one-time effort.&lt;/p&gt;

&lt;p&gt;In my own workflow, I already treat AI as a defensive layer. Each night, using the remaining quota of daily tokens, I run a set of autonomous agents over our codebase. These agents scan for bugs, inconsistencies, and potential security vulnerabilities. They also compare the implementation against known best practices and architectural patterns, and generate concrete suggestions for improvement. The output is collected in a dedicated location, effectively creating a continuous stream of recommendations for hardening the system.&lt;/p&gt;

&lt;p&gt;This is not perfect, and it does not replace human judgment. But it changes the baseline. Instead of occasional reviews, the system is subjected to constant internal pressure that mirrors, in a controlled way, the external pressure it will face in production.&lt;/p&gt;

&lt;p&gt;Seen from this perspective, what we are entering is not only a more dangerous environment, but also a more honest one.&lt;/p&gt;

&lt;p&gt;Weak systems will fail faster. Strong systems will emerge more clearly. Over time, this leads to a kind of large-scale hardening of software. Poor assumptions, fragile architectures, and hidden inconsistencies are less likely to survive prolonged exposure.&lt;/p&gt;

&lt;p&gt;Smart contracts and open source are the first widely adopted systems forced to operate under continuous adversarial pressure. They showed that this pressure, while costly, leads to a different standard of thinking about correctness and security.&lt;/p&gt;

&lt;p&gt;AI is now extending that pressure to the rest of the software world—surfacing vulnerabilities and bugs that have been hidden in plain sight, sometimes for decades.&lt;/p&gt;

&lt;p&gt;The result will not be a collapse in security. More probable scenario, painful hardening. On the other side of it? Possibly better software.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>security</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Taming complexity: programming abstractions, epistemic boundaries, and our right to be ignorant!</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Wed, 18 Mar 2026 08:13:34 +0000</pubDate>
      <link>https://dev.to/bastarrd/taming-complexity-programming-abstractions-epistemic-boundaries-and-our-right-to-be-ignorant-4cej</link>
      <guid>https://dev.to/bastarrd/taming-complexity-programming-abstractions-epistemic-boundaries-and-our-right-to-be-ignorant-4cej</guid>
      <description>&lt;p&gt;Programming is complicated! Software stacks are deep, layered, and complex. Human cognitive abilities, time, and energy are limited. And then the central question for each programmer wrestling with this situation becomes: how can we be productive in this endlessly complex computing universe?&lt;/p&gt;

&lt;p&gt;The answer is deceptively simple: hide complexity behind abstractions! Hide complexity behind walls of public interfaces. Layer these kinds of abstractions one onto another as a kind of stack, and you get the modern computing world. Layer upon layer. Each allows us to live in blissful ignorance and to be productive without knowing every layer that sits below or above us!&lt;/p&gt;

&lt;p&gt;These boundaries are “epistemic,” not physical. They allow us to use the ls command without knowing anything about syscalls. They allow you to write a full-fledged Python program without ever knowing about C or CPU instruction sets—or, God forbid, assembly. They allow you to make a request and never know what is going on between the moment you send it and when you receive a nicely formatted JSON response.&lt;/p&gt;

&lt;p&gt;And this has been the main strategy we have used to tame computing complexity all these years. A very successful recipe—just look at the world around you.&lt;/p&gt;

&lt;p&gt;Of course, as you may guess at this point, the main enemy of this new computing order is the “leaky abstraction.” An abstraction that allows internal problems to escape into the outside world and confuse our good programmers, who stare at errors as if they were written by some alien intelligence! A sure sign that they are confronting things produced by something non-terrestrial! Strange words, never-heard-before terms—signals of new territory.&lt;/p&gt;

&lt;p&gt;And then we are forced to acknowledge that there is a whole underworld beneath us, with its own mechanisms and complexities.&lt;/p&gt;

&lt;p&gt;But this pattern is not unique to programming.&lt;/p&gt;

&lt;p&gt;Scientific disciplines are exactly the same move. Physics, chemistry, biology, psychology, sociology—these aren't natural categories carved at the joints of reality. They're epistemic partitions imposed because no single human mind can reason from quarks to civilizations in one pass. A biologist studies cell signaling without solving Schrödinger's equation for every electron involved. A sociologist studies institutional behavior without modeling individual neurons. Each discipline is an abstraction layer that says, “you can reason productively at this level without understanding the level below.”&lt;/p&gt;

&lt;p&gt;And the interfaces between disciplines work the same way as APIs. Chemistry exposes a certain contract to biology—“here’s how molecules bond, here are reaction kinetics”—and biology consumes that interface without caring about quantum mechanics. The contract is: these regularities hold at your level. Don’t look underneath unless something breaks.&lt;/p&gt;

&lt;p&gt;The leaky abstraction problem exists in science too. That’s literally what happens at interdisciplinary boundaries. A biologist hits a problem that can't be solved with biological vocabulary alone—suddenly you need biophysics, or biochemistry. The epistemic boundary has leaked. You're forced into that same cognitive expansion across two worlds, exactly like a Python developer suddenly staring at C segfault traces.&lt;/p&gt;

&lt;p&gt;This human way of wrestling with complexity is a hidden assumption and organizing principle of everything we build. Epistemic partitions are how complexity becomes digestible for our limited cognitive abilities. But now the big question is: what happens when those cognitive limitations are no longer the organizing principle? When they are lifted via AI?&lt;/p&gt;

&lt;p&gt;Will we start to see entirely new type of systems and understanding emerging in a way fundamentally different from the layered abstraction model that human cognition demanded?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>Ship as fast as possible! But not faster than that</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Fri, 27 Feb 2026 10:34:23 +0000</pubDate>
      <link>https://dev.to/bastarrd/ship-as-fast-as-possible-but-not-faster-than-that-40cb</link>
      <guid>https://dev.to/bastarrd/ship-as-fast-as-possible-but-not-faster-than-that-40cb</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gu6opjorzvt74qqzevd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gu6opjorzvt74qqzevd.jpg" alt=" " width="703" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With great love for the Anthropic team and Claude Code CLI, that I'm using on a daily level, a lot, I must say that their /worktree feature is just confusing.&lt;/p&gt;

&lt;p&gt;Claude Code has a "worktree" feature, but it's not exactly worktree as you know it from git. They use the same name to deliver something much less useful, adding to confusion by giving it a slightly different nature. All in all — and to put it bluntly -- kind of bad design.&lt;/p&gt;

&lt;p&gt;git worktree add is precise. You give it a path, a branch. Done. Run session in worktree. You control everything.&lt;/p&gt;

&lt;p&gt;Claude Code's version creates a new branch you didn't ask for, hides it in .claude/worktrees/, doesn't let you pick the branch or location, and when the session ends — leaves everything behind. Orphaned branches, stale git metadata, empty directories.&lt;/p&gt;

&lt;p&gt;The concept of worktree is used here in a strange way. The problem they're solving — isolating agent work from your current branch — is exactly what git worktree already does. One command. No magic.&lt;br&gt;
If they just ran git worktree add   under the hood and let you drive, it would actually be useful. Instead they built an abstraction that hides the one thing that made worktrees useful in the first place: you control it.&lt;/p&gt;

&lt;p&gt;In my personal opinion it looks like Anthropic is starting to pay the price of shipping features at relentless speed. In the last month they've been hitting that wall often and learning the hard way. The desktop app is buggy all around the place — it literally breaks on a daily level in every imaginable way. And we're talking about one of the best product and dev teams in the world.&lt;/p&gt;

&lt;p&gt;Maybe it's time to step back and rethink the shipping approach and quality guardrails in AI driven development. Because the current approach is giving people early draft versions full of half-baked features, which increases frustration and dissatisfaction with an otherwise loved product.&lt;/p&gt;

&lt;p&gt;Now we can remix a bit one popular version of Einstein thought: "Everything should be made as simple as possible, but not simpler than that!" And in the context of new AI driven development we could say: "Dear Anthropic please ship as fast as possible! But not faster than that."&lt;/p&gt;

&lt;p&gt;Practical advice skip /worktree. Use regular git worktree folder_name branch_name. Run claude code session in parallel worktree...&lt;/p&gt;

</description>
      <category>ai</category>
    </item>
    <item>
      <title>There is simplicity underneath complexity: detachable frames, Coroutines and Python Generators</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Thu, 19 Feb 2026 13:27:20 +0000</pubDate>
      <link>https://dev.to/bastarrd/detachable-frames-coroutines-and-generators-2afg</link>
      <guid>https://dev.to/bastarrd/detachable-frames-coroutines-and-generators-2afg</guid>
      <description>&lt;p&gt;Every Python developer learns early: you call a function, it runs, it returns. Simple. And for most of your career, that model holds.&lt;/p&gt;

&lt;p&gt;Until you hit &lt;code&gt;async def&lt;/code&gt;. Until you hit &lt;code&gt;yield&lt;/code&gt;. Until something suspends and resumes and you're not quite sure what's keeping state alive between those moments.&lt;/p&gt;

&lt;p&gt;There's a single mechanism underneath all of it. One trick that Python learned in 2001 and has been reusing ever since. Once you see it, generators, coroutines, and async generators all collapse into variations of the same idea.&lt;/p&gt;

&lt;p&gt;That mechanism is the &lt;strong&gt;detachable frame&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Frame?
&lt;/h2&gt;

&lt;p&gt;Before we can detach a frame, we need to know what one is.&lt;/p&gt;

&lt;p&gt;Every time Python calls a function, it creates a &lt;strong&gt;frame object&lt;/strong&gt;. This is a real thing — not a metaphor, not an abstraction. It's a C struct in CPython, and you can inspect it from Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;show_frame&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_getframe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;locals:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_locals&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;code:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;line:    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_lineno&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;caller:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_back&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;co_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;show_frame&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A frame holds everything a function needs to execute:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;f_locals&lt;/code&gt;&lt;/strong&gt; — the local variables (&lt;code&gt;x = 1&lt;/code&gt; lives here)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;f_code&lt;/code&gt;&lt;/strong&gt; — pointer to the code object (the bytecode blueprint)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;f_lasti&lt;/code&gt;&lt;/strong&gt; — the last bytecode instruction executed (where we are)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;f_back&lt;/code&gt;&lt;/strong&gt; — pointer to the calling frame (the chain back up)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Frames live on the &lt;strong&gt;call stack&lt;/strong&gt;. When you call a function, its frame gets pushed on top. When the function returns, its frame gets popped off. That's the fundamental rhythm of execution in Python. Quite simple: add, execute, return, pop. That's it! All superficial complexity of your programs comes down to this simple data structure, stack! Add, execute, return, pop.  &lt;/p&gt;

&lt;p&gt;But here's the question: what happens to the frame after it's popped?&lt;/p&gt;

&lt;h2&gt;
  
  
  The One-Shot Frame
&lt;/h2&gt;

&lt;p&gt;For a normal function, the answer is simple. It dies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The lifecycle:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;add(1, 2)&lt;/code&gt; is called — frame created, pushed onto the call stack&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;f_locals&lt;/code&gt; gets &lt;code&gt;{'a': 1, 'b': 2}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Bytecode executes: &lt;code&gt;LOAD_FAST a&lt;/code&gt;, &lt;code&gt;LOAD_FAST b&lt;/code&gt;, &lt;code&gt;BINARY_OP +&lt;/code&gt;, &lt;code&gt;RETURN_VALUE&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Frame popped off the stack — &lt;strong&gt;destroyed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;call add(1,2) → frame ON stack → execute → return → frame DESTROYED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One shot. The frame goes on, does its job, comes off, and it's gone forever. You can't get back to it. The locals are gone. The instruction pointer is gone. Everything that function &lt;em&gt;was&lt;/em&gt; while it was running — gone.&lt;/p&gt;

&lt;p&gt;This is how most people think all functions work. Create, run, destroy.&lt;/p&gt;

&lt;p&gt;And for normal functions, they're right.&lt;/p&gt;

&lt;p&gt;In general Python has exactly &lt;strong&gt;four types of functions&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;              &lt;span class="c1"&gt;# 1. Normal function
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;              &lt;span class="c1"&gt;# 2. Generator function
&lt;/span&gt;    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;   &lt;span class="c1"&gt;# 3. Coroutine function
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;      &lt;span class="c1"&gt;# 4. Async generator function
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two binary toggles — has &lt;code&gt;async&lt;/code&gt;? has &lt;code&gt;yield&lt;/code&gt; in the body? — give you a 2x2 matrix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                No yield            Has yield
             ─────────────────   ─────────────────
No async     Normal function     Generator
Has async    Coroutine           Async generator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler decides which type at compilation time. Sets a flag in &lt;code&gt;co_flags&lt;/code&gt;. That's it.&lt;/p&gt;

&lt;p&gt;The normal function is the one we just saw — one-shot frame, create, run, destroy. The other three? They all break that model in the same way. And that way has been available since 2001.&lt;/p&gt;

&lt;h2&gt;
  
  
  What If the Frame Could Survive?
&lt;/h2&gt;

&lt;p&gt;Now imagine a different rule. What if, when a function gives up control, its frame doesn't die? What if it gets popped off the call stack but keeps living — in the heap (part of memory), with all its locals intact, its instruction pointer remembering exactly where it stopped?&lt;/p&gt;

&lt;p&gt;And what if you could push it back onto the stack later and pick up right where you left off?&lt;/p&gt;

&lt;p&gt;That's what I'm calling a &lt;strong&gt;detachable frame&lt;/strong&gt;. You won't find this term in the CPython docs — but the mechanism is there. The &lt;a href="https://github.com/python/cpython/blob/main/InternalDocs/frames.md" rel="noopener noreferrer"&gt;CPython internals&lt;/a&gt; describe how generator and coroutine frames are embedded directly in their objects on the heap, rather than living and dying on the call stack like normal function frames. The concept is real. I'm just giving it a name.&lt;/p&gt;

&lt;p&gt;And Python has had them since 2001.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Detachable Frame: Generators
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you call &lt;code&gt;counter()&lt;/code&gt;, Python does something unexpected — it &lt;strong&gt;doesn't execute the body&lt;/strong&gt;. It creates a generator object, allocates a frame, attaches the frame to the object, and hands it back to you. Not a single line of the body has run.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;           &lt;span class="c1"&gt;# body does NOT execute
&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# &amp;lt;class 'generator'&amp;gt;
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;              &lt;span class="c1"&gt;# a live frame object — it exists!
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_locals&lt;/span&gt;     &lt;span class="c1"&gt;# {} — empty, nothing has run yet
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_lasti&lt;/span&gt;      &lt;span class="c1"&gt;# -1 — instruction pointer at the very start
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame is sitting in the heap, attached to &lt;code&gt;g&lt;/code&gt;, waiting. Now you drive it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# → 1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happened:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The frame gets &lt;strong&gt;pushed onto the call stack&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Execution starts from where &lt;code&gt;f_lasti&lt;/code&gt; points&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;x = 1&lt;/code&gt; runs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yield x&lt;/code&gt; fires — the value &lt;code&gt;1&lt;/code&gt; is sent out to the caller&lt;/li&gt;
&lt;li&gt;The frame gets &lt;strong&gt;popped off the call stack&lt;/strong&gt; — but NOT destroyed&lt;/li&gt;
&lt;li&gt;It goes back to the heap, attached to &lt;code&gt;g&lt;/code&gt;, with all its state preserved
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_locals&lt;/span&gt;     &lt;span class="c1"&gt;# {'x': 1} — state preserved!
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f_lasti&lt;/span&gt;      &lt;span class="c1"&gt;# advanced past the yield
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Call &lt;code&gt;next(g)&lt;/code&gt; again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# → 2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same dance. Frame goes on the stack, resumes from exactly where it left off, &lt;code&gt;x = 2&lt;/code&gt; runs, hits &lt;code&gt;yield&lt;/code&gt;, frame comes off again.&lt;/p&gt;

&lt;p&gt;One more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;# StopIteration("done")
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gi_frame&lt;/span&gt;              &lt;span class="c1"&gt;# None — NOW it's destroyed
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame went on and off the call stack &lt;strong&gt;three times&lt;/strong&gt; before finally being destroyed on &lt;code&gt;return&lt;/code&gt;. Between each trip, it sat in the heap, holding its locals, its instruction pointer, its entire execution state — alive but dormant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One-shot frame:     ON → execute → OFF → destroyed
Detachable frame:   ON → OFF → ON → OFF → ON → OFF → destroyed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole trick. A frame that can leave the call stack and come back. Python learned this in 2001 with generators. And this — not &lt;code&gt;async def&lt;/code&gt;, not &lt;code&gt;await&lt;/code&gt;, not &lt;code&gt;asyncio&lt;/code&gt; — is the mechanism that makes async Python possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Two-Way Door
&lt;/h2&gt;

&lt;p&gt;There's more. &lt;code&gt;yield&lt;/code&gt; isn't just an exit — it's a door that swings both ways.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ping_pong&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;first&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;# send "first" out, pause, wait for input
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;got: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;received&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;second&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;# send "second" out, pause, wait for input
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;got: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;received&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;done&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ping_pong&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;               &lt;span class="c1"&gt;# → "first"   (value comes OUT)
&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# → "second"  (prints: got: hello — value goes IN)
&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;# → StopIteration("done")  (prints: got: world)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;yield&lt;/code&gt; sends a value &lt;strong&gt;out&lt;/strong&gt; and pops the frame off. &lt;code&gt;.send()&lt;/code&gt; pushes the frame back on and delivers a value &lt;strong&gt;in&lt;/strong&gt;. Two-way communication through a suspended frame.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;             yield x                   send(y)
Generator ──────────────→ Caller    Caller ──────────────→ Generator
           "here's x,                "here's y,
            your turn"                your turn"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that twice. This bidirectional channel — yield out, send in, through a detachable frame — is the &lt;em&gt;entire foundation&lt;/em&gt; of Python's async system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repackaging
&lt;/h2&gt;

&lt;p&gt;Now the part that should make you pause.&lt;/p&gt;

&lt;p&gt;In 2013, Python 3.4 introduced &lt;code&gt;asyncio&lt;/code&gt;. It used generators for async I/O. Not a new mechanism — plain generators with &lt;code&gt;yield from&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Python 3.4 — this is how async worked before async existed
&lt;/span&gt;&lt;span class="nd"&gt;@asyncio.coroutine&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generator suspends → event loop takes control → I/O completes → event loop calls &lt;code&gt;.send()&lt;/code&gt; → generator resumes.&lt;/p&gt;

&lt;p&gt;Same detachable frame. Same &lt;code&gt;.send()&lt;/code&gt; protocol. The event loop was just a fancy caller doing &lt;code&gt;next()&lt;/code&gt; and &lt;code&gt;.send()&lt;/code&gt; based on I/O readiness instead of iteration needs.&lt;/p&gt;

&lt;p&gt;It worked. But it was confusing — is this generator producing values or doing async I/O? The syntax doesn't tell you. You had to squint.&lt;/p&gt;

&lt;p&gt;So in 2015, Python 3.5 introduced &lt;code&gt;async def&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt; (PEP 492). And here's the thing — &lt;strong&gt;no new frame machinery was built.&lt;/strong&gt; None. The detachable frame mechanism is identical. What changed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A new type.&lt;/strong&gt; Coroutine objects are not generator objects. Different type, different name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Renamed attributes.&lt;/strong&gt; Same internals, new labels:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generator              Coroutine
─────────              ─────────
g.gi_frame      →      coro.cr_frame
g.gi_code       →      coro.cr_code
g.gi_running    →      coro.cr_running
g.gi_yieldfrom  →      coro.cr_await
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Guardrails.&lt;/strong&gt; You can't accidentally mix them up anymore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;my_coroutine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;     &lt;span class="c1"&gt;# TypeError!
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;my_generator&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;         &lt;span class="c1"&gt;# TypeError!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One flag.&lt;/strong&gt; One bit in &lt;code&gt;co_flags&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal function:  co_flags = 0b0000_0011
Async function:   co_flags = 0b1000_0011
                               ^
                               CO_COROUTINE — this one bit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the VM sees this flag at call time, it says: "don't execute the body, wrap it in a coroutine object." Same thing it does for generators with &lt;code&gt;CO_GENERATOR&lt;/code&gt;. Same logic, same mechanism.&lt;/p&gt;

&lt;p&gt;That's it. &lt;code&gt;async/await&lt;/code&gt; is a repackaging of generator frame suspension with type safety and clearer intent. New clothes on an old engine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Only Difference That Matters
&lt;/h2&gt;

&lt;p&gt;If the engine is the same, what's actually different?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who calls &lt;code&gt;.send()&lt;/code&gt; to put the frame back on the stack.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That's it. That's the whole thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generator:        YOUR code calls next()/send()      — you decide when to resume
Coroutine:        EVENT LOOP calls .send()            — the orchestrator decides
Async generator:  BOTH take turns                     — two drivers, one frame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The frame doesn't know or care who wakes it up. It gets pushed on the stack, runs until it suspends, gets popped off, and waits. The driver is external to the mechanism.&lt;/p&gt;

&lt;p&gt;A generator suspends at &lt;code&gt;yield&lt;/code&gt; and waits for your code to ask for the next value. A coroutine suspends at &lt;code&gt;await&lt;/code&gt; and waits for the event loop to signal that I/O is ready. Same suspension. Different reason. Different caller.&lt;/p&gt;

&lt;p&gt;You can prove this to yourself right now. Drive a coroutine manually, without any event loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;simple&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;

&lt;span class="n"&gt;coro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;simple&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;coro&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# YOU are the event loop
&lt;/span&gt;&lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;StopIteration&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# 42
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;.send(None)&lt;/code&gt; pushes the frame on the stack, the body executes, &lt;code&gt;return 42&lt;/code&gt; triggers &lt;code&gt;StopIteration&lt;/code&gt; with the value. Same protocol as generators. Because it &lt;em&gt;is&lt;/em&gt; the same protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Async Generator: Both Reasons at Once
&lt;/h2&gt;

&lt;p&gt;The fourth type in our matrix — &lt;code&gt;async def&lt;/code&gt; with &lt;code&gt;yield&lt;/code&gt; — creates something that suspends for &lt;strong&gt;two different reasons&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="c1"&gt;# returns ["a", "b", "c"]
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="c1"&gt;# returns ["d", "e", "f"]
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;stream_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trace the frame:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;anext() → frame ON stack
  → hits await fetch()
  → frame OFF (reason: I/O — event loop will resume)

I/O returns → frame ON stack
  → data = ["a", "b", "c"], enters loop
  → yield "a"
  → frame OFF (reason: producing value — async for will resume)
  → consumer prints "a"

anext() → frame ON → yield "b" → frame OFF → prints "b"
anext() → frame ON → yield "c" → frame OFF → prints "c"

anext() → frame ON (loop ends, continues to next line)
  → hits await fetch()
  → frame OFF (reason: I/O — event loop resumes)

I/O returns → frame ON → yield "d" → frame OFF → prints "d"
anext() → frame ON → yield "e" → frame OFF → prints "e"
anext() → frame ON → yield "f" → frame OFF → prints "f"

anext() → frame ON → end of function → StopAsyncIteration → frame DESTROYED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eight suspensions. Two for I/O, six for producing values. Two different drivers taking turns pushing the same frame back onto the stack. The frame doesn't care. It just does its thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Picture
&lt;/h2&gt;

&lt;p&gt;Four function types. One core mechanism. Three variations of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Normal function    → frame ON → execute → frame OFF → gone forever
                     one-shot, no suspension

Generator          → frame ON/OFF, driven by next()/for
                     suspends at: yield
                     purpose: produce values

Coroutine          → frame ON/OFF, driven by event loop
                     suspends at: await
                     purpose: wait for I/O

Async generator    → frame ON/OFF, driven by BOTH
                     suspends at: yield AND await
                     purpose: stream values with async I/O
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The normal function is the simple case — frame created, used, destroyed. The other three all share the detachable frame: the frame leaves the call stack, survives in the heap, and comes back when someone calls &lt;code&gt;.send()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;They differ only in the answer to two questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why did the frame suspend?&lt;/strong&gt; To produce a value (&lt;code&gt;yield&lt;/code&gt;), to wait for I/O (&lt;code&gt;await&lt;/code&gt;), or both?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Who puts it back?&lt;/strong&gt; Your code, the event loop, or both?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Why it suspends          Who resumes it
                    ──────────────           ──────────────
Generator           yield (value out)        your code
Coroutine           await (I/O wait)         event loop
Async generator     yield + await            both
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole model. Everything else — &lt;code&gt;asyncio&lt;/code&gt;, &lt;code&gt;uvicorn&lt;/code&gt;, FastAPI's dependency injection, database connection pools — is built on top of this. Abstractions upon abstractions, all the way up. But at the bottom, there's a frame going on and off the call stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Timeline
&lt;/h2&gt;

&lt;p&gt;This didn't appear overnight. It was a twenty-year evolution:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Year  What Happened&lt;/th&gt;
&lt;th&gt;PEP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2001  Generators + &lt;code&gt;yield&lt;/code&gt; — detachable frames are born&lt;/td&gt;
&lt;td&gt;PEP 255&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2005  &lt;code&gt;.send()&lt;/code&gt; / &lt;code&gt;.throw()&lt;/code&gt; — two-way communication&lt;/td&gt;
&lt;td&gt;PEP 342&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2012  &lt;code&gt;yield from&lt;/code&gt; — delegation to sub-generators&lt;/td&gt;
&lt;td&gt;PEP 380&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2013  asyncio — generators repurposed for async I/O&lt;/td&gt;
&lt;td&gt;PEP 3156&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2015  &lt;code&gt;async def&lt;/code&gt; / &lt;code&gt;await&lt;/code&gt; — dedicated type and syntax&lt;/td&gt;
&lt;td&gt;PEP 492&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2016  Async generators — &lt;code&gt;yield&lt;/code&gt; inside &lt;code&gt;async def&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;PEP 525&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Twenty years from detachable frames to the async ecosystem we use today. Not a revolution — an evolution. Each step built on the one before. The frame suspension mechanism from 2001 is still the same mechanism running your FastAPI handlers right now.&lt;/p&gt;

&lt;p&gt;Pull back &lt;code&gt;async/await&lt;/code&gt; and you find generators. Pull back generators and you find detachable frames. Pull back detachable frames and you find the simple insight that a function's state doesn't have to die when it gives up control.&lt;/p&gt;

&lt;p&gt;That's the whole story.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;All concepts in this post can be verified hands-on: create a coroutine, don't await it, inspect &lt;code&gt;cr_frame&lt;/code&gt;, call &lt;code&gt;.send()&lt;/code&gt; yourself. Drive it manually. Build a toy event loop in 20 lines. Once you see that an event loop is just "pick a frame, &lt;code&gt;.send()&lt;/code&gt;, handle what it yields" — the magic evaporates and understanding takes its place.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>UI Tectonic Plates are on the Move?</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Mon, 29 Dec 2025 13:19:27 +0000</pubDate>
      <link>https://dev.to/bastarrd/ui-tectonic-plates-on-the-move-432f</link>
      <guid>https://dev.to/bastarrd/ui-tectonic-plates-on-the-move-432f</guid>
      <description>&lt;p&gt;After 50 years, it looks like the UI tectonic plates are on the move.&lt;/p&gt;

&lt;p&gt;Modern UI is the far cousin of ideas born in the famous Xerox PARC (Palo Alto Research Center) laboratory in the mid-70s. This was the place that invented concepts like windows, icons, menus, and pointers—the WIMP paradigm—as well as files, folders, and the trash can. And, equally famously, Xerox never succeeded in capitalizing on any of those inventions. Something that cannot be said for macOS, Windows, and every other modern OS that took "inspiration" from the Xerox lab at the time.&lt;/p&gt;

&lt;p&gt;This UI paradigm was so successful that we haven't even been able to imagine an alternative. We took it for granted. It shaped how humans interact with machines for the last 50 years—from desktop to web to mobile apps. It just clicked with the way we do the things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottleneck We Stopped Seeing
&lt;/h2&gt;

&lt;p&gt;However, this approach assumes a mouse and keyboard as the input devices. Step back and look at the keyboard: even if you're a blind typist and very fast, using a keyboard is akin to the speed of a dial-up modem. It's an obvious bottleneck in communication between our brains and machines.&lt;/p&gt;

&lt;p&gt;We run around the screen searching for icons, clicking, and typing. And there's a feeling that this cannot be the final UI frontier. There must be a different, more natural, faster, and more intuitive way for humans to talk to machines.&lt;/p&gt;

&lt;p&gt;And, you guessed it, there is: &lt;strong&gt;Voice&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The New Paradigm
&lt;/h2&gt;

&lt;p&gt;If voice increasingly becomes the new brain-computer interface, what will this new UI paradigm look like?&lt;/p&gt;

&lt;p&gt;My guess: it will look much more fluid and intuitive. You will just talk to the computer, and things will magically happen. The UI—with all its icons and windows—won't stand in the way of finishing your tasks. You ask, and it is done. This doesn't mean to say that typing will go away entirely, but it will become more on call when precision is needed. &lt;/p&gt;

&lt;p&gt;What means something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Voice: "Draft an email to Sarah about the Q4 results"&lt;/li&gt;
&lt;li&gt;Keyboard: Fine-tuning the wording, fixing a specific sentence &lt;/li&gt;
&lt;li&gt;Voice: "Send it"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's imagine a classic CRM application through this new lens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Old way:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Go to project page → Press menu → Check table → 
Find export option → Select format → Choose destination → 
Enter file name → Confirm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New way:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Export the Acme project data to my desktop."
"What should I call the file?"
"Q4-summary."
Done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't navigate to a project page, press a menu, and scan a table. You simply ask the machine what you want—and voilà, it's there for you.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbjhg2z9sfgkwswnb00j.png" alt="old and new way" width="800" height="800"&gt;
&lt;/h2&gt;

&lt;p&gt;That's the shift. Things just happen. And the interface? It gets out of the way.&lt;/p&gt;

</description>
      <category>ui</category>
      <category>react</category>
      <category>vue</category>
      <category>angular</category>
    </item>
    <item>
      <title>The Two-Field Mental Model: What Python Frameworks actually Hide From You?</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Mon, 22 Dec 2025 13:42:35 +0000</pubDate>
      <link>https://dev.to/bastarrd/observability-and-the-two-field-mental-model-what-python-frameworks-actually-hide-from-you-1oi9</link>
      <guid>https://dev.to/bastarrd/observability-and-the-two-field-mental-model-what-python-frameworks-actually-hide-from-you-1oi9</guid>
      <description>&lt;p&gt;Frameworks hide imperative complexity behind declarative simplicity. That's fine—until it isn't. And this is the price we pay for comfort and convenience. When abstractions leak, understanding the machinery underneath becomes non-optional. But in most of cases we are just staring at the wall of unintelligible framework code. And that is a kind of a problem.... &lt;/p&gt;

&lt;p&gt;You write &lt;code&gt;@pytest.fixture&lt;/code&gt;. Pytest auto-magically handles the rest.&lt;/p&gt;

&lt;p&gt;You write &lt;code&gt;async def&lt;/code&gt;. FastAPI and Uvicorn's event loop figures out the rest.&lt;/p&gt;

&lt;p&gt;You write &lt;code&gt;Column(String)&lt;/code&gt;. SQLAlchemy maps it to your database. It allows you to define class attributes but actually wait, they are class instance attributes at the end? Did we just break Python? No, just a bit of masking that allows app devs to practice declarative programming without thinking about the inner workings of the framework or library.&lt;/p&gt;

&lt;p&gt;This is the basic deal we make with frameworks and libraries: we declare what we want, and they handle how it happens. This is mainly a good deal—until your fixture mysteriously runs twice, and you end up scratching your head without a clue what could possibly go wrong. At that point, "the framework will handles it" becomes "I have no idea what's going on?"&lt;/p&gt;

&lt;h2&gt;
  
  
  About Two Fields
&lt;/h2&gt;

&lt;p&gt;Most Python libraries/frameworks operate on two distinct layers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Nature&lt;/th&gt;
&lt;th&gt;You See It?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Observable Field&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Declarative&lt;/td&gt;
&lt;td&gt;Yes—this is your code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hidden Field&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Imperative&lt;/td&gt;
&lt;td&gt;No—this is the machinery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt; = expressing &lt;em&gt;what&lt;/em&gt; you want (intent, structure, outcome)&lt;br&gt;
&lt;strong&gt;Imperative&lt;/strong&gt; = executing &lt;em&gt;how&lt;/em&gt; it happens (step-by-step machinery)&lt;/p&gt;

&lt;p&gt;The observable field is comfortable. The hidden field is where bugs live.&lt;/p&gt;
&lt;h2&gt;
  
  
  Seeing Through the Abstraction
&lt;/h2&gt;

&lt;p&gt;Let's pull back the curtain on three common patterns.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Async/Await with FastAPI-Uvicorn
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What you write:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean. Declarative. "Fetch data asynchronously."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happens:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;coro&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;        &lt;span class="c1"&gt;# Creates a coroutine object (not executed yet!)
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;coro&lt;/span&gt;                  &lt;span class="c1"&gt;# Event loop: schedule, suspend, resume, complete
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;async def&lt;/code&gt; doesn't run your function—it creates a coroutine object. That object implements the coroutine protocol (&lt;code&gt;__await__&lt;/code&gt;, &lt;code&gt;send()&lt;/code&gt;, &lt;code&gt;throw()&lt;/code&gt;). The event loop drives execution by repeatedly calling &lt;code&gt;send()&lt;/code&gt; until completion.&lt;/p&gt;

&lt;p&gt;At the level of FastAPI, you define an async router and path—and that's it. Under the hood, this coroutine obj have life on its own! Function needs to be converted into a coroutine object and managed by the event loop. All this happens somewhere deep underground where the majority of devs never dare to go.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Pytest Fixtures
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What you write:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Elegant. Setup, provide value, teardown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What actually happens:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fixture_func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;       &lt;span class="c1"&gt;# Generator object created
&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# __next__ called → setup runs → value yielded
# ... your test executes with 'value' ...
&lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                  &lt;span class="c1"&gt;# Resumes after yield → cleanup runs → StopIteration
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;yield&lt;/code&gt; isn't magic—it's part of the generator function and iterator protocol. Pytest calls this generator function, gets a generator, advances it to get your fixture value, runs your test, then advances it again to trigger cleanup (which ends with &lt;code&gt;StopIteration&lt;/code&gt;).&lt;/p&gt;




&lt;p&gt;Here it's important to notice one "creative misuse" of generators. This is a clever use of &lt;code&gt;yield&lt;/code&gt;—not for its intended purpose (iterating multiple values), but specifically to exploit its suspend/resume behavior for cleanup.&lt;/p&gt;

&lt;p&gt;If you look semantically, &lt;code&gt;return&lt;/code&gt; would be more appropriate for "give back one value." But &lt;code&gt;return&lt;/code&gt; can't do cleanup, so we "abuse" &lt;code&gt;yield&lt;/code&gt; for its superpower: pausing execution and resuming later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What we WANT semantically:
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;      &lt;span class="c1"&gt;# ← more appropriate for "one value"
&lt;/span&gt;    &lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# ← but this is impossible
&lt;/span&gt;
&lt;span class="c1"&gt;# What we MUST do:
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;resource&lt;/span&gt;       &lt;span class="c1"&gt;# ← "creative" use, not really iterating
&lt;/span&gt;    &lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# ← now this works!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By the way, this pattern is so common it has a name: "generator-based resource management" or "generator-based context managers."&lt;/p&gt;




&lt;p&gt;Now back to main article thread =&amp;gt; &lt;/p&gt;

&lt;p&gt;In short, you can live long and happy life at the surface level of the async fixture generator function, but underneath the surface a lot is happening. Pytest manages all of these details for you, allowing your test to run successfully with the context values it needs.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. SQLAlchemy ORM
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What you write:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# Looks like a class attribute
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What actually happens:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# During class creation:
&lt;/span&gt;&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__set_name__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# Column captures its attribute name
&lt;/span&gt;
&lt;span class="c1"&gt;# During instantiation:
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__set__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# Descriptor intercepts assignment
&lt;/span&gt;
&lt;span class="c1"&gt;# During access:
&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="n"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;__get__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# Descriptor intercepts, returns from instance.__dict__
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;name = Column(String)&lt;/code&gt; is a descriptor. When you access &lt;code&gt;user.name&lt;/code&gt;, Python doesn't just return a value—it calls &lt;code&gt;Column.__get__()&lt;/code&gt;. The Column object on the class orchestrates everything; actual data lives on the instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When this bites you:&lt;/strong&gt; You try to access &lt;code&gt;User.name&lt;/code&gt; expecting a default value, but get a &lt;code&gt;Column&lt;/code&gt; object instead. Class-level access and instance-level access behave completely differently.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Protocols Behind the Magic
&lt;/h2&gt;

&lt;p&gt;The hidden field isn't random—it's systematic. Python's protocols are the machinery:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Descriptor Protocol&lt;/strong&gt; → &lt;code&gt;__get__&lt;/code&gt;, &lt;code&gt;__set__&lt;/code&gt;, &lt;code&gt;__set_name__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Iterator Protocol&lt;/strong&gt; → &lt;code&gt;__iter__&lt;/code&gt;, &lt;code&gt;__next__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Manager Protocol&lt;/strong&gt; → &lt;code&gt;__enter__&lt;/code&gt;, &lt;code&gt;__exit__&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coroutine Protocol&lt;/strong&gt; → &lt;code&gt;__await__&lt;/code&gt;, &lt;code&gt;send()&lt;/code&gt;, &lt;code&gt;throw()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Metaclass Machinery&lt;/strong&gt; → &lt;code&gt;__new__&lt;/code&gt;, &lt;code&gt;__init_subclass__&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every framework you use is built on these. When you understand the protocols, you understand the frameworks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;For debugging:&lt;/strong&gt; When declarative code misbehaves, the bug lives in the hidden field. You can't debug what you can't see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For mental models:&lt;/strong&gt; Understanding protocols makes &lt;em&gt;any&lt;/em&gt; library transparent. New framework? Same protocols, different application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For API design:&lt;/strong&gt; If you build libraries, this is your job—hiding imperative complexity behind declarative interfaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For mastery:&lt;/strong&gt; There's a difference between &lt;em&gt;using&lt;/em&gt; Python and &lt;em&gt;understanding&lt;/em&gt; Python. The gap is the hidden field.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Practical Takeaway
&lt;/h2&gt;

&lt;p&gt;You don't need to memorize every protocol. You need to know they exist and where to look.&lt;/p&gt;

&lt;p&gt;Next time something breaks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify what abstraction you're using&lt;/li&gt;
&lt;li&gt;Ask: "What protocol enables this?"&lt;/li&gt;
&lt;li&gt;Look at what the machinery actually does&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So is the deal worth it? Absolutely. Just know that someday you'll need to read the fine print. &lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>architecture</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Solid steel programming: blockchain and hardware programming</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Wed, 28 Feb 2024 14:19:57 +0000</pubDate>
      <link>https://dev.to/bastarrd/solid-steel-programing-blockchain-and-hardware-programming-1n2k</link>
      <guid>https://dev.to/bastarrd/solid-steel-programing-blockchain-and-hardware-programming-1n2k</guid>
      <description>&lt;p&gt;There are notable similarities between programming for hardware devices and programming for blockchain. Both operate within defined constraints and exhibit a deterministic nature. In hardware programming, one must work within the limitations of available energy and memory, acknowledging and adhering to these constraints. Similarly, blockchain programming faces constraints such as the upper gas limit, which restricts the number of computational steps that can be executed, with each step incurring a cost.&lt;br&gt;
Solidity, the programming language for smart contracts on Ethereum, occupies a unique position within the realm of programming languages and technology. It blends the flexibility and expressiveness typical of Turing-complete languages, enabling the implementation of complex logic, with the robustness and rigidity characteristic of hardware programming. This amalgamation of flexibility during development and resilience in production gives rise to new kind of programming. Strange and interesting beast....&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Web3 backend and smart contract development for Python developers Musical NFTs part 17: Deployment time!</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Wed, 22 Nov 2023 21:27:48 +0000</pubDate>
      <link>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-17-deployment-time-276b</link>
      <guid>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-17-deployment-time-276b</guid>
      <description>&lt;p&gt;Now we want to have our Django app up and running somewhere live for other people to see and use. In this version of our app we will use Google App engine. &lt;/p&gt;

&lt;p&gt;First go to Google cloud and open your free account. Google will allocate some free resources automatically to your account and this will be more then enough for start. But also don't forget to go to billing part and to set some alerts when certain price thresholds are hit. Just to avoid any surprise. &lt;/p&gt;

&lt;p&gt;Ones you are there search for App engine and create and select new &lt;code&gt;MusicalNFT&lt;/code&gt; project.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx499ygebmjhs7frqkk9c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx499ygebmjhs7frqkk9c.png" alt=" " width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short what is Google App Engine (GAE)? It is a fully managed, serverless platform for developing and hosting web applications at scale. It has a powerful built-in auto-scaling feature, which automatically allocates more/fewer resources based on demand. GAE natively supports applications written in Python, Node.js, Java, Ruby, C#, Go, and PHP. Alternatively, it provides support for other languages via custom runtimes or Dockerfiles. It has powerful application diagnostics, which you can combine with Cloud Monitoring and Logging to monitor the health and the performance of your app. Additionally, GAE allows your apps to scale to zero, which means that you don't pay anything if no one uses your service.&lt;/p&gt;

&lt;p&gt;In attempt to use GAE first we need to install Google Cloud CLI.&lt;br&gt;
The gcloud CLI allows you to create and manage your Google Cloud resources and services. The installation process differs depending on your operating system and processor architecture. Go ahead and follow the official installation guide for your OS and CPU. =&amp;gt; &lt;a href="https://cloud.google.com/sdk/docs/install#deb" rel="noopener noreferrer"&gt;gcloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To verify the installation has been successful, run:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcloud version

Google Cloud SDK 415.0.0
bq 2.0.84
core 2023.01.20
gcloud-crc32c 1.0.0
gsutil 5.18
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now comes very important step: configuring Django Project to work with GEA. Till this moment we were working all the time in local dev environment, running all  things (Postgres, Stripe, Django server, Celery etc.) on our machine. What we need to do now if we want our app to be live on GEA? First we need to configure our Django project and to tell to GEA that he need to run all those supporting services in the background for our app to be able to run smoothly. That is why let's open our Django &lt;code&gt;settings.py&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;First thing we need to do is to erase defualt secrete keys. Then we will generate new one and pass to our &lt;code&gt;.env&lt;/code&gt; file. And only then import inside our &lt;code&gt;settings.py&lt;/code&gt; again. &lt;/p&gt;

&lt;p&gt;Let's generate new secrete Django key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   $py manage.py shell
    &amp;gt;&amp;gt;&amp;gt;from django.core.management.utils import get_random_secret_key
    &amp;gt;&amp;gt;&amp;gt;secret_key = get_random_secret_key()
    &amp;gt;&amp;gt;&amp;gt;print(secret_key)
    # and you will get some gibberish like this
    &amp;gt;&amp;gt;&amp;gt;^&amp;amp;p@m*nhn#hg6ujgri2sppxr7t8o^mfp3bnj%1%2f72wcr+kkz
    # now pass value you get into `.env` file name of varibale `DJANGO_SECRET_KEY`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why we need secret key? In Django, a secret key plays a vital role in enhancing the security of our application. It helps manage user sessions, protects against Cross-Site Request Forgery (CSRF) attacks, and safeguards your data by generating and verifying cryptographic signatures among other things.&lt;/p&gt;

&lt;p&gt;Now our Django settings should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; from pathlib import Path
import os
from urllib.parse import urlparse

import environ
import io
import pyrebase
import firebase_admin
from firebase_admin import credentials
from google.cloud import secretmanager
from google.oauth2 import service_account


# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


env = environ.Env(DEBUG=(bool, False))
env_file = os.path.join(BASE_DIR, ".env")

if os.path.isfile(env_file):
    # read a local .env file
    env.read_env(env_file)
elif os.environ.get("GOOGLE_CLOUD_PROJECT", None):
    # pull .env file from Secret Manager
    project_id = os.environ.get("GOOGLE_CLOUD_PROJECT")
    client = secretmanager.SecretManagerServiceClient()
    settings_name = os.environ.get("SETTINGS_NAME", "django_setting_two")
    name = f"projects/{project_id}/secrets/{settings_name}/versions/latest"
    payload = client.access_secret_version(name=name).payload.data.decode("UTF-8")
    env.read_env(io.StringIO(payload))
else:
    raise Exception("No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.")

SECRET_KEY = env("SECRET_KEY")
DEBUG = env("DEBUG")

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now set ALLOWED_HOSTS and CSRF_TRUSTED_ORIGINS, we can use the following code snippet from the GAE docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; APPENGINE_URL = env("APPENGINE_URL", default=None)

if APPENGINE_URL:
    # ensure a scheme is present in the URL before it's processed.
    if not urlparse(APPENGINE_URL).scheme:
        APPENGINE_URL = f"https://{APPENGINE_URL}"
    ALLOWED_HOSTS = [
        urlparse(APPENGINE_URL).netloc,
        APPENGINE_URL,
        "localhost",
        "127.0.0.1",
    ]
    CSRF_TRUSTED_ORIGINS = [APPENGINE_URL]
    # SECURE_SSL_REDIRECT = True
else:
    ALLOWED_HOSTS = ["*"]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code fetches APPENGINE_URL from the environment (later we will add to our &lt;code&gt;.env&lt;/code&gt;) and automatically configures &lt;code&gt;ALLOWED_HOSTS&lt;/code&gt; and &lt;code&gt;CSRF_TRUSTED_ORIGINS&lt;/code&gt;. Additionally, it enables &lt;code&gt;SECURE_SSL_REDIRECT&lt;/code&gt; to enforce HTTPS.&lt;/p&gt;

&lt;p&gt;Final version &lt;code&gt;.env&lt;/code&gt; file should look something like this (we still don't have all values but just for information)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_SECRET_KEY=sk_test_xxxxxxxxx
DENIS_PASS=xxxxxxx
ETHEREUM_NETWORK=maticmum
INFURA_PROVIDER=https://polygon-mumbai.infura.io/v3/xxxxxx
SIGNER_PRIVATE_KEY=xxxxxxx
MUSIC_NFT_ADDRESS=0x1D33a553541606E98c74a61D1B8d9fff9E0fa138
STRIPE_ENDPOINT=whsec_GExxxxxxx
OWNER=0x273f4FCa831A7e154f8f979e1B06F4491Eb508B6
DJANGO_SECRET_KEY='^&amp;amp;p@m*nhn#hg6ujgri2sppxr7t8o^mfp3bnj%1%2f72wcr+kkz'
DATABASE_URL=postgres://name:password!@localhost/musicalnft
# DATABASE_URL=postgres://name:password!@//cloudsql/musicnft-405811:europe-west1:musicnft-instance/musicalnft
GS_BUCKET_NAME=musicnft-bucket
APPENGINE_URL=https://musicnft-405811.ew.r.appspot.com/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;later on we will populate all those values. &lt;/p&gt;

&lt;p&gt;Don't forget to add the import at the top of the &lt;code&gt;settings.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; from urllib.parse import urlparse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use Postgres instead of SQLite, we first need to install the database adapter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $pip install psycopg2-binary==2.9.5
    $pip freeze &amp;gt; requirements.txt 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To utilize DATABASE_URL with Django, we can use django-environ's db() method like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  #settings.py
    DATABASES = {'default': env.db()}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now create &lt;code&gt;DATABASE_URL&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; and pass some random value, later on we will set up this value properly. (in general format of this value will go as fallow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;postgres://USER:PASSWORD@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DATABASE_NAME)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we want to do is to replace Django dev server (what is not recommended for any kind of production environment) with Gunicorn. But first what is Gunicorn and why we should want to replace Django default server? The Django development server is lightweight and easy to use, but it's not designed/reccomndent to handle production traffic. It's meant for use during development only. On the other hand, Gunicorn is a WSGI HTTP server that's designed for production use. It's robust, efficient, and can handle multiple requests simultaneously, which is crucial for a production environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $pip install gunicorn==20.1.0
    $pip freeze &amp;gt; requirements.txt 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now very important &lt;code&gt;app.yaml&lt;/code&gt; file. Google App Engine's &lt;code&gt;app.yaml&lt;/code&gt; config file is used to configure your web application's runtime environment. The &lt;code&gt;app.yaml&lt;/code&gt; file contains information such as the runtime, URL handlers, and environment variables.&lt;/p&gt;

&lt;p&gt;Start by creating a new file called &lt;code&gt;app.yaml&lt;/code&gt; in the project root with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    # app.yaml

    runtime: python39
    env: standard
    entrypoint: gunicorn -b :$PORT musical_nft.wsgi:application

    handlers:
    - url: /.*
    script: auto

    runtime_config:
    python_version: 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We defined the entrypoint command that starts the WSGI server.&lt;br&gt;
There are two options for env: standard and flexible. We picked standard since it is easier to get up and running, is appropriate for smaller apps, and supports Python 3.9 out of the box.&lt;br&gt;
Lastly, handlers define how different URLs are routed. We'll define handlers for static and media files later in the tutorial.&lt;/p&gt;

&lt;p&gt;Now let's define &lt;code&gt;.gcloudignore&lt;/code&gt;. A &lt;code&gt;.gcloudignore&lt;/code&gt; file allows you to specify the files you don't want to upload to GAE when deploying an application. It works similarly to a .gitignore file.&lt;/p&gt;

&lt;p&gt;Go ahead and create a &lt;code&gt;.gcloudignore&lt;/code&gt; file in the project root with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   # .gcloudignore

    .gcloudignore

    # Ignore local .env file
    .env

    # If you would like to upload your .git directory, .gitignore file, or files
    # from your .gitignore file, remove the corresponding line
    # below:
    .git
    .gitignore

    # Python pycache:
    __pycache__/

    # Ignore collected static and media files
    mediafiles/
    staticfiles/

    # Ignore the local DB
    db.sqlite3

    # Ignored by the build system
    /setup.cfg
    venv/

    # Ignore IDE files
    .idea/

    README_DEV.md
    celerybeat-schedule
    node_modules
    photos
    smart-contracts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead and initialize the gcloud CLI if you haven't already:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $ gcloud init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;gcloud CLI will ask you about mail and project you would like to associate with this project. It will offer to you all avaliable projects. Choose the one we created (MusciNFT) and gcloud will automatically set all the rest. What means from this point on when ever you deploy your Django app to Google App Engine it will be avalible under that project name.&lt;/p&gt;

&lt;p&gt;To create an App Engine app go to your project root and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$gcloud app create

    # you will get something like this
    You are creating an app for project [musicnft-405811].
    WARNING: Creating an App Engine application for a project is irreversible and the region
    cannot be changed. More information about regions is at
    &amp;lt;https://cloud.google.com/appengine/docs/locations&amp;gt;.

    Please choose the region where you want your App Engine application located:

    [1] asia-east1    (supports standard and flexible)
    [2] asia-east2    (supports standard and flexible and search_api)
    [3] asia-northeast1 (supports standard and flexible and search_api)
    [4] asia-northeast2 (supports standard and flexible and search_api)
    [5] asia-northeast3 (supports standard and flexible and search_api)
    [6] asia-south1   (supports standard and flexible and search_api)
    [7] asia-southeast1 (supports standard and flexible)
    [8] asia-southeast2 (supports standard and flexible and search_api)
    [9] australia-southeast1 (supports standard and flexible and search_api)
    [10] europe-central2 (supports standard and flexible)
    [11] europe-west   (supports standard and flexible and search_api)
    [12] europe-west2  (supports standard and flexible and search_api)
    [13] europe-west3  (supports standard and flexible and search_api)
    [14] europe-west6  (supports standard and flexible and search_api)
    [15] northamerica-northeast1 (supports standard and flexible and search_api)
    [16] southamerica-east1 (supports standard and flexible and search_api)
    [17] us-central    (supports standard and flexible and search_api)
    [18] us-east1      (supports standard and flexible and search_api)
    [19] us-east4      (supports standard and flexible and search_api)
    [20] us-west1      (supports standard and flexible)
    [21] us-west2      (supports standard and flexible and search_api)
    [22] us-west3      (supports standard and flexible and search_api)
    [23] us-west4      (supports standard and flexible and search_api)
    [24] cancel
    Please enter your numeric choice:  11

    Creating App Engine application in project [musicnft-405811] and region [europe-west]....done.                       
    Success! The app is now created. Please use `gcloud app deploy` to deploy your first app.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, now we need to set up our Postgres database inside Cloud SQL dashboard: &lt;a href="https://console.cloud.google.com/sql/" rel="noopener noreferrer"&gt;https://console.cloud.google.com/sql/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ones you are there &lt;code&gt;create new instance&lt;/code&gt; and choose PostgresSQL (enable Compute Engine API if needed). &lt;/p&gt;

&lt;p&gt;Now pass following values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   Instance ID: musicnft-instance
    Password: Enter a custom password or generate it
    Database version: PostgreSQL 14
    Configuration: Up to you
    Region: The same region as your app
    Zonal availability: Up to you

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might also need to enable "Compute Engine API" to create a SQL instance.&lt;/p&gt;

&lt;p&gt;Once the database has been provisioned, you should get redirected to the database details. Take note of the "Connection name".&lt;/p&gt;

&lt;p&gt;Go ahead and enable the Cloud SQL Admin API by searching for "Cloud SQL Admin API" and clicking "Enable". We'll need this enabled to test the database connection. Here is a link (it will route you to your project): &lt;a href="https://console.cloud.google.com/marketplace/product/google/sqladmin.googleapis.com" rel="noopener noreferrer"&gt;https://console.cloud.google.com/marketplace/product/google/sqladmin.googleapis.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To test the database connection and migrate the database we'll use Cloud SQL Auth proxy. The Cloud SQL Auth proxy provides secure access to your Cloud SQL instance without the need for authorized networks or for configuring SSL.&lt;/p&gt;

&lt;p&gt;First, authenticate and acquire credentials for the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$gcloud auth application-default login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, download Cloud SQL Auth Proxy and make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; wget https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64 -O cloud_sql_proxy


    --2023-11-21 00:27:00--  https://dl.google.com/cloudsql/cloud_sql_proxy.linux.amd64
    Resolving dl.google.com (dl.google.com)... 172.217.20.78, 2a00:1450:4017:800::200e
    Connecting to dl.google.com (dl.google.com)|172.217.20.78|:443... connected.
    HTTP request sent, awaiting response... 200 OK
    Length: 19239740 (18M) [application/octet-stream]
    Saving to: ‘cloud_sql_proxy’

    cloud_sql_proxy               100%[===============================================&amp;gt;]  18.35M  1.22MB/s    in 13s     

    2023-11-21 00:27:13 (1.45 MB/s) - ‘cloud_sql_proxy’ saved [19239740/19239740]

    $chmod +x cloud_sql_proxy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the installation is complete open a new terminal window and start the proxy with your connection details like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $./cloud_sql_proxy -instances="PROJECT_ID:REGION:INSTANCE_NAME"=tcp:5432

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where basicaly &lt;code&gt;PROJECT_ID:REGION:INSTANCE_NAME&lt;/code&gt; connection name you saved first time you created database&lt;/p&gt;

&lt;p&gt;And you should see something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 2023/11/21 00:31:03 current FDs rlimit set to 1048576, wanted limit is 8500. Nothing to do here.
    2023/11/21 00:31:04 Listening on 127.0.0.1:5432 for musicnft-405811:europe-west1:musicnft-instance
    2023/11/21 00:31:04 Ready for new connections
    2023/11/21 00:31:05 Generated RSA key in 135.901349ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now connect to localhost:5432 the same way you would if you had Postgres running on your local machine.&lt;/p&gt;

&lt;p&gt;Since GAE doesn't allow us to execute commands on the server, we'll have to migrate the database from our local machine. &lt;/p&gt;

&lt;p&gt;Inside our &lt;code&gt;.env&lt;/code&gt; file in the project root, with the required environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DATABASE_URL=postgres://DB_USER:DB_PASS@localhost/DB_NAME

    # Example `DATABASE_URL`:
    # DATABASE_URL=postgres://django-images:password@localhost/mydb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's make migrations&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $python manage.py migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create superuser&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   $python manage.py createsuperuser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ones you done with &lt;code&gt;superuser&lt;/code&gt; you can move to &lt;code&gt;secret manager&lt;/code&gt;. We used to have local &lt;code&gt;.env&lt;/code&gt; file when we worked in local dev context. But because we are migrating now whole app into Google App Engine, we need to make our &lt;code&gt;.env&lt;/code&gt; varibales present in cloud. And for this we will use Google secret manager. &lt;/p&gt;

&lt;p&gt;Navigate to the Secret Manager dashboard (&lt;a href="https://console.cloud.google.com/security/secret-manager?project=musicnft-405811" rel="noopener noreferrer"&gt;https://console.cloud.google.com/security/secret-manager?project=musicnft-405811&lt;/a&gt;) and enable the API if you haven't already. Next, create a secret named &lt;code&gt;django_settings&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
   DJANGO_SECRET_KEY='^&amp;amp;p@m*nhn#hg6ujgri2sppxr7t8o^mfp3bnj%1%2f72wcr+kkz'
    # local development
    # DATABASE_URL=postgres://ilija:Meripseli1986!@localhost/musicalnft
    # in cloud
    DATABASE_URL=postgres://ilija:Meripseli1986!@//cloudsql/musicnft-405811:europe-west1:musicnft-instance/musicalnft
    GS_BUCKET_NAME=django-music-nft


    # Example `DATABASE_URL`:
    DATABASE_URL=postgres://DB_USER:DB_PASS@//cloudsql/PROJECT_ID:REGION:INSTANCE_NAME/DB_NAME
    # postgres://django-images:password@//cloudsql/indigo-35:europe-west3:mydb-instance/mydb
    GS_BUCKET_NAME=django-images-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to change DATABASE_URL accordingly. PROJECT_ID:REGION:INSTANCE_NAME equals your database connection details.&lt;/p&gt;

&lt;p&gt;You don't have to worry about GS_BUCKET_NAME. This is just the name of a bucket we're going to create and use later.&lt;/p&gt;

&lt;p&gt;Pip installl google-cloud-secret-manager==2.15.1&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
$pip install google-cloud-secret-manager==2.15.1
    $pip freeze &amp;gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To load the environment variables from Secret Manager we can use the following official code snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from pathlib import Path
    import os
    import environ
    from urllib.parse import urlparse
    import io # new
    from google.cloud import secretmanager # new



    # Build paths inside the project like this: BASE_DIR / 'subdir'.
    BASE_DIR = Path(__file__).resolve().parent.parent

    env = environ.Env(DEBUG=(bool, False))

    env_file = os.path.join(BASE_DIR, ".env")


    if os.path.isfile(env_file):
        # read a local .env file
        env.read_env(env_file)
        password = env("DENIS_PASS")
    elif os.environ.get('GOOGLE_CLOUD_PROJECT', None):
        # pull .env file from Secret Manager
        project_id = os.environ.get('GOOGLE_CLOUD_PROJECT')

        client = secretmanager.SecretManagerServiceClient()
        settings_name = os.environ.get('SETTINGS_NAME', 'django_settings')
        name = f'projects/{project_id}/secrets/{settings_name}/versions/latest'
        payload = client.access_secret_version(name=name).payload.data.decode('UTF-8')

        env.read_env(io.StringIO(payload))
    else:
        raise Exception('No local .env or GOOGLE_CLOUD_PROJECT detected. No secrets found.')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is two new imports: io and secretmanager &lt;/p&gt;

&lt;p&gt;Great! It's finally time to deploy our app. To do so, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $ gcloud app deploy

    Services to deploy:

    descriptor:                  [C:\Users\Nik\PycharmProjects\django-images-new\app.yaml]
    source:                      [C:\Users\Nik\PycharmProjects\django-images-new]
    target project:              [indigo-griffin-376011]
    target service:              [default]
    target version:              [20230130t135926]
    target url:                  [https://indigo-griffin-376011.ey.r.appspot.com]


    Do you want to continue (Y/n)?  y

    Beginning deployment of service [default]...
    #============================================================#
    #= Uploading 21 files to Google Cloud Storage               =#
    #============================================================#
    File upload done.
    Updating service [default]...done.
    Setting traffic split for service [default]...done.
    Deployed service [default] to [https://indigo-griffin-376011.ey.r.appspot.com]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You can stream logs from the command line by running:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   $ gcloud app logs tail -s default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your web app in your browser and test if it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $ gcloud app browse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get a 502 Bad Gateway error, you can navigate to Logs Explorer to see your logs.&lt;/p&gt;

&lt;p&gt;If there's a 403 Permission 'secretmanager.versions.access' denied error, navigate to django_settings secret permissions and make sure the default App Engine service account has access to this secret. See solution on &lt;a href="https://stackoverflow.com/questions/62444867/secret-manager-access-denied-despite-correct-roles-for-service-account/67331344#67331344" rel="noopener noreferrer"&gt;StackOverflow&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;And that is it! We have now our toy app fully up and running on Google app engine for world to see&lt;/p&gt;

&lt;p&gt;Code can be found in this &lt;a href="https://github.com/ilijapet/musical_nft_thumbnails/tree/gcloud" rel="noopener noreferrer"&gt;repo &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;p.s. whole this process is defined by great crew at &lt;a href="https://testdriven.io/" rel="noopener noreferrer"&gt;testdrive.io&lt;/a&gt; High recommendation for all their writings!&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>ethereum</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>Web3 backend and smart contract development for Python developers Musical NFTs part 16: Stripe integration</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Tue, 21 Nov 2023 10:25:09 +0000</pubDate>
      <link>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-16-stripe-integration-2mi5</link>
      <guid>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-16-stripe-integration-2mi5</guid>
      <description>&lt;p&gt;At this point we are almost done with our version of Musical NFT platform. What we still need to do is to allow credit card buyers to buy our NFTs with credit card and to deploy our app somewhere live.  &lt;/p&gt;

&lt;p&gt;Let's start with Stripe integrations fore credit card buyers. Update &lt;code&gt;credit_card_user.html&lt;/code&gt; doc with buy button and Buy NFT name. Now &lt;code&gt;creadit_card_user.html&lt;/code&gt; file inside root &lt;code&gt;templates&lt;/code&gt; folder should look something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;table class="table  table-hover"&amp;gt;
        &amp;lt;thead class="table-dark"&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Name&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Email&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Total no. NFTs&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Means of payment&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Buy NFT&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.first_name }} {{ customer.last_name }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.email }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.total_no_of_nfts }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;&amp;lt;form action="" method="post"&amp;gt;
                {%csrf_token%}
                {{orderForm.as_p}}
                &amp;lt;input type="submit" value="Save" class="btn btn-secondary"&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;/td&amp;gt;        
            &amp;lt;td&amp;gt;
            &amp;lt;form action="" method="post"&amp;gt;
                &amp;lt;label for="NFT"&amp;gt;Number of NFTs&amp;lt;/label&amp;gt;
                {%csrf_token%}
                &amp;lt;input  type="number" id="NFT" &amp;gt;&amp;lt;br&amp;gt;
                &amp;lt;/form&amp;gt;
                &amp;lt;input  id="creditCard" type="submit" value="Buy" class="btn btn-secondary"&amp;gt;
                &amp;lt;/td&amp;gt;   
            &amp;lt;/tr&amp;gt;
        &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;        
        {% for card in metadata%}
        {% if forloop.counter0|divisibleby:3 %} &amp;lt;div class="row"&amp;gt;{%  endif %}
        &amp;lt;div class="card m-5 p-2" style="width: 18rem;"&amp;gt;
            {% load static %}
            &amp;lt;img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/&amp;gt;
            &amp;lt;div class="card-body"&amp;gt;
            &amp;lt;h5 class="card-title"&amp;gt;{{card.name}}&amp;lt;/h5&amp;gt;
            &amp;lt;br&amp;gt;
            &amp;lt;p class="card-text"&amp;gt;{{card.description}}&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        {%  if forloop.counter|divisibleby:3 or forloop.last %}&amp;lt;/div&amp;gt; {%  endif %}
        &amp;lt;br&amp;gt;
        {% endfor %}
        &amp;lt;p&amp;gt; credit card &amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's pip install stripe python library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    (env)$pip install stripe==5.5.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, register for a Stripe account (if you haven't already done so) and navigate to the dashboard. Click on "Developers":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa856xq0hgifxzk57kgs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa856xq0hgifxzk57kgs.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click on "API keys":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxr4ba0zu8obqps00n3s8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxr4ba0zu8obqps00n3s8.png" alt=" " width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each Stripe account has four API keys: two for testing and two for production. Each pair has a "secret key" and a "publishable key". Do not reveal the secret key to anyone; the publishable key will be embedded in the JavaScript on the page that anyone can see.&lt;/p&gt;

&lt;p&gt;Currently the toggle for "Viewing test data" in the upper right indicates that we're using the test keys now. That's what we want.&lt;/p&gt;

&lt;p&gt;Add to your &lt;code&gt;.env&lt;/code&gt; file &lt;code&gt;STRIPE_SECRET_KEY&lt;/code&gt; and pass there your secret keys from Stipe. Then at the end of Django &lt;code&gt;settings.py&lt;/code&gt; add this to two new lines of code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY")
STRIPE_PUBLISHABLE_KEY = "pk_test_51MgCeMHGy7fyfctt4TIWxu6ev25bm5180LalTib2YAx2YmBe3IAWNxPHDMSUzn0tx7K4Mrq8aoKIQyzHc8TRWRGG00p8ePmfug" # here your publishable key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you'll need to specify an "Account name" within your "Account settings" at &lt;a href="https://dashboard.stripe.com/settings/account:" rel="noopener noreferrer"&gt;https://dashboard.stripe.com/settings/account:&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp63kailisw5ykeen7cvc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp63kailisw5ykeen7cvc.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, we need to create a product to sell.&lt;/p&gt;

&lt;p&gt;Click "Products" and then "Add product":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs757d8r0lane95i7vd9j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs757d8r0lane95i7vd9j.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add a product name, enter a price, and select "One time":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq968b4jvzhlb7nic4p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmq968b4jvzhlb7nic4p6.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "Save product".&lt;/p&gt;

&lt;p&gt;Now flow should go something like this:&lt;/p&gt;

&lt;p&gt;After the user clicks the purchase button we need to do the following:&lt;/p&gt;

&lt;p&gt;Get Publishable Key&lt;br&gt;
1) Send an XHR request from the client to the server requesting the publishable key&lt;br&gt;
2) Respond with the key&lt;br&gt;
3) Use the key to create a new instance of Stripe.js&lt;/p&gt;

&lt;p&gt;Create Checkout Session&lt;br&gt;
1)Send another XHR request to the server requesting a new Checkout Session ID&lt;br&gt;
2)Generate a new Checkout Session and send back the ID&lt;br&gt;
3)Redirect to the checkout page for the user to finish their purchase&lt;br&gt;
4) Redirect the User Appropriately:&lt;br&gt;
    Redirect to a success page after a successful payment&lt;br&gt;
    Redirect to a cancellation page after a cancelled payment&lt;/p&gt;

&lt;p&gt;Confirm Payment with Stripe Webhooks&lt;br&gt;
1)Set up the webhook endpoint&lt;br&gt;
2)Test the endpoint using the Stripe CLI&lt;br&gt;
3)Register the endpoint with Stripe&lt;/p&gt;

&lt;p&gt;Let's start =&amp;gt; Get Publishable key:&lt;br&gt;
Updated version of &lt;code&gt;base.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    {% load static %}
    &amp;lt;!doctype html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
        &amp;lt;title&amp;gt;Muscial NFT&amp;lt;/title&amp;gt;
        &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"&amp;gt;
        &amp;lt;script src="https://js.stripe.com/v3/"&amp;gt;&amp;lt;/script&amp;gt;  &amp;lt;!-- new --&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'connect_wallet.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;link rel="shortcut icon" type="image" href="{% static 'favicon.ico' %}" &amp;gt;
        &amp;lt;!-- &amp;lt;script type="module" src="{% static 'call_sc.js' %}"&amp;gt;&amp;lt;/script&amp;gt; --&amp;gt;
    &amp;lt;/script&amp;gt;

    &amp;lt;/head&amp;gt;
    &amp;lt;body onload="checkMetaMaskState()"&amp;gt;
    &amp;lt;body&amp;gt;
        {% include "navbar.html"%}
        &amp;lt;div class="container "&amp;gt;       
        &amp;lt;br/&amp;gt;
        &amp;lt;br/&amp;gt;
        {% if messages %}
        {% for message in messages%}
        &amp;lt;div class="alert alert-warning alert-dismissible fade show" role="alert"&amp;gt;
            {{ message }}
            &amp;lt;button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"&amp;gt;&amp;lt;/button&amp;gt;
        &amp;lt;/div&amp;gt;

        {% endfor%}
        {% endif %} 

        {% block content %}
        {% endblock content %}
        &amp;lt;/div&amp;gt;
        &amp;lt;/body&amp;gt;
        &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
        charset="utf-8"
        type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'call_sc.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'stripe_integration.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside our &lt;code&gt;authentication&lt;/code&gt; app folder &lt;code&gt;views.py&lt;/code&gt;,  create new class &lt;code&gt;StripeConfigView&lt;/code&gt; + we should add few new imports as well as one new method to handle XHR request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; #new imports 
    from django.conf import settings # new
    from django.http.response import JsonResponse # new
    from django.views.decorators.csrf import csrf_exempt # new

    class StripeConfigView(TemplateView): # new

        @csrf_exempt
        def get(request):
            stripe_config = {'publicKey': settings.STRIPE_PUBLISHABLE_KEY}
            return JsonResponse(stripe_config, safe=False)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now update our &lt;code&gt;authentication&lt;/code&gt; app level &lt;code&gt;urls.py&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; from django.urls import path

    from .views import HomeView, LogoutUser, RegisterUser, StripeConfigView

    urlpatterns = [
        path("", HomeView.as_view(), name="home"),
        # path("login/", LoginUser.as_view(), name="login"),
        path("logout/", LogoutUser.as_view(), name="logout"),
        path("register/", RegisterUser.as_view(), name="register"),
        path("config/", StripeConfigView.as_view(), name="stripe_config"), # new
    ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, use the Fetch API to make an XHR (XMLHttpRequest) request to the new /config/ endpoint in &lt;code&gt;static/stripe_integrations.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // new
    // Get Stripe publishable key
    fetch("/config/")
    .then((result) =&amp;gt; { return result.json(); })
    .then((data) =&amp;gt; {
    // Initialize Stripe.js
    const stripe = Stripe(data.publicKey);
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Include &lt;code&gt;stripe.js&lt;/code&gt; in head tag of our &lt;code&gt;base.html&lt;/code&gt; just bellow bootstrap&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; `&amp;lt;script src="https://js.stripe.com/v3/"&amp;gt;&amp;lt;/script&amp;gt;` 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this we close down first item from our list:&lt;br&gt;
Get Publishable Key&lt;/p&gt;

&lt;p&gt;Send an XHR request from the client to the server requesting the publishable key&lt;br&gt;
Respond with the key&lt;br&gt;
Use the key to create a new instance of Stripe.js &lt;/p&gt;

&lt;p&gt;What still needs to be done =&amp;gt;&lt;br&gt;
Create Checkout Session&lt;/p&gt;

&lt;p&gt;Send another XHR request to the server requesting a new Checkout Session ID&lt;br&gt;
Generate a new Checkout Session and send back the ID&lt;br&gt;
Redirect to the checkout page for the user to finish their purchase&lt;br&gt;
Redirect the User Appropriately&lt;/p&gt;

&lt;p&gt;Redirect to a success page after a successful payment&lt;br&gt;
Redirect to a cancellation page after a cancelled payment&lt;br&gt;
Confirm Payment with Stripe Webhooks&lt;/p&gt;

&lt;p&gt;Set up the webhook endpoint&lt;br&gt;
Test the endpoint using the Stripe CLI&lt;br&gt;
Register the endpoint with Stripe&lt;/p&gt;

&lt;p&gt;Create Checkout Session&lt;br&gt;
Moving on, we need to attach an event handler to the button's click event which will send another XHR request to the server to generate a new Checkout Session ID.&lt;/p&gt;

&lt;p&gt;inside &lt;code&gt;authentication/views.py&lt;/code&gt; crete new &lt;code&gt;CreateCheckoutSession&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; class CreateCheckoutSession(TemplateView): # new   

    @csrf_exempt
    def get(self, request):
        number_of_nfts = request.GET.get("number")
        domain_url = 'http://localhost:8000/'
        stripe.api_key = settings.STRIPE_SECRET_KEY
        try:
            # Create new Checkout Session for the order
            # Other optional params include:
            # [billing_address_collection] - to display billing address details on the page
            # [customer] - if you have an existing Stripe Customer ID
            # [payment_intent_data] - capture the payment later
            # [customer_email] - prefill the email input in the form
            # For full details see https://stripe.com/docs/api/checkout/sessions/create

            # ?session_id={CHECKOUT_SESSION_ID} means the redirect will have the session ID set as a query param
            checkout_session = stripe.checkout.Session.create(
                success_url=domain_url + 'success?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain_url + 'cancelled/',
                payment_method_types=['card'],
                mode='payment',
                line_items=[
                    {
                        'price_data': {
                            'currency': 'usd',
                            'unit_amount': 2000,
                            'product_data': {
                                'name': 'MusicalNFT',
                            },
                            },
                            'quantity': number_of_nfts,
                    }
                ]
            )
            return JsonResponse({'sessionId': checkout_session['id']})
        except Exception as e:
            return JsonResponse({'error': str(e)})

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we pick up fron front-end number of NFTs user wants. Then we defined a domain_url, assigned the Stripe secret key to stripe.api_key (so it will be sent automatically when we make a request to create a new Checkout Session), created the Checkout Session, and sent the ID back in the response. Take note of the success_url and cancel_url. The user will be redirected back to those URLs in the event of a successful payment or cancellation, respectively. We'll set those views up shortly.&lt;/p&gt;

&lt;p&gt;Don't forget to import &lt;code&gt;stripe&lt;/code&gt; into &lt;code&gt;views.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    import stripe
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add to &lt;code&gt;authentication&lt;/code&gt; &lt;code&gt;urls.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; from django.urls import path

    from .views import HomeView, LogoutUser, RegisterUser, StripeConfigView, CreateCheckoutSession

    urlpatterns = [
        path("", HomeView.as_view(), name="home"),
        path("logout/", LogoutUser.as_view(), name="logout"),
        path("register/", RegisterUser.as_view(), name="register"),
        path("config/", StripeConfigView.as_view(), name="stripe_config"),
        path('create-checkout-session/', CreateCheckoutSession.as_view(), name = "create_checkout_session"), # new
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;XHR Request&lt;/p&gt;

&lt;p&gt;Add the event handler and subsequent XHR request to &lt;code&gt;static/stripe_integrations.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Get Stripe publishable key
    fetch("/config/")
    .then((result) =&amp;gt; { return result.json(); })
    .then((data) =&amp;gt; {
    // Initialize Stripe.js
    console.log("stripe config")
    const stripe = Stripe(data.publicKey);

    // Event handler
    document.querySelector("#creditCard").addEventListener("click", () =&amp;gt; {
    // Get Checkout Session ID
    // pass input values to backend
    let num = Number(document.getElementById("NFT").value);

    fetch(`/create-checkout-session?` + new URLSearchParams({number: `${num}`}))
    .then((result) =&amp;gt; { return result.json(); })
    .then((data) =&amp;gt; {
        console.log(data);
        /// Redirect to Stripe Checkout
        return stripe.redirectToCheckout({sessionId: data.sessionId})
    })
    .then((res) =&amp;gt; {
        console.log(res);
        });
    });
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, after resolving the result.json() promise, we called redirectToCheckout with the Checkout Session ID from the resolved promise.&lt;/p&gt;

&lt;p&gt;Navigate to user profile page on button click you should be redirected to an instance of Stripe Checkout (a Stripe-hosted page to securely collect payment information) with the NFT product information:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qc3tu5aqo005vr646mx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6qc3tu5aqo005vr646mx.png" alt=" " width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can test the form by using one of the several test card numbers that Stripe provides. Let's use 4242 4242 4242 4242. Make sure the expiration date is in the future. Add any 3 numbers for the CVC and any 5 numbers for the postal code. Enter any email address and name. If all goes well, the payment should be processed, but the redirect will fail since we have not set up the /success/ URL yet.&lt;/p&gt;

&lt;p&gt;To confirm a charge was actually made, go back to the Stripe dashboard under "Payments":&lt;/p&gt;

&lt;p&gt;To review, we used the secret key to create a unique Checkout Session ID on the server. This ID was then used to create a Checkout instance, which the end user gets redirected to after clicking the payment button. After the charge occurred, they are then redirected back to the success page.&lt;/p&gt;

&lt;p&gt;Ok, at this point we resolve first two items from our list: 1) Get publishable key and 2) Create Checkout session. What still need to be doe is to 3) Redirect User based on success or failure of transaction.  And after that, finaly,  4) confirm payment over Stripe webhook, 5) minti new NFT to platform to custodial wallet and 6) database bookeeping staff (to keep all things in the sync).&lt;/p&gt;

&lt;p&gt;To redirect user after transacrtion success or failure we can make two additional templates: &lt;code&gt;canceled.html&lt;/code&gt; and &lt;code&gt;success.html&lt;/code&gt; in project root &lt;code&gt;templates folder&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;success.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
        &amp;lt;title&amp;gt;Django + Stripe Checkout&amp;lt;/title&amp;gt;
        &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        {% block content%}
        &amp;lt;section class="section"&amp;gt;
        &amp;lt;div class="container"&amp;gt;
            &amp;lt;p&amp;gt;Your payment succeeded!&amp;lt;/p&amp;gt;
            &amp;lt;a href="{% url 'home' %}"&amp;gt; Return to user profile page &amp;lt;/a&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;/section&amp;gt;
        {% endblock content%}
    &amp;lt;/body&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then &lt;code&gt;canceled.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;!DOCTYPE html&amp;gt;
    &amp;lt;html&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;

        &amp;lt;title&amp;gt;Django + Stripe Checkout&amp;lt;/title&amp;gt;
        &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"&amp;gt;

    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        {% block content %}
        &amp;lt;section class="section"&amp;gt;
        &amp;lt;div class="container"&amp;gt;
            &amp;lt;p&amp;gt;Your payment was cancelled.&amp;lt;/p&amp;gt;
            &amp;lt;a href="{% url 'home' %}"&amp;gt; Return to user page &amp;lt;/a&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;/section&amp;gt;
        {% endblock content %}
    &amp;lt;/body&amp;gt;
    &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then inside &lt;code&gt;authenticaiton&lt;/code&gt; &lt;code&gt;views.py&lt;/code&gt; add this two new classes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  class SuccessView(TemplateView):
        template_name = 'success.html'


    class CancelledView(TemplateView):
        template_name = 'cancelled.html'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This two templates will be used to redirect user to correct page from inside our &lt;code&gt;CreateCheckoutSession&lt;/code&gt; class. &lt;/p&gt;

&lt;p&gt;If tx was succesfule user should see something like this&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac8mzhc21mekmi5wk3w7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fac8mzhc21mekmi5wk3w7.png" alt=" " width="800" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;but before that we need to update our urls.py with two additional paths&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # payments/urls.py
    from django.urls import path

    from . import views

    urlpatterns = [
        path('', views.HomePageView.as_view(), name='home'),
        path('config/', views.stripe_config),
        path('create-checkout-session/', views.create_checkout_session),
        path('success/', views.SuccessView.as_view()), # new
        path('cancelled/', views.CancelledView.as_view()), # new
    ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, refresh the web page at &lt;a href="http://localhost:8000/" rel="noopener noreferrer"&gt;http://localhost:8000/&lt;/a&gt;. Click on the payment button and use the credit card number 4242 4242 4242 4242 again along with the rest of the dummy info. Submit the payment. You should be redirected back to &lt;a href="http://localhost:8000/success/" rel="noopener noreferrer"&gt;http://localhost:8000/success/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To confirm a charge was actually made, go back to the Stripe dashboard under "Payments":&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjg31zb76nti9gsgglgh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjg31zb76nti9gsgglgh.png" alt=" " width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To review, we used the secret key to create a unique Checkout Session ID on the server. This ID was then used to create a Checkout instance, which the end user gets redirected to after clicking the payment button. After the charge occurred, they are then redirected back to the success page.&lt;/p&gt;

&lt;p&gt;At this point we have just few more things to add to finish credit card functionaliy. Precasly three more things: 1) confirm payment over Stripe webhook; 2) minti new NFT to custodial wallet ones confirmaton arrive over wevhook and 3) user database bookeeping.&lt;/p&gt;

&lt;p&gt;Ok, webhooks! Our app works well at this point, but we still can't programmatically confirm payments and  run smart contract related code if a payment was successful. One of the easiest ways to get notified when the payment goes through is to use a callback or so-called Stripe webhook. We'll need to create a simple endpoint in our application, which Stripe will call whenever an event occurs (e.g., when a user buys a NFT). By using webhooks, we can be absolutely sure the payment went through successfully (and based on that mint new NFT to custodial wallet and asisgne ownership to the user in our Postgres database). In order to use webhooks, we need to:&lt;br&gt;
Create Stripe request handler inside our &lt;code&gt;views.py&lt;/code&gt; &lt;br&gt;
Test the endpoint using the Stripe CLI&lt;br&gt;
Register the endpoint with Stripe&lt;/p&gt;

&lt;p&gt;Create a new view functon called &lt;code&gt;stripe_webhook&lt;/code&gt; which prints a message every time a payment goes through successfully (more about code in comments and bellowe code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from django.http.response import JsonResponse, HttpResponse

    # payments/views.py
    @csrf_exempt
    def stripe_webhook(request):
        stripe.api_key = settings.STRIPE_SECRET_KEY
        endpoint_secret = settings.STRIPE_ENDPOINT_SECRET
        payload = request.body
        sig_header = request.META['HTTP_STRIPE_SIGNATURE']
        event = None

        try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, endpoint_secret
            )
        except ValueError as e:
            # Invalid payload
            return HttpResponse(status=400)
        except stripe.error.SignatureVerificationError as e:
            # Invalid signature
            return HttpResponse(status=400)

        # Handle the checkout.session.completed event
        if event['type'] == 'checkout.session.completed':
            # Mint new NFT
            result = contract_interface.mint_nft("ipfs://uri.test")
            suc, result = contract_interface.event()
            if suc:
                try:
                    # if tx was sucesfule update custodial wallet related DB as well as user
                    c1 = Customer.objects.get(eth_address=result.args.owner)
                    if result.args.numberOfNFT not in c1.nft_ids:
                        # update custodial wallet information db
                        c1.nft_ids.append(result.args.numberOfNFT)
                        c1.total_no_of_nfts += 1
                        c1.save()
                        # update customer db
                        user = event["data"]["object"]["customer_details"]["name"]
                        name, last = user.split()
                        c2 = Customer.objects.get(first_name=name, last_name=last)
                        c2.nft_ids.append(result.args.numberOfNFT)
                        c2.total_no_of_nfts += 1         
                        c2.save()   
                        print("Payment success!)                                
                except Exception as e:
                    print (e)
        return HttpResponse(status=200)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then update &lt;code&gt;urls.py&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from django.urls import path

    from . import views

    urlpatterns = [
        path('', views.HomePageView.as_view(), name='home'),
        path('config/', views.stripe_config),
        path('create-checkout-session/', views.create_checkout_session),
        path('success/', views.SuccessView.as_view()),
        path('cancelled/', views.CancelledView.as_view()),
        path('webhook/', views.stripe_webhook), # new
    ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Logic here is that over our webhook we are listen for request from Stripe. Then ones we get that request we are checking if sender is authroized, if type is completed and there is no any error alonge the way. Only if this is the case we assigne custodial wallet user and assigne id from newly minted NFT as well as increase total number of NFTs we have in custodial wallet. Then we query our database for user who acctualy pay this NFT with his card and then update his &lt;code&gt;nft_ids&lt;/code&gt; field and &lt;code&gt;total_no_of_nfts&lt;/code&gt;. Because he payed with credit card we minted new NFT to custodial wallet not to his one (he don't have one). But also in db we made record about the fact that he is owner of this NFT minted to custodial wallet.&lt;/p&gt;

&lt;p&gt;Now let's test our webhook. &lt;/p&gt;

&lt;p&gt;We'll use the Stripe CLI to test the webhook.&lt;/p&gt;

&lt;p&gt;Once downloaded and installed, run the following command in a new terminal window to log in to your Stripe account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $ stripe login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command should generate a pairing code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Your pairing code is: peach-loves-classy-cozy
    This pairing code verifies your authentication with Stripe.
    Press Enter to open the browser (^C to quit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By pressing Enter, the CLI will open your default web browser and ask for permission to access your account information. Go ahead and allow access. Back in your terminal, you should see something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   &amp;gt; Done! The Stripe CLI is configured for Django Test with account id acct_&amp;lt;ACCOUNT_ID&amp;gt;
    Please note: this key will expire after 90 days, at which point you'll need to re-authenticate.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, we can start listening to Stripe events and forward them to our endpoint using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $ stripe listen --forward-to localhost:8000/webhook/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will also generate a webhook signing secret:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;gt; Ready! Your webhook signing secret is whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (^C to quit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In order to initialize the endpoint, add the secret to the settings.py file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    STRIPE_SECRET_KEY = env("STRIPE_SECRET_KEY")
    STRIPE_PUBLISHABLE_KEY = "pk_test_51MybU0KtroSirNQXi4fuyC99SsypbcWbqLZtfYtGWTUmwTyoNkPaPvu7vy2twd5JjyzHTaL9EirWX7GsFJV3xFsj00xVvZo3C8" 
    STRIPE_ENDPOINT_SECRET=env("STRIPE_ENDPOINT")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure that you have in your root &lt;code&gt;.env&lt;/code&gt; STRIPE_ENDPOINT_SECRET variable&lt;/p&gt;

&lt;p&gt;Stripe will now forward events to our endpoint. To test, run another test payment through with 4242 4242 4242 4242. In your terminal, you should see the Payment was successful. message.&lt;/p&gt;

&lt;p&gt;Once done, stop the stripe listen --forward-to localhost:8000/webhook/ process.&lt;/p&gt;

&lt;p&gt;Finally, after deploying your app, we will register the endpoint in the Stripe dashboard, under Developers &amp;gt; Webhooks. This will generate a webhook signing secret for use in your production app.&lt;/p&gt;

&lt;p&gt;With this basically we have also credit card buyer covered. And what's left in this moment is deployment of our platform somewhere live. And as you can guess this will be topic of our final blog post...   &lt;/p&gt;

&lt;p&gt;Code can be found in this &lt;a href="https://github.com/ilijapet/musical_nft_thumbnails" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;p.s. Huge thanks to &lt;a href="https://testdriven.io/" rel="noopener noreferrer"&gt;tesdrive.io&lt;/a&gt; for all knowledge they share!&lt;/p&gt;

</description>
      <category>python</category>
      <category>stripe</category>
      <category>ethereum</category>
      <category>django</category>
    </item>
    <item>
      <title>Web3 backend &amp; smart contract development for Python developers part 15: Listening on-chain events with Celery and web3.py</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Fri, 17 Nov 2023 22:09:17 +0000</pubDate>
      <link>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-15-listening-on-chain-events-with-celery-5cn1</link>
      <guid>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-15-listening-on-chain-events-with-celery-5cn1</guid>
      <description>&lt;p&gt;Now it is time to plug blockchain part into Django and database and to put all things together. General flow will go something like this: 1) cypto user order let's say 1 NFT and pay in crypto from his user profile. 2) We will listen emitted events from Polygin Mumbai on our backend and filter the one associated with our &lt;code&gt;MusicNFT&lt;/code&gt; contract. If &lt;code&gt;newNFTMinted&lt;/code&gt; event is there we will take out ID of newly minted NFT as well as buyer address. Then based on user address we will query our database and assign customer to variable. If NFT ID is not in his collection we will append ID to &lt;code&gt;nft_ids&lt;/code&gt; filed and for one increase &lt;code&gt;total_no_of_nft&lt;/code&gt; field. With this we will bring in sync database on backend and data in contract storage. &lt;/p&gt;

&lt;p&gt;Now, during this toy project a lot of different patterns emerged that most probably you will often confront in your web3 day to day job. And listening for smart contract events on backend and updating database accordingly is definitely one of them. And this can be done on myriad of different ways. Here is one possible solution =&amp;gt;&lt;/p&gt;

&lt;p&gt;We will use Celery (task/process) and RabitMQ (message/task broker). &lt;/p&gt;

&lt;p&gt;Let's start by installing our message broker &lt;code&gt;rabbitmq-server&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   #install message broker
    $sudo apt install rabbitmq-server

    # enable message broker
    $sudo systemctl enable rabbitmq-server

    # start message broker
    $ sudo systemctl start rabbitmq-server

    IMPORTANT: If you are working from `WSL2` this command will throw error:
    `System has not been booted with systemd as init system (PID 1). Can't operate.
    Failed to connect to bus: Host is down`

    That is why in case of `WSL2` we need to use another command (reasons for this are a bit deep and you can find more in this nice stack exchange "essay" explaining all technical intricacy https://askubuntu.com/questions/1379425/system-has-not-been-booted-with-systemd-as-init-system-pid-1-cant-operate):



 $sudo service rabbitmq-server start

    If everything whent well you should get seomething like this 
    $* Starting RabbitMQ Messaging Server rabbitmq-server 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this we have broker server up and running. Now we need Celery related staff. Let's go first pip install &lt;code&gt;Celery&lt;/code&gt; (don't forget to activate virtual environment)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $pip install celery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside Django project directory &lt;code&gt;./musical_nft&lt;/code&gt; create new file &lt;code&gt;celery.py&lt;/code&gt;. Content of file should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# celery.py
    from __future__ import absolute_import, unicode_literals
    import os
    from celery import Celery

    # set the default Django settings module for the 'celery' program.
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'musical_nft.settings')

    app = Celery('musical_nft', broker="pyamqp://guest@localhost//")

    # Using a string here means the worker doesn't have to serialize
    # the configuration object to child processes.
    app.config_from_object('django.conf:settings', namespace='CELERY')

    # Load task modules from all registered Django app configs.
    app.autodiscover_tasks()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in our &lt;code&gt;authentication&lt;/code&gt; app folder we should create new &lt;code&gt;tasks.py&lt;/code&gt; file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; # tasks.py
    from celery import shared_task
    from web3_interface import contract_interface
    from .models import Customer


    @shared_task
    def event_listener():
        suc, result = contract_interface.event()
        if suc:
            try:        
                c1 = Customer.objects.get(eth_address=result.args.owner)
                if result.args.numberOfNFT not in c1.nft_ids:
                    # update user db
                    c1.nft_ids.append(result.args.numberOfNFT)
                    c1.total_no_of_nfts += 1
                    c1.save()
            except Exception as e:
                print (e)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in our Django &lt;code&gt;musical_nft/settings.py&lt;/code&gt; we should add Celery relevant setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  # Celery settings
    CELERY_BROKER_URL = 'pyamqp://guest@localhost//'
    CELERY_RESULT_BACKEND = 'rpc://'
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    CELERY_ACCEPT_CONTENT = ['json']
    CELERY_TIMEZONE = 'UTC'
    CELERY_ENABLE_UTC = True
    CELERY_BEAT_SCHEDULE = {
        'event_listener': {
            'task': 'authentication.tasks.event_listener',
            'schedule': 5.0,  # Run every 5 seconds
        },
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then update &lt;code&gt;__init__.py&lt;/code&gt; file from &lt;code&gt;musical_nft&lt;/code&gt; folder with followinig code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from __future__ import absolute_import, unicode_literals

    # This will make sure the app is always imported when
    # Django starts so that shared_task will use this app.
    from .celery import app as celery_app

    __all__ = ('celery_app',)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What we need now is new &lt;code&gt;contract_interface&lt;/code&gt; class where we will do all web3 related staff (listening for the &lt;code&gt;newNFTMinted&lt;/code&gt; etc.). But first let's update our Django &lt;code&gt;customer&lt;/code&gt; model with one additional field for user ETH address (we will use this field to pull correct user from DB based on &lt;code&gt;newNFTMinted&lt;/code&gt; &lt;code&gt;arg.owner&lt;/code&gt; of person who bought NFT). &lt;/p&gt;

&lt;p&gt;Just add to &lt;code&gt;models.py&lt;/code&gt; inside &lt;code&gt;authentication&lt;/code&gt; app one line (now our &lt;code&gt;Customer&lt;/code&gt; models should look something like this):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from django.contrib.postgres.fields import ArrayField
    from django.db import models


    class Customer(models.Model):
        CRYPTO = "CRYPTO"
        CREDIT = "CREDIT"

        OPTIONS = [(CRYPTO, "cypto buyer"), (CREDIT, "credit card buyer")]

        created_at = models.DateTimeField(auto_now=True)
        first_name = models.CharField(max_length=50, blank=True)
        last_name = models.CharField(max_length=50, blank=True)
        username = models.CharField(max_length=50, blank=True)
        eth_address = models.CharField(max_length=100, blank=True)
        email = models.EmailField(max_length=250, blank=True)
        type = models.CharField(
            max_length=20,
            choices=OPTIONS,
            # default="CRYPTO"
        )
        total_no_of_nfts = models.IntegerField(default=0)
        nft_ids = ArrayField(
            models.IntegerField(null=True, blank=True), default=list
        )

        nft_metadata = models.ManyToManyField(NFTMetadata)

        def test_function():
            return list()

        def __str__(self):
            return f"{self.first_name} {self.last_name}"

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then follow standard procedure: &lt;code&gt;python manage.py makemigrations&lt;/code&gt; &amp;amp; &lt;code&gt;python manage.py migrate&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Ones this is done we can go to Django shell and add to our user address from which we will buy first NFT (in real life user should have ability on front-end to add or change his eth address. But for test we will do by the hand):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $py manage.py shell   
    &amp;gt;&amp;gt;&amp;gt;from authentication.models import Customer

    # pull user from database. put your desired pk/id 
    &amp;gt;&amp;gt;&amp;gt; c1 = Customer.objects.get(pk=10)

    # update newly added eth_address field with address from which you will buy NFT
    &amp;gt;&amp;gt;&amp;gt;c1.eth_address = "0xB4A5D329e35F83a2e6AB5a74d56f5bD67EaB8d83"
    &amp;gt;&amp;gt;&amp;gt;c1.save()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this we have user in database with his eth address. Ones we pull information's from smart contract event we will query our database for user with that address and update number of NFTs he own as well as add newly minted NFT id. &lt;/p&gt;

&lt;p&gt;What means that now we need &lt;code&gt;web3&lt;/code&gt; python class which will be part of celery task. Main purpose of this class is to listen for this specific smart contract event (&lt;code&gt;newNFTminted&lt;/code&gt; emited ones &lt;code&gt;buyNFT&lt;/code&gt; is successfully executed).  &lt;/p&gt;

&lt;p&gt;We will organize web3 part as new folder in project root directory. Name of folder will be &lt;code&gt;web3_interface&lt;/code&gt;. We will transform this folder into Python package (to be imported in our &lt;code&gt;tasks.py&lt;/code&gt; file) by adding &lt;code&gt;__init__.py&lt;/code&gt; inside &lt;code&gt;web3_interface&lt;/code&gt; folder. Inside &lt;code&gt;__init__.py&lt;/code&gt; file add following code (explanation in comments):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  from .sc_interface import Interface
    import os
    import json

    abidir = os.path.dirname(os.path.abspath(__file__))

    # functino for smart contract ABI loading
    def load_abi(name):
        filename = os.path.join(abidir, f"{name}.json")
        return json.load(open(filename))

    # calling this function with MusicNFT.json as argument
    musicNFT = load_abi("MusicNFT")

    # instatitatin Interface class (yet to be made) with musicNFT ABI as argument
    contract_interface = Interface(musicNFT["abi"])

    # now contract interface is ready to be used imported and used in our `tasks.py` 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside &lt;code&gt;web3_interface&lt;/code&gt; you need to copy &lt;code&gt;MusicNFT.json&lt;/code&gt; ABI and create &lt;code&gt;sc_interface.py&lt;/code&gt;. Content of this file should look something like this (here we have all our web3 related staff)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
from web3 import Web3
from web3.middleware import geth_poa_middleware
from web3._utils.events import get_event_data
from eth_abi.codec import ABICodec
from dotenv import load_dotenv


load_dotenv()


class Contracts:
    def __init__(self, musicNFT):
        self.musicNFT = musicNFT
        self.musicNFT_address = os.environ.get("MUSIC_NFT_ADDRESS")
        self.private_key = os.environ.get("SIGNER_PRIVATE_KEY")
        self.w3Provider = Web3(Web3.HTTPProvider(os.environ.get("INFURA_PROVIDER")))
        self.codec: ABICodec = self.w3Provider.codec
        self.w3Provider.middleware_onion.inject(geth_poa_middleware, layer=0)
        self.nft_contract = self.w3Provider.eth.contract(
            address=self.musicNFT_address, abi=self.musicNFT
        )


class Interface(Contracts):
    def __init__(self, musicNFT):
        Contracts.__init__(self, musicNFT)
        self.event_template = self.nft_contract.events.newNFTMinted


    def handle_event(self, event, event_template):
        try:
            result = get_event_data(
                self.codec, event_template._get_event_abi(), event
            )
            return True, result
        except Exception as e:
            print(e)
            return False, None

    def event(self):
        block_number = self.w3Provider.eth.block_number
        events = self.w3Provider.eth.get_logs(
            {
                "fromBlock": block_number - 5,
                "toBlock": "latest",
                "address": self.musicNFT_address,
            }
        )

        try:
            for event in events:
                suc, result = self.handle_event(
                    event=event, event_template=self.event_template
                )
                if suc:
                    return (True, result)
            return False, False
        except:
            return (False, None)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then our root &lt;code&gt;.env&lt;/code&gt; should have following elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#database
DENIS_PASS=xxxxxx
ETHEREUM_NETWORK=maticmum
INFURA_PROVIDER=https://polygon-mumbai.infura.io/v3/your_keys
SIGNER_PRIVATE_KEY=private-keys
MUSIC_NFT_ADDRESS=contract_address_mumbai

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this things in place we are ready to run! Open three separate terminals:&lt;/p&gt;

&lt;p&gt;From command line turn on our worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $celery -A musical_nft worker -l info

    #And you should get something like this:

    [2023-11-15 14:23:58,390: INFO/MainProcess] mingle: searching for neighbors
    [2023-11-15 14:23:58,398: WARNING/MainProcess] No hostname was supplied. Reverting to default 'localhost'
    [2023-11-15 14:23:59,414: INFO/MainProcess] mingle: all alone
    [2023-11-15 14:23:59,443: INFO/MainProcess] celery@Ilija ready.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in another temrinal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; $celery -A musical_nft beat --loglevel=info

    # and you should get something like
    celery beat v5.3.4 (emerald-rush) is starting.
__    -    ... __   -        _
    LocalTime -&amp;gt; 2023-11-15 19:19:32
    Configuration -&amp;gt;
        . broker -&amp;gt; amqp://guest:**@localhost:5672//
        . loader -&amp;gt; celery.loaders.app.AppLoader
        . scheduler -&amp;gt; celery.beat.PersistentScheduler
        . db -&amp;gt; celerybeat-schedule
        . logfile -&amp;gt; [stderr]@%INFO
        . maxinterval -&amp;gt; 5.00 minutes (300s)
    [2023-11-15 19:19:32,379: INFO/MainProcess] beat: Starting...
    [2023-11-15 19:19:32,400: INFO/MainProcess] Scheduler: Sending due task my_scheduled_task (authentication.tasks.my_scheduled_task)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lastly, third terminal window for Django server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $py manager.py runserver
    System check identified no issues (0 silenced).
    November 15, 2023 - 19:35:53
    Django version 4.2.7, using settings 'musical_nft.settings'
    Starting development server at http://127.0.0.1:8000/
    Quit the server with CONTROL-C.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In background we should have our Postgres server up and runnig. In any case, a lot of things currently going on in background to make all this happen. &lt;/p&gt;

&lt;p&gt;Now let's test this thing:&lt;/p&gt;

&lt;p&gt;1) go to front-end and log as denis user (the one to whom you assign eth address)&lt;/p&gt;

&lt;p&gt;2) input number 1 and press buy button. MetaMask should pop-up two times: ones to approve MockTokens to MucisNFT contract and second to &lt;code&gt;buyNFT&lt;/code&gt; (inside this function we have &lt;code&gt;transferFrom&lt;/code&gt; and _mint to caller/user address). &lt;/p&gt;

&lt;p&gt;Ones this is done contract will emit &lt;code&gt;newNFTMinted&lt;/code&gt; event. Then our &lt;code&gt;event_listener&lt;/code&gt; class will pick up this event and read two main piece of information's: buyers address and ID of newly minted MusicNFT. Then we will use user address to query our backend for that user and check if this NFT id is already in his collection. If no, we will append this new ID as well as increment for one total number of NFTs user have. This information will latter be used to update user profile on front-end.&lt;/p&gt;

&lt;p&gt;And with this we have crypto buyer finished. Before we make deployment of this app somewhere in cloud, we have final step to do: add Stripe integrations for credit card buyers! This will be topic of next blog.... &lt;/p&gt;

&lt;p&gt;Code can be found in this &lt;a href="https://github.com/ilijapet/musical_nft_thumbnails" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>django</category>
      <category>nft</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>Web3 backend and smart contract development for Python developers Musical NFTs part 14: Buy NFT crypto buyer</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Thu, 09 Nov 2023 11:17:00 +0000</pubDate>
      <link>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-14-buy-nft-crypto-buyer-38b2</link>
      <guid>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-14-buy-nft-crypto-buyer-38b2</guid>
      <description>&lt;p&gt;Now we want to allow crypto user to buy our musical NFTs with MockTokens. For this to happen we will need to do few steps: 1) re-deploy our MusicNFT contract with updated max number of NFTs; 2) set-up desired price per &lt;code&gt;MusicNFT&lt;/code&gt;; 3) send tokens from newly deployed &lt;code&gt;MockUSDC&lt;/code&gt; to user address; 4) create &lt;code&gt;buyNFT&lt;/code&gt; JavaScript function; 5) update our &lt;code&gt;base.html&lt;/code&gt; and lastly 6) update our &lt;code&gt;crypt_user.html&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;1) Currently we have max NFT variable inside &lt;code&gt;nft_package.sol&lt;/code&gt; set to 5. What you need to do is to re-deploy both contracts by running &lt;code&gt;deploy.py&lt;/code&gt; script in our &lt;code&gt;smart-contract/brownie_music_nfts/scripts&lt;/code&gt; folder (or add setter function for this var inside smart contract and then re-deploy). You should pass let's say 1000 NFTs as second argument to &lt;code&gt;MusciNFT.deploy()&lt;/code&gt; function inside Brownie &lt;code&gt;./scripts/deploy.py&lt;/code&gt; (and now this call need to look something like this &lt;code&gt;musciNFTdeployed = MusicNFT.deploy(mockDeployed,1000,{'from':accountOne})&lt;/code&gt;). And then from command line &lt;/p&gt;

&lt;p&gt;&lt;code&gt;brownie run deploy.py --network polygon-test&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Ones we re-deploy our contracts with new argument we need to pass new contract addresses to all JavaScript files where we use them (in our case &lt;code&gt;call_js.js&lt;/code&gt; and &lt;code&gt;node_interaction.js&lt;/code&gt;) as well as to import new &lt;code&gt;MockTokens&lt;/code&gt; and &lt;code&gt;MusciaNFTs&lt;/code&gt; in MetaMask. Then we need to transfer new &lt;code&gt;MockTokens&lt;/code&gt; to buyer address before he perform new test NFT purches (all code for this we have in &lt;code&gt;node_interaction_sc.js&lt;/code&gt;. Or simply you can make transfers directly in MetaMask from deployer to NFT buyers address ones you import our contracts there). &lt;/p&gt;

&lt;p&gt;Reason why we do all this is fact that if we keep max NFTs on 5 (what is current situation defined during our fist deploy), very soon we will hit max limit during testing phase and our calls to smart contract &lt;code&gt;buyNFT&lt;/code&gt; function will start to be reverted. To avoide this situation we need to re-deploy contracts and to assigne this new address to all relevenat places as we said earlier (for how to do all this you can go and check part no. 6 in this blog seria, &lt;code&gt;smart contract deployment&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Now, step two: setup desired MusicNFT price&lt;/p&gt;

&lt;p&gt;1) CDN Ethers at the end (just bellow ending body tag) of &lt;code&gt;base.html&lt;/code&gt; template.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then install with npm inside our root directory because we want to use it with Node (we will call some smart contract functions also from Node)&lt;/p&gt;

&lt;p&gt;&lt;code&gt;$npm install ethers@5.7.2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here is important to install this specific version of Ethers because newer versions, after 6, tend to have problems with setting up &lt;code&gt;InfuraProvider&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now is time to set new price of MusicalNFT and to transfer some MockUSDC to the user&lt;br&gt;
Add to existing &lt;code&gt;.env&lt;/code&gt; 1) infura API key; 2) signer privat keys and 3) network on which we would like to connect. &lt;/p&gt;

&lt;p&gt;Also add &lt;code&gt;.env&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt; (if you didn't already)&lt;/p&gt;

&lt;p&gt;To get Infura API key you need to open profile on &lt;code&gt;https://www.infura.io/&lt;/code&gt;. Ones you are in just create new project and copy API keys. They should look something like this &lt;code&gt;https://polygon-mumbai.infura.io/v3/52153a550fd8498c9041752356789010&lt;/code&gt;. Be cerfule to pick up &lt;code&gt;Mumbai&lt;/code&gt; testnet from &lt;code&gt;Polygon&lt;/code&gt;, becuse we deploy our contracts there. Pass API key to &lt;code&gt;INFURA_API_KEY&lt;/code&gt; inside newly created &lt;code&gt;.env&lt;/code&gt; file. &lt;/p&gt;

&lt;p&gt;Then for wallet private key go to MetaMask and select account from where you made contracts deployment. Then go to &lt;code&gt;more&lt;/code&gt; =&amp;gt; &lt;code&gt;account details&lt;/code&gt; =&amp;gt; &lt;code&gt;show private keys&lt;/code&gt;. Copy private key to your local enviroment varibale under &lt;code&gt;SIGNER_PRIVATE_KEY&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Last thing we need here is &lt;code&gt;ETHEREUM_NETWORK=maticmum&lt;/code&gt; (mainnet Polygon is &lt;code&gt;matic&lt;/code&gt; and testnet Mumabi is &lt;code&gt;maticmum&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;Now let's load &lt;code&gt;.env&lt;/code&gt; file. For this we need to install packege &lt;code&gt;dotenv&lt;/code&gt; with command &lt;code&gt;npm install dotenv&lt;/code&gt; inside our project root directory. Ones we start to work with contracts we will require this package inside &lt;code&gt;Node&lt;/code&gt; to load &lt;code&gt;.env&lt;/code&gt; varibales. &lt;/p&gt;

&lt;p&gt;Now open &lt;code&gt;Node&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     $node
        Welcome to Node.js v19.1.0.
        Type ".help" for more information.
        &amp;gt; require("dotenv").config()  
        &amp;gt; const network = process.env.ETHEREUM_NETWORK
        &amp;gt; const infura = process.env.INFURA_API_KEY
        &amp;gt; const private_key = process.env.SIGNER_PRIVATE_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we should have all varibales from our &lt;code&gt;.env&lt;/code&gt; loaded into Node and assigned to network, infura and private_key. What we need in this moment is to defin provider, signer (the one who will pay for transaction) and instancies of our contracts.&lt;/p&gt;

&lt;p&gt;To make instance of provider, signer and contracts we always need to have follwoing ingredients: 1) ethereum network we intend to use; 2) in our case Infura API keys; 3) private keys of the signer account; 4) contract ABI and 5) address of deployed contract. With this elements you can first instatite: provider (gateway to Ethereum network); then signer (creator and signer of tx) and finally contract itself. And with &lt;code&gt;ethers.js&lt;/code&gt; this can be done very easly&lt;/p&gt;

&lt;p&gt;Open separate terminal from the one where your Node is running. From project root directory copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    $cp smart-contracts/brownie_musical_nfts/build/contracts/MockUSDC.json ./static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;MusicNFT.json&lt;/code&gt; (if you already didn't)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    $cp smart-contracts/brownie_musical_nfts/build/contracts/MusicNFT.json ./static
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's go back to Node and import contracts ABIs (we will need them in last step when we instatie contracts) from our &lt;code&gt;Node&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Assigning ABI path to const
    &amp;gt;const mockJsonFile = "./static/MockUSDC.json";
    &amp;gt;const nftJsonFile = "./static/MusicNFT.json";

    // Loading contract ABIs
    &amp;gt;const mockAbi=JSON.parse(fs.readFileSync(mockJsonFile));
    &amp;gt;const nftAbi=JSON.parse(fs.readFileSync(nftJsonFile));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we have our &lt;code&gt;ABI&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; varibles loaded in &lt;code&gt;node&lt;/code&gt; and we can move to define provider, signer and contracts instancie (ones we come to the last point we will be able to work with our deplyed contracts from within &lt;code&gt;Node&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;step 1: Lets define &lt;code&gt;provider&lt;/code&gt; and contract.&lt;/p&gt;

&lt;p&gt;Node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     &amp;gt; const { ethers } = require("ethers");
        &amp;gt; const provider = new ethers.providers.InfuraProvider(
        network,
        infura);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;step 2: define &lt;code&gt;signer&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;gt;const signer = new ethers.Wallet(private_key, provider);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;step 3: define &lt;code&gt;MusicNFT&lt;/code&gt; contract as well as &lt;code&gt;MockToken&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    // contract address from Mumbai Polygon testnet (last deployment)
    const nft_contract_address = "0x1D33a553541606E98c74a61D1B8d9fff9E0fa138"
    const mock_usdc_address = "0xCeD9Fe6C4851acea6833DB0B7A0d2b892E4BBD5f"

    // Let's instatiate both contracts
    const contract_usdc = new ethers.Contract(mock_usdc_address, mockAbi.abi, signer);
    const contract_nft = new ethers.Contract(nft_contract_address, nftAbi.abi, signer);

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point we have our contract in &lt;code&gt;Node&lt;/code&gt; ready to be used. We can test that. Let's define one async function to check for name of our NFT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    // This function will return name of our NFT contract
    getNftName = async () {
        let result = await contract_nft.name();
        console.log(result);
    }



    &amp;gt;getNftName()
    Promise {
    &amp;lt;pending&amp;gt;,
    [Symbol(async_id_symbol)]: 1779,
    [Symbol(trigger_async_id_symbol)]: 5
    }
    &amp;gt; MuscialNFT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have fully functional contract in our &lt;code&gt;node&lt;/code&gt; and we can update NFT price and send to potential user some &lt;code&gt;MockTokens&lt;/code&gt;. Here we should notice that we are sending &lt;code&gt;MockTokens&lt;/code&gt; to user because only deployer of contract have all issued tokens. In real life situation user should have his own USDC or some other stabilcoins to pay for his NFTs. Last two steps: update NFT price, send MockTokens tokens to the buyer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
    setNftPrice = async (price) =&amp;gt; {
        let result = await contract_nft.setNFTPrice(price);

    }

    // check for new price
    var assert = require('assert');
    // When you call this function please pass as argument price you set previously
    checkNFTPrice = async (price) =&amp;gt; {
      let nft_price = await contract_nft.NFTPriceInUSDC();
      console.log(Number(nft_price));
      assert(Number(nft_price) == price, "Price was not upddted!");
    }

    // sending MockTokens to the customer 
    // const buyer = here you should add address of acocunt from which you will purches NFTs
    const buyer = "0xB4A5D329e35F83a2e6AB5a74d56f5bD67EaB8d83"
    const amount = BigInt(10e18)

    sendTokenToBuyer = async (buyer, amount) =&amp;gt; {
        await mock_usdc_contract.transfer(buyer, amount)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*this JS code you can find in &lt;code&gt;static/node_interaction_sc.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now when our customer have MockTokens to buy NFT, we will move to front-end and add functinoality to allow user to make this NFT purches.&lt;/p&gt;

&lt;p&gt;For this to work we will have few step process. In first step user approve NFT contract to spend his &lt;code&gt;MockTokens&lt;/code&gt; in amount calulcated by simple multiplication of number of NFTs he wants to buy time current price of &lt;code&gt;MusicNFT&lt;/code&gt;. But because we are calling &lt;code&gt;MockUSDC&lt;/code&gt; &lt;code&gt;transfeFrom&lt;/code&gt; from within MusicNFT, &lt;code&gt;MockUSDC&lt;/code&gt; contract will se &lt;code&gt;MusicNFT&lt;/code&gt; as caller and it will expect &lt;code&gt;MusicNFT&lt;/code&gt; to have allowance on buyer USDC mock tokens. That is why first step is to call approve function of our &lt;code&gt;MockToken&lt;/code&gt; and then in second step to call &lt;code&gt;buyNFT&lt;/code&gt; function from &lt;code&gt;MusicNFT&lt;/code&gt; contract. If first step was ok, then &lt;code&gt;MockUSDC&lt;/code&gt; will se that caller (&lt;code&gt;MusicNFT&lt;/code&gt; contract) have rights to use buyer &lt;code&gt;MockTokens&lt;/code&gt; and he will then transfer in last step from buyer to &lt;code&gt;MusicNFT&lt;/code&gt; precise amount of tokens. Only then &lt;code&gt;MusicNFT&lt;/code&gt; contract will mint new NFTs to buyer addrees and emit event of confirmation. And with this we have all steps done! &lt;/p&gt;

&lt;p&gt;And this function can look something like this (inside &lt;code&gt;static/call_sc.js&lt;/code&gt; file):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; const nft_contract_address = "0x1D33a553541606E98c74a61D1B8d9fff9E0fa138"
    const mock_usdc_address = "0xCeD9Fe6C4851acea6833DB0B7A0d2b892E4BBD5f"
    const main_account = "0x273f4FCa831A7e154f8f979e1B06F4491Eb508B6"
    const test_account = "0xB4A5D329e35F83a2e6AB5a74d56f5bD67EaB8d83"

    // set provider
    const provider = new ethers.providers.Web3Provider(window.ethereum);

    // get signer this need to be test_account
    const signer = provider.getSigner(0);

    // see for original ABI file in github repo. you can copy from there
    const ABI_NFT = [...]

    // see for original ABI file in github repo. you can copy from there
    const ABI_USDC = [..]

    buyNFT = async () =&amp;gt; {
        // 1. get value user pass to use from front-end about number of NFTs he would like to buy
        const numberOfNFTS = document.getElementById('NFT').value;

        // 2. get current price of NFT from our smart contract and caluclate total price to be payed
        const price = await nft_contract.NFTPriceInUSDC();
        console.log(Number(price)*numberOfNFTS);
        const total_price = Number(price)*numberOfNFTS

        // 3. approve MusicNFT to transfer user MockTokens in total amount on contract 
        await usdc_contract.approve(nft_contract_address, total_price)


        // 4. finally call buyNFT function from MusicNFT contract to buy and mint new NFTs to user account
        await nft_contract.buyNFT("uri://music_nft_token", numberOfNFTS)
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This newly created &lt;code&gt;call_js.js&lt;/code&gt; script should be added to our &lt;code&gt;base.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; {% load static %}
    &amp;lt;!doctype html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
        &amp;lt;title&amp;gt;Muscial NFT&amp;lt;/title&amp;gt;
        &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'connect_wallet.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;link rel="shortcut icon" type="image" href="{% static 'favicon.ico' %}" &amp;gt;
        &amp;lt;!-- &amp;lt;script type="module" src="{% static 'call_sc.js' %}"&amp;gt;&amp;lt;/script&amp;gt; --&amp;gt;
    &amp;lt;/script&amp;gt;

    &amp;lt;/head&amp;gt;
    &amp;lt;body onload="checkMetaMaskState()"&amp;gt;
    &amp;lt;body&amp;gt;
        {% include "navbar.html"%}
        &amp;lt;div class="container "&amp;gt;       
            &amp;lt;br/&amp;gt;
            &amp;lt;br/&amp;gt;
            {% if messages %}
            {% for message in messages%}
            &amp;lt;div class="alert alert-warning alert-dismissible fade show" role="alert"&amp;gt;
            {{ message }}
            &amp;lt;button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            {% endfor%}
            {% endif %} 

            {% block content %}
            {% endblock content %}
        &amp;lt;/div&amp;gt;
        &amp;lt;/body&amp;gt;
        &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script src="https://cdn.ethers.io/scripts/ethers-v4.min.js"
        charset="utf-8"
        type="text/javascript"&amp;gt;&amp;lt;/script&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'call_sc.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then last step is to connect &lt;code&gt;onclick&lt;/code&gt; inside our &lt;code&gt;crypto_user.html&lt;/code&gt; to newly created JS funciton &lt;code&gt;buyNFT()&lt;/code&gt;. This will allow user to pass number of NFTs he would like to buy, then in backgorund we will calculate total price, approve tokens to &lt;code&gt;MusicNFT&lt;/code&gt; contract, transfer MockTokens to &lt;code&gt;MusciNFT&lt;/code&gt; contract and in last step mint new NFT (or NFTs, depends from number user give as input to front-end) and assigne to buyer address. With this we have buy NFT with crypto fully in place.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table class="table  table-hover"&amp;gt;
        &amp;lt;thead class="table-dark"&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Name&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Email&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Total no. NFTs&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Means of payment&amp;lt;/th&amp;gt;
            &amp;lt;th scope="col"&amp;gt;Buy NFT&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;/thead&amp;gt;
        &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.first_name }} {{ customer.last_name }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.email }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt; {{ customer.total_no_of_nfts }} &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
                &amp;lt;form action="" method="post"&amp;gt;
                {%csrf_token%}
                {{orderForm.as_p}}
                &amp;lt;input type="submit" value="Save" class="btn btn-secondary"&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;
            &amp;lt;form action="" method="post"&amp;gt;
                {%csrf_token%}
                &amp;lt;label for="NFT"&amp;gt;Number of NFTs&amp;lt;/label&amp;gt;
                &amp;lt;input  type="text" id="NFT" &amp;gt;&amp;lt;br&amp;gt;
            &amp;lt;/form&amp;gt;
            &amp;lt;input  onclick="buyNFT()" type="submit" value="Buy" class="btn btn-secondary"&amp;gt;
            &amp;lt;/td&amp;gt;             

            &amp;lt;/tr&amp;gt;
        &amp;lt;/tbody&amp;gt;
        &amp;lt;/table&amp;gt;      
        {% for card in metadata%}
        {% if forloop.counter0|divisibleby:3 %} &amp;lt;div class="row"&amp;gt;{%  endif %}
        &amp;lt;div class="card m-5 p-2" style="width: 18rem;"&amp;gt;
            {% load static %}
            &amp;lt;img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/&amp;gt;
            &amp;lt;div class="card-body"&amp;gt;
            &amp;lt;h5 class="card-title"&amp;gt;{{card.name}}&amp;lt;/h5&amp;gt;
            &amp;lt;br&amp;gt;
            &amp;lt;p class="card-text"&amp;gt;{{card.description}}&amp;lt;/p&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
        {%  if forloop.counter|divisibleby:3 or forloop.last %}&amp;lt;/div&amp;gt; {%  endif %}
        &amp;lt;br&amp;gt;
        {% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went well, ones user login into our platform he will be able to give number of NFTs he wants to buy and then press &lt;code&gt;buy&lt;/code&gt; button. MetaMask will pop-up aksking him first for approval for &lt;code&gt;MockTokens&lt;/code&gt; to &lt;code&gt;MusicNFT&lt;/code&gt; contract, and then in second step ask to confirm start of &lt;code&gt;buyNFT&lt;/code&gt; transaction. Something like this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvneelle0kpstl8545jc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcvneelle0kpstl8545jc.png" alt=" " width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In next blog, we will listen for emited minting event on our backend and updated our database and front-end accordingly.  &lt;/p&gt;

&lt;p&gt;In this &lt;a href="https://github.com/ilijapet/musical_nft_thumbnails" rel="noopener noreferrer"&gt;repo &lt;/a&gt;you can find latest code&lt;/p&gt;

</description>
      <category>python</category>
      <category>ethereum</category>
      <category>nft</category>
      <category>django</category>
    </item>
    <item>
      <title>Web3 backend and smart contract development for Python developers Musical NFTs part 13: Connecting MetaMask</title>
      <dc:creator>Basstardd</dc:creator>
      <pubDate>Sun, 29 Oct 2023 22:20:28 +0000</pubDate>
      <link>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-13-connecting-metamask-59pf</link>
      <guid>https://dev.to/bastarrd/web3-backend-and-smart-contract-development-for-python-developers-musical-nfts-part-13-connecting-metamask-59pf</guid>
      <description>&lt;p&gt;In this part we will add ability for crypto users to connect his/her MetaMask and to purches new NFT with our MockUSDC tokens. But before that let's clean a bit our &lt;code&gt;home.html&lt;/code&gt; because it started to look a bit messy. What we can do is to create two new templates for crypto buyers and credit card buyer and to loaded them into &lt;code&gt;home.html&lt;/code&gt; when we check user payment method status. For this to happen we just need to repackage our &lt;code&gt;home.html&lt;/code&gt; and to move code related to crypto buyer to newly created &lt;code&gt;templates/cypto_user.html&lt;/code&gt; and then all HTML code related to credit card buyer to &lt;code&gt;templates/credit_card_buyer.html&lt;/code&gt;. Then in second step we will use Django tag &lt;code&gt;include&lt;/code&gt; to include this two newly created HTML files into our &lt;code&gt;home.html&lt;/code&gt;. With this we will clean up cluttery and make our main &lt;code&gt;home.html&lt;/code&gt; template a bit more readable. &lt;/p&gt;

&lt;p&gt;Create &lt;code&gt;cypto_user.html&lt;/code&gt; and &lt;code&gt;credit_card_user.html&lt;/code&gt; files inside root &lt;code&gt;templates&lt;/code&gt; folder. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;cypto_user.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table class="table  table-hover"&amp;gt;
    &amp;lt;thead class="table-dark"&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Name&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Email&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Total no. NFTs&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Means of payment&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Buy NFT&amp;lt;/th&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
      &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.first_name }} {{ customer.last_name }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.email }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.total_no_of_nfts }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;
            &amp;lt;form action="" method="post"&amp;gt;
            {%csrf_token%}
            {{orderForm.as_p}}
            &amp;lt;input type="submit" value="Save" class="btn btn-secondary"&amp;gt;
        &amp;lt;/form&amp;gt;
          &amp;lt;/td&amp;gt;       
          &amp;lt;td&amp;gt;
            &amp;lt;form action="" method="post"&amp;gt;
            {%csrf_token%}
            {{form.as_p}}
            &amp;lt;input type="submit" value="Save" class="btn btn-secondary"&amp;gt;
            &amp;lt;/form&amp;gt;
          &amp;lt;/td&amp;gt;             

        &amp;lt;/tr&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;      
    {% for card in metadata%}
    {% if forloop.counter0|divisibleby:3 %} &amp;lt;div class="row"&amp;gt;{%  endif %}
      &amp;lt;div class="card m-5 p-2" style="width: 18rem;"&amp;gt;
        {% load static %}
        &amp;lt;img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/&amp;gt;
        &amp;lt;div class="card-body"&amp;gt;
          &amp;lt;h5 class="card-title"&amp;gt;{{card.name}}&amp;lt;/h5&amp;gt;
          &amp;lt;br&amp;gt;
          &amp;lt;p class="card-text"&amp;gt;{{card.description}}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    {%  if forloop.counter|divisibleby:3 or forloop.last %}&amp;lt;/div&amp;gt; {%  endif %}
    &amp;lt;br&amp;gt;
    {% endfor %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then in &lt;code&gt;credit_card_user.html&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;table class="table  table-hover"&amp;gt;
    &amp;lt;thead class="table-dark"&amp;gt;
      &amp;lt;tr&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Name&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Email&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Total no. NFTs&amp;lt;/th&amp;gt;
        &amp;lt;th scope="col"&amp;gt;Means of payment&amp;lt;/th&amp;gt;
      &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
      &amp;lt;tr&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.first_name }} {{ customer.last_name }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.email }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt; {{ customer.total_no_of_nfts }} &amp;lt;/td&amp;gt;
          &amp;lt;td&amp;gt;&amp;lt;form action="" method="post"&amp;gt;
            {%csrf_token%}
            {{orderForm.as_p}}
            &amp;lt;input type="submit" value="Save" class="btn btn-secondary"&amp;gt;
        &amp;lt;/form&amp;gt;
          &amp;lt;/td&amp;gt;         
        &amp;lt;/tr&amp;gt;
      &amp;lt;/tbody&amp;gt;
    &amp;lt;/table&amp;gt;        
    {% for card in metadata%}
    {% if forloop.counter0|divisibleby:3 %} &amp;lt;div class="row"&amp;gt;{%  endif %}
      &amp;lt;div class="card m-5 p-2" style="width: 18rem;"&amp;gt;
        {% load static %}
        &amp;lt;img src="{% static 'nft/'%}{{card.cover_file_name}}"  class="card-img-top" alt="..." width="50" height="200"/&amp;gt;
        &amp;lt;div class="card-body"&amp;gt;
          &amp;lt;h5 class="card-title"&amp;gt;{{card.name}}&amp;lt;/h5&amp;gt;
          &amp;lt;br&amp;gt;
          &amp;lt;p class="card-text"&amp;gt;{{card.description}}&amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    {%  if forloop.counter|divisibleby:3 or forloop.last %}&amp;lt;/div&amp;gt; {%  endif %}
    &amp;lt;br&amp;gt;
    {% endfor %}
    &amp;lt;p&amp;gt; credit card &amp;lt;/p&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now what we need to do is to include back into our &lt;code&gt;home.html&lt;/code&gt; this two new files via Django template language tag &lt;code&gt;include&lt;/code&gt;. Syntax should go something like this &lt;br&gt;
&lt;code&gt;{% include "crypto_user.html" %}&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;And now our &lt;code&gt;home.html&lt;/code&gt; should look a bit shorter and cleaner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% extends "base.html" %}
    {% block content%}

    {% if user.is_authenticated %}
        {% if customer.type == "CRYPTO"%}
        {% include "crypto_user.html" %}    

        {% else %}
        {% include "credit_card_user.html" %}    

        {% endif %}
        {% else %}
        &amp;lt;div class="col-md-6 offset-md-3"&amp;gt; 
        &amp;lt;h1&amp;gt; Login &amp;lt;/h1&amp;gt;
        &amp;lt;br/&amp;gt;
        &amp;lt;form method="POST" action="{% url 'home' %}"&amp;gt; 
            {% csrf_token %}        
                &amp;lt;div class="mb-3"&amp;gt;
                &amp;lt;input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Username" name="username" required&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;div class="mb-3"&amp;gt;
                &amp;lt;input type="password" class="form-control" placeholder="Password" name="password" required&amp;gt;
                &amp;lt;/div&amp;gt;
                &amp;lt;button type="submit" class="btn btn-secondary"&amp;gt;Login&amp;lt;/button&amp;gt;
            &amp;lt;/form&amp;gt; 
        &amp;lt;/div&amp;gt;
    {% endif %}
    {% endblock content%}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now all things should work exactly the same only thing is that we have more readeble main html document. &lt;/p&gt;

&lt;p&gt;Let's move to web3 part. For now our aim is to integrate MetaMask (you can easlly experiment with WalletConnect if you want alternative version of this code). This will allow crypto user to purches new NFT by using MetaMask. For this to happen we will need to use some JavaScript (in next iteration of this app, when we start to use React for our frontend we will integrate WalletConnect instead of MetaMask).  &lt;/p&gt;

&lt;p&gt;Ones crypto user login into his profile he will see two new buttons: connect and buy NFT. He first need to press connect and MetaMask will pop-up asking him to provide his credentials. Ones he finish login into MetaMask he will be able to use buy NFT button and to pass number of NFTs he would like to buy. &lt;/p&gt;

&lt;p&gt;Step 1: In your static folder create new &lt;code&gt;connect_wallet.js&lt;/code&gt; file. And inside that file pass following code (Please check if static parameters are set correctly inside your &lt;code&gt;settings.py&lt;/code&gt; It should look something like this =&amp;gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  STATIC_URL = "/static/"
    STATICFILES_DIRS = (
        os.path.join(BASE_DIR, 'static'),
    )
    STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JavaScript file need to handle connection to MetaMask as well as to update list items to &lt;code&gt;connected&lt;/code&gt; or &lt;code&gt;connect to metamask&lt;/code&gt; status how they change. For this achive we will need few more functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; //function used to connect to MetaMask wallet
    connect = async () =&amp;gt; {
        const address = await ethereum
        .request({
            method: 'eth_requestAccounts',
            params: [],
        })
        .then((res) =&amp;gt; console.log('request accounts', res))
        .catch((e) =&amp;gt; console.log('request accounts ERR', e));
        document_test = document.getElementById("connect");
        document_test.innerHTML = "Connected"  
    }   

    // Simple function used to manipulate HTML elements according to connected or user disconnencted status
    checkMetaMaskState = async () =&amp;gt; {
        const account = await window.ethereum.request({method: 'eth_accounts'})
        const liElement = document.createElement("li");
        liElement.setAttribute("class", "nav-item")
        const aElement = document.createElement("a");
        aElement.setAttribute("class", "nav-link p-3")
        if (typeof account[0] == "undefined") {
            aElement.setAttribute("href", "javascript:connect()")
            aElement.setAttribute("id", "link")
            liElement.setAttribute("id", "connect")
            liElement.innerHTML = "Connect to MetaMask"
            aElement.appendChild(liElement);
        document.getElementById("logout").appendChild(aElement);
    } else if (account[0].includes("0x")) {
        aElement.setAttribute("id", "link")
        liElement.setAttribute("id", "connect")
        liElement.innerHTML = "Connected"
        aElement.appendChild(liElement);
        document.getElementById("logout").appendChild(aElement);
    } else {
        console.log("There is some problem with MetaMask")        
    }
    }

    // Detect change in connect or dissconcet wallet status and updated front-end accordingly
    window.ethereum.on('accountsChanged', async () =&amp;gt; {
    let ilElement = document.getElementById("connect")
    const account = await window.ethereum.request({method: 'eth_accounts'})
    if (typeof account[0] == "undefined") {
        ilElement.innerHTML = "Connect to MetaMask"
    } else if (account[0].includes("0x")) {
        ilElement.innerHTML = "Connected"
    }
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to add &lt;code&gt;onload&lt;/code&gt; in body element of our &lt;code&gt;base.html&lt;/code&gt;. And then to give &lt;code&gt;checkMetaMaskState&lt;/code&gt; JS funciton as value. This function is used to generate new HTML document according to MetaMask connect/disconnect status.   &lt;/p&gt;

&lt;p&gt;Now &lt;code&gt;base.html&lt;/code&gt; should look something like this (bascially the same as beafore just with &lt;code&gt;&amp;lt;body onload="checkMetaMaskState()"&amp;gt;&lt;/code&gt; added)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{% load static %}
    &amp;lt;!doctype html&amp;gt;
    &amp;lt;html lang="en"&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;meta charset="utf-8"&amp;gt;
        &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;
        &amp;lt;title&amp;gt;Muscial NFT&amp;lt;/title&amp;gt;
        &amp;lt;link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"&amp;gt;
        &amp;lt;script type="text/javascript" src="{% static 'connect_wallet.js' %}"&amp;gt;&amp;lt;/script&amp;gt;

    &amp;lt;/head&amp;gt;
    &amp;lt;body onload="checkMetaMaskState()"&amp;gt;
        {% include "navbar.html"%}
        &amp;lt;div class="container "&amp;gt;       
            &amp;lt;br/&amp;gt;
            &amp;lt;br/&amp;gt;
            {% if messages %}
            {% for message in messages%}
            &amp;lt;div class="alert alert-warning alert-dismissible fade show" role="alert"&amp;gt;
            {{ message }}
            &amp;lt;button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"&amp;gt;&amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;

            {% endfor%}
            {% endif %}

            {% block content %}
            {% endblock content %}
        &amp;lt;/div&amp;gt;
        &amp;lt;script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in second step we will erase one list item element from our &lt;code&gt;navbar.html&lt;/code&gt; template (this place will be populated directly from javascript level according to MetaMask wallet status).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;nav class="navbar navbar-expand-lg navbar-dark bg-dark"&amp;gt;
      &amp;lt;div class="container-fluid"&amp;gt;
        &amp;lt;a class="navbar-brand" href="{% url 'home' %}"&amp;gt;Musical NFT &amp;lt;/a&amp;gt;
        &amp;lt;button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"&amp;gt;
          &amp;lt;span class="navbar-toggler-icon"&amp;gt;&amp;lt;/span&amp;gt;
        &amp;lt;/button&amp;gt;
        &amp;lt;div class="collapse navbar-collapse" id="navbarSupportedContent"&amp;gt;
          &amp;lt;ul class="navbar-nav me-auto mb-2 mb-lg-0" id="logout"&amp;gt;
            {% if user.is_authenticated%}
              {% if customer.type == "CRYPTO"%}
              {% load static %}
              &amp;lt;script type="text/javascript" src="{% static 'connect_wallet.js' %}"&amp;gt;&amp;lt;/script&amp;gt;
                &amp;lt;li class="nav-item"  &amp;gt;
                  &amp;lt;a class="nav-link p-3" href="{% url 'logout'%}"  &amp;gt;Logout&amp;lt;/a&amp;gt;
                &amp;lt;/li&amp;gt;
                {% else %}
                &amp;lt;li class="nav-item"&amp;gt;
                  &amp;lt;a class="nav-link" href="{% url 'logout'%}"&amp;gt;Logout&amp;lt;/a&amp;gt;
                &amp;lt;/li&amp;gt;

                {% endif %}

          {% else %}
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'register'%}"&amp;gt;Register&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          &amp;lt;li class="nav-item"&amp;gt;
            &amp;lt;a class="nav-link" href="{% url 'home'%}"&amp;gt;Login&amp;lt;/a&amp;gt;
          &amp;lt;/li&amp;gt;
          {% endif%}
              &amp;lt;/ul&amp;gt;
            &amp;lt;/li&amp;gt;
          &amp;lt;/ul&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/nav&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything went well ones you login Denis user into his account you should be able to see something like this. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes0sgicjov58y57094be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes0sgicjov58y57094be.png" alt=" " width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Code can be found in this github &lt;a href="https://github.com/ilijapet/musical_nft_thumbnails" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
