<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Software ninja and the wise path of software development]]></title><description><![CDATA[python software hardware computers]]></description><link>https://hubert-dev.xyz</link><image><url>https://cdn.hashnode.com/res/hashnode/image/upload/v1741978937943/e9d47488-285b-46f9-82e3-d8b873a60edd.jpeg</url><title>Software ninja and the wise path of software development</title><link>https://hubert-dev.xyz</link></image><generator>RSS for Node</generator><lastBuildDate>Sat, 12 Sep 2026 01:33:31 GMT</lastBuildDate><atom:link href="https://hubert-dev.xyz/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Less full of colors
]]></title><description><![CDATA[less is the workhorse of the terminal, but by default it dumps source files onto the screen as monochrome text. You can pipe files through a highlighter manually, but the nicer solution is to make it ]]></description><link>https://hubert-dev.xyz/less-full-of-colors</link><guid isPermaLink="true">https://hubert-dev.xyz/less-full-of-colors</guid><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Thu, 10 Sep 2026 06:50:31 GMT</pubDate><content:encoded><![CDATA[<p><a href="https://www.man7.org/linux/man-pages/man1/less.1.html"><code>less</code></a> is the workhorse of the terminal, but by default it dumps source files onto the screen as monochrome text. You can pipe files through a highlighter manually, but the nicer solution is to make it automatic: configure your shell so that <em>every</em> time you run <code>less</code> on a code file, GNU source-highlight colors it for you.</p>
<p>Based on <a href="https://dev.to/vvidovic/viewing-more-with-less-color-syntax-hhp">Viewing more with less – color syntax</a> by Vedran Vidovic, here's the permanent setup.</p>
<h4><code>The</code> Setup</h4>
<p>The whole thing rests on two environment variables that <code>less</code> reads. First, install the highlighter (Debian/Ubuntu naming):</p>
<pre><code class="language-plaintext">brew install source-highlight
</code></pre>
<p>Then append this to your <code>~/.</code>zshrc (or <code>~/.profile</code> if you prefer login-shell scope):</p>
<pre><code class="language-bash"># less syntax highlighting + source-highlight installation
export LESSOPEN="| /opt/homebrew/Cellar/source-highlight/3.1.9_6/bin/src-hilite-lesspipe.sh %s"
export LESS=' -R'
</code></pre>
<p>Reload your shell config or open a new terminal:</p>
<pre><code class="language-bash">source ~/.bashrc
</code></pre>
<p>From that point on, any file you open by name — <code>less main.go</code>, <code>less config.json</code>, <code>less</code> <a href="http://script.py"><code>script.py</code></a> — arrives already colorized. No wrapper aliases, no remembering extra flags; it just becomes the default behavior of <code>less</code>.</p>
<h4>Why These Two Lines Work</h4>
<p><code>LESSOPEN</code> is the hook that does the heavy lifting. When <code>less</code> opens a file, it runs the command after the pipe symbol as an <em>input preprocessor</em>, with <code>%s</code> standing in for the filename. Here the preprocessor is <a href="http://src-hilite-lesspipe.sh"><code>src-hilite-lesspipe.sh</code></a>, a small bridge script shipped with the source-highlight package that detects the file's language and emits colorized output for <code>less</code> to display.</p>
<p><code>LESS=' -R'</code> is the necessary companion. Terminals show colors through raw ANSI escape sequences, and without <code>-R</code>, <code>less</code> would neutralize them into literal <code>^[[</code> gibberish. The <code>-R</code> flag tells <code>less</code> to pass raw control characters through so the colors actually render. Note the leading space in the value — it's the standard way of placing the option without disturbing any <code>LESS</code> value you might set elsewhere.</p>
<h4>Caveats Worth Knowing</h4>
<p>The automatic highlighting applies to files <code>less</code> opens <em>by name</em>, since that's what <code>LESSOPEN</code> receives — piping content in (<code>cat file | less</code>) bypasses the preprocessor. The script path is also distribution-specific; if you're not on Debian/Ubuntu, check where your package placed <a href="http://src-hilite-lesspipe.sh"><code>src-hilite-lesspipe.sh</code></a>. And if a language isn't supported out of the box (YAML, for instance), source-highlight accepts custom language definitions that you can drop into its configuration directory.</p>
<p>It's a five-minute setup that quietly improves every future code-reading session in the terminal — and as the original article's screenshots show, the difference between highlighted and plain <code>less</code> output is immediately obvious the first time you open a JSON file.</p>
<p>Notice pipelining and redirect output between scripts does bring small increase of memory foorprint for less command, but hey! Colors are colors, no?</p>
]]></content:encoded></item><item><title><![CDATA[Ghostly
]]></title><description><![CDATA[So once I started to use ghostly terminal I can honestly say - yes I enjoy using it but.. Some of my remote machines started to misbihave each time i SSH-ed to them. They put semirandom character into]]></description><link>https://hubert-dev.xyz/ghostly</link><guid isPermaLink="true">https://hubert-dev.xyz/ghostly</guid><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Wed, 17 Jun 2026 14:20:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6573539bdb0b40362d063060/c122e014-dbf1-43f6-9a02-3e92888ed1b6.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So once I started to use <a href="https://ghostty.org/">ghostly terminal</a> I can honestly say - yes I enjoy using it but.. Some of my remote machines started to misbihave each time i SSH-ed to them. They put semirandom character into terminal or for instance when I've been pressing <em>backspace</em> I saw <em>space</em> on the terminal instead .</p>
<p>I thought it is maybe the fact that I use zsh with <a href="https://ohmyz.sh/">oh my zsh</a> or some weird mapping i have in there .. nope. Switching to other shells didn't help much and then I foudn thje holy grail. This all you have to do in your SSH client settings if you <a href="https://ghostty.org/docs/help/terminfo#ssh">read manual</a> or you are like me TLDR; 🤦</p>
<ol>
<li><p>Configure SSH to fall back to a known terminfo entry using the following SSH configuration (<strong>requires</strong> <a href="https://www.openssh.com/txt/release-8.7"><strong>OpenSSH 8.7</strong></a> <strong>or newer</strong>):</p>
<pre><code class="language-ssh-config"># .ssh/config
Host example.com
  SetEnv TERM=xterm-256color
</code></pre>
</li>
</ol>
<p>that's it! Enjoy your day ✌️</p>
]]></content:encoded></item><item><title><![CDATA[Business logic with PostgreSQL and Python]]></title><description><![CDATA[When you can't use ORM and have to have a rock solid and consistent business logic in database why not using the power of Python as part of database? Using functions and triggers and PostgreSQL magic with plpy.
This is my speech I did in Singapore in...]]></description><link>https://hubert-dev.xyz/business-logic-with-postgresql-and-python</link><guid isPermaLink="true">https://hubert-dev.xyz/business-logic-with-postgresql-and-python</guid><category><![CDATA[plpy]]></category><category><![CDATA[Python]]></category><category><![CDATA[PostgreSQL]]></category><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Thu, 16 Oct 2025 08:55:26 GMT</pubDate><content:encoded><![CDATA[<p>When you can't use ORM and have to have a rock solid and consistent business logic in database why not using the power of Python as part of database? Using functions and triggers and PostgreSQL magic with plpy.</p>
<p>This is my speech I did in Singapore in 2015 during PyCon.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/l6vhg9kQIcA?si=2HoDtuF72sfwg87p"></iframe>

<p>Here you can <a target="_blank" href="https://www.slideshare.net/slideshow/business-logic-with-postgre-sql-and-python/49581320">find slides</a> that I used.</p>
]]></content:encoded></item><item><title><![CDATA[Django method decorator]]></title><description><![CDATA[Recently I found some interesting issue when switching from Django 4.x to Django 5.x. I’ve been working with a legacy software and I had found some interesting case when core framework (Django) has been updated to one of the latest versions.
In the c...]]></description><link>https://hubert-dev.xyz/django-method-decorator</link><guid isPermaLink="true">https://hubert-dev.xyz/django-method-decorator</guid><category><![CDATA[Django]]></category><category><![CDATA[decorators]]></category><category><![CDATA[upgrade]]></category><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Fri, 14 Mar 2025 19:09:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741979054484/023494be-0252-4a83-aea8-fca86283905b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Recently I found some interesting issue when switching from Django 4.x to Django 5.x. I’ve been working with a legacy software and I had found some interesting case when core framework (Django) has been updated to one of the latest versions.</p>
<p>In the <a target="_blank" href="https://docs.djangoproject.com/en/5.1/topics/class-based-views/">class view</a> we can add lots of different decorators to methods. One of the main decorator I’ve been using for ling time in my django code was the one that I wrote to catch and log view calls, ie.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_log</span>(<span class="hljs-params">*args</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">log_func</span>(<span class="hljs-params">function</span>):</span>
        <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">wrapper</span>(<span class="hljs-params">request, *args, **kwargs</span>):</span>
            <span class="hljs-comment"># inject request API payload for airbrake</span>
            <span class="hljs-keyword">if</span> log.isEnabledFor(logging.DEBUG):
                function_name = <span class="hljs-string">"{}.{}"</span>.format(type(function).__name__, function.__name__)
                log.debug(
                    <span class="hljs-string">"{} called with data: {}, args:{}, kwargs:{}"</span>.format(function_name, request.data, args, kwargs)
                )
                responder = function(request, *args, **kwargs)
                <span class="hljs-keyword">try</span>:
                    log.debug(<span class="hljs-string">"{} responds with: {}"</span>.format(function_name, responder.content))
                <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> ex:
                    log.error(<span class="hljs-string">"{} failed to log response: {}"</span>.format(function_name, ex))
                <span class="hljs-keyword">return</span> responder
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> function(request, *args, **kwargs)
        <span class="hljs-keyword">return</span> wrapper
    <span class="hljs-keyword">return</span> log_func
</code></pre>
<p>Once I have custom decorator I can apply it to my view as shown in the following example.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyRestAPIView</span>(<span class="hljs-params">GenericAPIView</span>):</span>

    authentication_classes = ()
    authentication_classes = (authentication.TokenAuthentication,)
    permission_classes = (permissions.IsAuthenticated, )

<span class="hljs-meta">    @my_log</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post</span>(<span class="hljs-params">self, request</span>):</span>
        <span class="hljs-string">""""magic happens here"""</span>
        <span class="hljs-keyword">return</span> Response(response.data, status=http_status.HTTP_201_CREATED)
</code></pre>
<p>That’s been working just fine until Django version 5. Once upgraded to this I realized that my decorator does not work anymore. The easiest fix is to move it under method decorator call like in proceeding example.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> django.utils.decorators <span class="hljs-keyword">import</span> method_decorator

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyRestAPIView</span>(<span class="hljs-params">GenericAPIView</span>):</span>

<span class="hljs-meta">    @method_decorator((my_log())</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">post</span>(<span class="hljs-params">self, request</span>):</span>
        <span class="hljs-string">""""magic happens here"""</span>
        <span class="hljs-keyword">return</span> Response(response.data, status=http_status.HTTP_201_CREATED)
</code></pre>
<p>That’s it! Happy coding:)</p>
]]></content:encoded></item><item><title><![CDATA[Kinesis Advantage360 Hot-swappable keyboard]]></title><description><![CDATA[Since I’ve got this keyboard I fell in love with it. This piece of technology is definitely addressing most of my needs as software developer as having very stable and reliable keyboard. I’ve been using laptop keyboard for many, many years. This got ...]]></description><link>https://hubert-dev.xyz/kinesis-advantage360-hot-swappable-keyboard</link><guid isPermaLink="true">https://hubert-dev.xyz/kinesis-advantage360-hot-swappable-keyboard</guid><category><![CDATA[Advantage360]]></category><category><![CDATA[hot-swappable]]></category><category><![CDATA[Mechanical]]></category><category><![CDATA[keyboard]]></category><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Mon, 10 Feb 2025 22:45:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739221811257/86e48b28-31d0-4cb9-ba9e-8469fa2f5571.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Since I’ve got this keyboard I fell in love with it. This piece of technology is definitely addressing most of my needs as software developer as having very stable and reliable keyboard. I’ve been using laptop keyboard for many, many years. This got me into trouble after years of use - I started to get some back and shoulders pain. No, it is not about posture but just purely about the fact that hand on keyboards are too close to each-other much makes my chest to be mostly closed all the time when I type. That, with many hours of use, leaded to those injuries. That is why i decided to switch to split keybaord. Choice was quite obvious - it shouldn’t be just keyboard that is being split in a half but as well something more ergonomic as much as possible. I bought Kinesis <a target="_blank" href="https://kinesis-ergo.com/keyboards/advantage360/">Advantage 360</a>. I decided to got with wired version.</p>
<p><img src="https://c8.alamy.com/comp/2K9CTHP/keyboard-and-mouse-hands-posture-correct-and-incorrect-hand-position-for-text-typyng-wrist-and-arm-positions-for-computer-users-vector-illustration-2K9CTHP.jpg" alt class="image--center mx-auto" /></p>
<p>Since beginning I was surprised about the overall quality of plastic, keycaps and switched. I choose <a target="_blank" href="https://www.keychron.com/blogs/news/gateron-mechanical-switch-guide">Gateron</a> brown switches as something medium level for as my daily driver - not to clicky-clacky nor too soft. After some time of use I noticed that keyboard has been making quite some unwanted noise - I mean the actual keybord box was working like resonance box. That was quite annoying and sounded quite hmmm cheap. Inspired by <a target="_blank" href="https://www.youtube.com/watch?v=WaI0Tc3HZis">YoutTube video</a> I decided to upgrade my brown switches and go with <a target="_blank" href="https://www.gateron.co/products/gateron-switch-set">Gateron</a> White Pro. Then i discovered that my original switches are… soldered to PCB. Doh! Won’t be easy to replace :-( Then after reading a bit and google I thought to myself - wait a minute - why not making my keyboard hot-swappable then? In that case we need to go for a little shopping then.</p>
<p>To make our little project work we have to purchase</p>
<ul>
<li><p><a target="_blank" href="https://www.gateron.co/products/gateron-switch-set">switches</a> of choice</p>
</li>
<li><p>a bit a skills when it comes to <a target="_blank" href="https://youtu.be/Qps9woUGkvI">soldering</a></p>
</li>
<li><p>solder</p>
</li>
<li><p>hot-swappable <a target="_blank" href="https://a.aliexpress.com/_EJ72TnI">sockets</a> (make sure that you buy at least 200 of them - we’re going to need 2 of these per 1 switch)</p>
</li>
<li><p>soldering station (like for instance the one I have)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739178218830/d46d529a-2e81-4c88-bd75-4b0e402fbdb8.png" alt="soldering station" class="image--center mx-auto" /></p>
<ul>
<li>following unsoldering gun can be helpful as well</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739178180451/40f1f0c1-2aad-4aca-bf93-33d8d909a48a.png" alt="unsoldering station" class="image--center mx-auto" /></p>
<p>Once we have all the goodies we can start with removing key caps. Next we want to unsolder old switches. You can check following photo where you can see how I removed few switches and left clean soldering points on PCB (those with holes in them).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739176976559/74c4d431-676f-432a-b13f-a338bf7c6185.jpeg" alt class="image--center mx-auto" /></p>
<p>In the following photo you can see that keyboard PCB is double sided. That means - be really, really carful when unsoldering - do not use brute force. Be patient and make sure that before you‘re going to remove switch those switch pins are not soldered anymore to PCB. Being too nervous and giving too much pressure before the old solder is being removed can lead to damaging PCB paths on one side of the PCB. That is going with 100% centrality make your single key or even entire row to not work anymore! Let the peaceful mind be with you. Ohmmmmmm….</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177175942/8b8e1349-c3b0-4a02-b6f1-74e974605574.jpeg" alt class="image--center mx-auto" /></p>
<p>I noticed that sometimes even by cleaning the old soldering PCB hole is a bit too tight for the new hot-swappable socket. WIth this simple tool I maned to fix it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177205982/7d6fdaef-e1ac-4d96-b30e-24a31bdd2dc6.jpeg" alt="Altering soldering holes" class="image--center mx-auto" /></p>
<p>With proceeding photo you can see that I used old resistor as a little helper which was my tool to put those hot-swappable sockets to those PCB holes. Resistor, as its another fantastic feature, is absorbing heat so you won’t burn your fingers + socket does not fall out and stays still when being soldered to PCB.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177235092/5d262023-4471-4965-939c-2c0a83aed58a.jpeg" alt="Resistor as helper to hold socket still" class="image--center mx-auto" /></p>
<p>You can see in the following photo that some sockets I’ve been soldering are a little bit more or less shallow since PCB itself is very curved. Because of its quite unique shape I’d to manage to figure out how to find the right spot of how deep shall I solder those sockets. In that case once I put socket in I just dropped as well a tiny drop of solder and attached new switch to those new sockets. Next I checked how shallow or deep those sockets would have to be soldered by unsoldering them again and moving them up and down until I found the right spot. Next, I soldered them for good :).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177334291/68851d9b-2d1d-4ea0-b2b1-3a0ae6db1962.jpeg" alt="Soldering not too deep" class="image--center mx-auto" /></p>
<p>You can see that when I managed to adjust level of sockets and how deep they’re soldered. I was sure that newly attached switch to it will have proper contact with socket and electricity will flow as a thunder.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177386718/76f14c9b-bded-47e4-b9b7-69b2d5335cd8.jpeg" alt="solder not too deep" class="image--center mx-auto" /></p>
<p>Another thing to notice is that the switches I bought had those two extra plastic pins. In Advantage 360 PCB there were just no holes for them. In that case I used tool shown in the following photo to…cut them off. Case solved.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177432258/4838dde9-0e5a-4813-8559-13cadfbe49ea.jpeg" alt="cut off unwanted plastic dummy pins" class="image--center mx-auto" /></p>
<p>Original switch and the one “fixed” by me is shown side by side.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739177446351/3980e2e5-b11a-4e6e-89c6-76123c2a31ca.jpeg" alt="compare switches" class="image--center mx-auto" /></p>
<p>After popping in my new switches - they gently clicked once being attached to plastic cage. Now all the switches being tested I was happy with what I’ve accomplished. You can check this short <a target="_blank" href="https://youtu.be/fJN3jMapHXU">YouTube clip</a> I prepared to show how easy it is to replace those switches now.</p>
<p>In the end before fully reassembling keyboard I decided to put some foam inside (just below PCB) to make the whole keyboard box less noisy and sound more like a solid piece of plastic. For that task I purchased <a target="_blank" href="https://www.aliexpress.com/item/1005004684031433.html">soft cotton filling</a> that in the end is absorbing unwanted echo and noise super well. It is absolutely optional thing since inside the box there is no so much space left, albeit still IMHO it is worth it! That nice deep milky sound of new switches…</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1739260716714/7151046f-a514-4d36-9fcd-3510bf30eb25.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Sync sublime settings across]]></title><description><![CDATA[For me personally it was pretty annoying that there was no build in mechanizm of syncing preferences, especially those related to keyboard shortcuts or remapping. He re come the husaria - I found very easy way of doing it.
I will use github here but ...]]></description><link>https://hubert-dev.xyz/sync-sublime-settings-across</link><guid isPermaLink="true">https://hubert-dev.xyz/sync-sublime-settings-across</guid><category><![CDATA[Git]]></category><category><![CDATA[Sublime Text]]></category><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Fri, 12 Jan 2024 11:39:51 GMT</pubDate><content:encoded><![CDATA[<p>For me personally it was pretty annoying that there was no build in mechanizm of syncing preferences, especially those related to keyboard shortcuts or remapping. He re come the husaria - I found very easy way of doing it.</p>
<p>I will use <a target="_blank" href="https://github.com">github</a> here but if you prefer you may apply other git service of your personal preference of choice.</p>
<pre><code class="lang-bash">mv ~/Library/Application Support/Sublime Text/Packages/User/ /var/tmp/
</code></pre>
<p>Not create repository in github - suggestion it is to make it private if you don't want to share your personal settings to wide audience.</p>
<p>Next we go to local settings folder and checkout project from github.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/Library/Application Support/Sublime Text/Packages/
git <span class="hljs-built_in">clone</span> git <span class="hljs-built_in">clone</span> git@github.com:&lt;your GH username&gt;/sublime.git User
</code></pre>
<p>Now add moved files back.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> ~/Library/Application Support/Sublime Text/Packages/User
mv /var/tmp/User/* .
git add .
git commit -m <span class="hljs-string">"init commit"</span> .
git push --set-upstream origin main
</code></pre>
<p>Now we will be able to track all setting changes with git. Each time you update something you can got to above folder commit those change and push them to yoiur git repository.</p>
<p>One thing to notice is to add some files to <em>.gitignore</em> that we shall ignore from tracking.</p>
<pre><code class="lang-bash">Package Control.last-run
Package Control.ca-list
Package Control.ca-bundle
Package Control.system-ca-bundle
Package Control.cache/
Package Control.ca-certs/
Package Control.user-ca-bundle
Projects/*.sublime-workspacecion
</code></pre>
<p>Have fun!</p>
]]></content:encoded></item><item><title><![CDATA[Sorting IP addresses with SQLAlchemy]]></title><description><![CDATA[This has been driving me nuts how to efficiently sort IP address field in MySQL database. Ha! I managed to find one elegant solution. So let's assume we have this kind of table like in following example.
class TableFoo(BaseTable):
    __tablename__ =...]]></description><link>https://hubert-dev.xyz/sorting-ip-addresses-with-sqlalchemy</link><guid isPermaLink="true">https://hubert-dev.xyz/sorting-ip-addresses-with-sqlalchemy</guid><category><![CDATA[sqlalchemy]]></category><category><![CDATA[Python]]></category><category><![CDATA[Query]]></category><category><![CDATA[MySQL]]></category><dc:creator><![CDATA[Hubert Piotrowski]]></dc:creator><pubDate>Fri, 08 Dec 2023 18:02:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/aOC7TSLb1o8/upload/c87b8dfafbbda4871e4084e3bfe29a09.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This has been driving me nuts how to efficiently sort IP address field in MySQL database. Ha! I managed to find one elegant solution. So let's assume we have this kind of table like in following example.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TableFoo</span>(<span class="hljs-params">BaseTable</span>):</span>
    __tablename__ = <span class="hljs-string">"table_foo"</span>
    id = Column(Integer, primary_key=<span class="hljs-literal">True</span>)
    ip_address = Column(String(<span class="hljs-number">50</span>), nullable=<span class="hljs-literal">False</span>)
</code></pre>
<p>Now let us try to query for the last record.</p>
<pre><code class="lang-python">result = (
    session.query(UserLease)
    .order_by(desc(TableFoo.ip_address))
    .limit(<span class="hljs-number">1</span>).first()
    .ip_address
)
</code></pre>
<p>This is almost right albeit we please notice that we created IP address field as string so if we sort it like in this example we will get something like this</p>
<pre><code class="lang-python"><span class="hljs-string">'10.65.4.2'</span>,
<span class="hljs-string">'10.65.4.123'</span>,
<span class="hljs-string">'10.65.4.12'</span>,
<span class="hljs-string">'10.65.4.1'</span>
</code></pre>
<p>Nope, this is not correct :( MySQL has got <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/miscellaneous-functions.html#function_inet-aton">function</a> that will convert dotted notation IPv4 address to integer. Unfortunately SQLAlchemy does not support it out of the box. We can try other approach - we will use <a target="_blank" href="https://docs.sqlalchemy.org/en/20/core/sqlelement.html#sqlalchemy.sql.expression.func">func</a> wrapper to use MySQL function.</p>
<p>After rewriting sorting query with <strong>func</strong> in use we are going to do it like in following code.</p>
<pre><code class="lang-python">result = (
    session.query(UserLease)
    .order_by(esc(func.INET_ATON(UserLease.ip_address)))
    .limit(<span class="hljs-number">1</span>)
    .first()
    .ip_address
)
</code></pre>
<p>Thank you for attention. Have a productive day.</p>
]]></content:encoded></item></channel></rss>