<?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>Forem: sumandari</title>
    <description>The latest articles on Forem by sumandari (@sumandari).</description>
    <link>https://forem.com/sumandari</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%2F263964%2F82bdade1-523d-4c69-9b51-58a76ab4ed02.png</url>
      <title>Forem: sumandari</title>
      <link>https://forem.com/sumandari</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/sumandari"/>
    <language>en</language>
    <item>
      <title>Flask-Migrate (SQLAlchemy) update length of String Field</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Thu, 20 Jul 2023 22:17:29 +0000</pubDate>
      <link>https://forem.com/sumandari/flask-migrate-sqlalchemy-update-length-of-string-field-2ohc</link>
      <guid>https://forem.com/sumandari/flask-migrate-sqlalchemy-update-length-of-string-field-2ohc</guid>
      <description>&lt;p&gt;I wanted to update the length of my string field model. But when I run &lt;code&gt;flask db migrate&lt;/code&gt; it says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"INFO  [alembic.env] No changes in schema detected."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So here's how I updated my length of a string field in my SQLALchemy model (I used postgres for the database)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create an empty migration file 
&lt;code&gt;flask db revision -m "your migration message here"&lt;/code&gt;.
it created a new migration file with this content:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="s"&gt;"""your migration message here

Revision ID: 24822ca23a29
Revises: c48c50454932
Create Date: 2023-07-20 23:37:28.918613

"""&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;alembic&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;op&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sqlalchemy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;sa&lt;/span&gt;


&lt;span class="c1"&gt;# revision identifiers, used by Alembic.
&lt;/span&gt;&lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'24822ca23a29'&lt;/span&gt;
&lt;span class="n"&gt;down_revision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'c48c50454932'&lt;/span&gt;
&lt;span class="n"&gt;branch_labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;span class="n"&gt;depends_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;downgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;according to that file, if you run &lt;code&gt;flask db current&lt;/code&gt;,  it will return the down_revision value: &lt;code&gt;c48c50454932&lt;/code&gt;.&lt;br&gt;
And if you want to implement this specific migration file, you need to run &lt;code&gt;flask db upgrade 24822ca23a29&lt;/code&gt; which is the value of &lt;code&gt;revision&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now, update the upgrade method with your expected string field properties.
I wanted to update field &lt;code&gt;my_field&lt;/code&gt; in &lt;code&gt;tablename&lt;/code&gt; table in my database. The existing length was 20, and I wanted to make it 50. Null value is not allowed in my case.
&lt;/li&gt;
&lt;/ul&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;upgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alter_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tablename'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'my_field'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;existing_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sa&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="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sa&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="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;existing_nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Always use downgrade method for the fallback just in case the migration doesn't work as expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;basically, I just wanted to make the length back to the previous value. So I switched the &lt;code&gt;existing_type&lt;/code&gt; and &lt;code&gt;type_&lt;/code&gt; value&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;downgrade&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;alter_column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'tablename'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'my_field'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;existing_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sa&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="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;type_&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sa&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="n"&gt;length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="n"&gt;existing_nullable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Run the &lt;code&gt;flask db upgrade&lt;/code&gt; to implement this latest migration file in your database schema and run &lt;code&gt;flask db downgrade&lt;/code&gt; to get the previous database schema.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flask</category>
      <category>sqlalchemy</category>
      <category>flaskmigrate</category>
      <category>alembic</category>
    </item>
    <item>
      <title>method (instance), @classmethod and @staticmethod</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Fri, 23 Jun 2023 14:46:37 +0000</pubDate>
      <link>https://forem.com/sumandari/method-instance-classmethod-and-staticmethod-1165</link>
      <guid>https://forem.com/sumandari/method-instance-classmethod-and-staticmethod-1165</guid>
      <description>&lt;p&gt;In a class we can have method, normal method without decorator. We also can have classmethod and staticmethod.&lt;/p&gt;

&lt;p&gt;the normal method is a method that is bound to the instance. It has &lt;code&gt;self&lt;/code&gt; as its first argument.&lt;br&gt;
classmethod is bound to the class itself. It's usually used as a factory. It has &lt;code&gt;cls&lt;/code&gt; as its first argument.&lt;br&gt;
staticmethod is not bound to either instance or class. It's a general function and it doesn't have either &lt;code&gt;self&lt;/code&gt; or &lt;code&gt;cls&lt;/code&gt; as its argument.&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;&amp;gt;&amp;gt; class Shape:
...     def __init__(self, name, height, width):
...         self.name = name
...         self.height = height
...         self.width = width
...     def __str__(self):
...         return f"shape: {self.name}, height: {self.height}, {self.width}"
...     @staticmethod
...     def area(height, width):
...         return height * width
...     @classmethod
...     def rectangular(cls):
...         return cls("rectangular", height, width)
...     @classmethod
...     def square(cls):
...         return cls("square", height, height)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;wait, is my code correct...?&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Random todos with iterator vs generator</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Thu, 15 Jun 2023 13:15:21 +0000</pubDate>
      <link>https://forem.com/sumandari/random-todos-with-iterator-objects-45bc</link>
      <guid>https://forem.com/sumandari/random-todos-with-iterator-objects-45bc</guid>
      <description>&lt;p&gt;we can iterate objects in a list:&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;&amp;gt;&amp;gt; persons = ["you", "I", "they"]
&amp;gt;&amp;gt;&amp;gt; for person in persons:
...     print(person)
... 
you
I
they
&amp;gt;&amp;gt;&amp;gt; 

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

&lt;/div&gt;



&lt;p&gt;We can also create an iterable object that will behave like a list with &lt;code&gt;__iter__&lt;/code&gt; magic method. And put logic on &lt;code&gt;__next__&lt;/code&gt; method.&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;&amp;gt;&amp;gt; import requests
&amp;gt;&amp;gt;&amp;gt; class RandomTodos:
...     url: str = 'https://jsonplaceholder.typicode.com/todos/'
...     def __init__(self, start=1):
...         self.current = start
...     def __next__(self):
...         current = self.current
...         res = requests.get(f"{self.url}{current}")
...         self.current += 1
...         return res.json().get('title', 'nothing todo')
...     def __iter__(self):
...         return self
... 
&amp;gt;&amp;gt;&amp;gt; person = ["you", "I", "they"]
&amp;gt;&amp;gt;&amp;gt; from pprint import pprint 
&amp;gt;&amp;gt;&amp;gt; pprint(list(zip(person, RandomTodos())))
[('you', 'delectus aut autem'),
 ('I', 'quis ut nam facilis et officia qui'),
 ('they', 'fugiat veniam minus')]
&amp;gt;&amp;gt;&amp;gt; todo = RandomTodos(3)
&amp;gt;&amp;gt;&amp;gt; next(todo)
'fugiat veniam minus'
&amp;gt;&amp;gt;&amp;gt; next(todo)
'et porro tempora'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also use generator for that&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;&amp;gt;&amp;gt; def random_todos(start=1):
...     url = "https://jsonplaceholder.typicode.com/todos/"
...     while True:
...         res = requests.get(f"{url}{start}")
...         yield res.json().get('title', 'nothing todo')
...         start += 1
... 
&amp;gt;&amp;gt;&amp;gt; todos = random_todos()
&amp;gt;&amp;gt;&amp;gt; next(todos)
'delectus aut autem'
&amp;gt;&amp;gt;&amp;gt; next(todos)
'quis ut nam facilis et officia qui'
&amp;gt;&amp;gt;&amp;gt; next(todos)
'fugiat veniam minus'
&amp;gt;&amp;gt;&amp;gt; person = ["you", "I", "they"]
&amp;gt;&amp;gt;&amp;gt; from pprint import pprint
&amp;gt;&amp;gt;&amp;gt; pprint(list(zip(person, random_todos())))
[('you', 'delectus aut autem'),
 ('I', 'quis ut nam facilis et officia qui'),
 ('they', 'fugiat veniam minus')]

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

&lt;/div&gt;



&lt;p&gt;read more about iterator: &lt;a href="https://wiki.python.org/moin/Iterator"&gt;https://wiki.python.org/moin/Iterator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;free api endpoints for testing: &lt;a href="https://jsonplaceholder.typicode.com"&gt;https://jsonplaceholder.typicode.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>oop</category>
    </item>
    <item>
      <title>sum, max, min, and statistics method with list and generator in python</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Thu, 15 Jun 2023 11:37:34 +0000</pubDate>
      <link>https://forem.com/sumandari/sum-max-min-and-statistics-method-with-list-and-generator-in-python-1a21</link>
      <guid>https://forem.com/sumandari/sum-max-min-and-statistics-method-with-list-and-generator-in-python-1a21</guid>
      <description>&lt;p&gt;If you have data both in list or generator, you can simplify the calculation by using &lt;code&gt;sum()&lt;/code&gt;, &lt;code&gt;max()&lt;/code&gt; and &lt;code&gt;min()&lt;/code&gt; built-in functions.&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;&amp;gt;&amp;gt; mylist = [_ * 3 for _ in range(10)]
&amp;gt;&amp;gt;&amp;gt; mylist
[0, 3, 6, 9, 12, 15, 18, 21, 24, 27]
&amp;gt;&amp;gt;&amp;gt; max(mylist)
27
&amp;gt;&amp;gt;&amp;gt; min(mylist)
0
&amp;gt;&amp;gt;&amp;gt; sum(mylist)
135
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if you use a generator, you need to re-create the iterated objects by re-defined the generator objects.&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;&amp;gt;&amp;gt; mygenerator = (_ * 3 for _ in range(10))
&amp;gt;&amp;gt;&amp;gt; mygenerator
&amp;lt;generator object &amp;lt;genexpr&amp;gt; at 0x1035170d0&amp;gt;
&amp;gt;&amp;gt;&amp;gt; max(mygenerator)
27
&amp;gt;&amp;gt;&amp;gt; min(mygenerator) # all objects have been iterated in the previous calculation max
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
ValueError: min() arg is an empty sequence
&amp;gt;&amp;gt;&amp;gt; mygenerator = (_ * 3 for _ in range(10))
&amp;gt;&amp;gt;&amp;gt; min(mygenerator)
0
&amp;gt;&amp;gt;&amp;gt; sum(_ * 3 for _ in range(10))
135
&amp;gt;&amp;gt;&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and you can also use methods in statistics module:&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;&amp;gt;&amp;gt; import statistics
&amp;gt;&amp;gt;&amp;gt; statistics.median_low(_ * 3 for _ in range(10))
12
&amp;gt;&amp;gt;&amp;gt; statistics.median_high(_ * 3 for _ in range(10))
15
&amp;gt;&amp;gt;&amp;gt; statistics.median(_ * 3 for _ in range(10))
13.5
&amp;gt;&amp;gt;&amp;gt; statistics.mean(_ * 3 for _ in range(10))
13.5
&amp;gt;&amp;gt;&amp;gt; statistics.mean(mylist)
13.5
&amp;gt;&amp;gt;&amp;gt; statistics.median_low(mylist)
12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;see all statistics methods at &lt;a href="https://docs.python.org/3.9/library/statistics.html"&gt;https://docs.python.org/3.9/library/statistics.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;read more about generator at &lt;a href="https://wiki.python.org/moin/Generators"&gt;https://wiki.python.org/moin/Generators&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>generator</category>
      <category>statistics</category>
    </item>
    <item>
      <title>Define fixed attributes dataclass with __slots__()</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Thu, 15 Jun 2023 09:29:40 +0000</pubDate>
      <link>https://forem.com/sumandari/define-fixed-attributes-dataclass-with-slots-3alo</link>
      <guid>https://forem.com/sumandari/define-fixed-attributes-dataclass-with-slots-3alo</guid>
      <description>&lt;p&gt;what is &lt;code&gt;__slots__()&lt;/code&gt;?&lt;br&gt;
see: &lt;a href="https://wiki.python.org/moin/UsingSlots"&gt;https://wiki.python.org/moin/UsingSlots&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;it defines fix objects in a &lt;code&gt;@dataclass&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;e.g dataclass without slots. we can define new_attribute in the instance&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;&amp;gt;&amp;gt; @dataclass
... class Rect:
...     height: int
...     width: int
... 
&amp;gt;&amp;gt;&amp;gt; r = Rect(1, 2)
&amp;gt;&amp;gt;&amp;gt; r
Rect(height=1, width=2)
&amp;gt;&amp;gt;&amp;gt; r.new_attribute = 5
&amp;gt;&amp;gt;&amp;gt; r.new_attribute
5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;e.g dataclass with fixed attribute&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;&amp;gt;&amp;gt; @dataclass
... class Circle:
...     __slots__ = ("r", )
...     r : int
... 
&amp;gt;&amp;gt;&amp;gt; c = Circle(10)
&amp;gt;&amp;gt;&amp;gt; c
Circle(r=10)
&amp;gt;&amp;gt;&amp;gt; c.new_attribute = 5
Traceback (most recent call last):
  File "&amp;lt;stdin&amp;gt;", line 1, in &amp;lt;module&amp;gt;
AttributeError: 'Circle' object has no attribute 'new_attribute'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>GithubAction -- use a workflow from another workflow</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Mon, 12 Jun 2023 09:44:23 +0000</pubDate>
      <link>https://forem.com/sumandari/githubaction-use-a-workflow-from-another-workflow-5acg</link>
      <guid>https://forem.com/sumandari/githubaction-use-a-workflow-from-another-workflow-5acg</guid>
      <description>&lt;p&gt;It's called &lt;code&gt;reusable&lt;/code&gt; workflow.&lt;/p&gt;

&lt;p&gt;I have a workflow for test, and I have another workflow for release. Everytime I release a version, I want to have test passed before run release jobs.&lt;/p&gt;

&lt;p&gt;What I have to do is to add &lt;code&gt;workflow_call&lt;/code&gt; on my workflow&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ci.yaml
name: ci-build-and-test

on:
  push:
    branches:
      - main
  pull_request:
  workflow_call:

jobs:
  - (steps to checkout, setup dependency and run test)

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

&lt;/div&gt;



&lt;p&gt;then I can call it manually from another workflow (yaml file) by using &lt;code&gt;uses&lt;/code&gt; and path to the ci.yaml file.&lt;br&gt;
If you're using the reusable workflow from different repository then you need to add your github organisation and repo name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# cd.yaml
name: cd-build-and-publish

on:
  release:
    types:
      - released

jobs:
  compliance:
    if: github.event.release.target_commitish == 'main'
    uses: ./.github/workflows/ci.yaml

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

&lt;/div&gt;



&lt;p&gt;see more at:&lt;br&gt;
&lt;a href="https://github.blog/2022-02-10-using-reusable-workflows-github-actions/"&gt;https://github.blog/2022-02-10-using-reusable-workflows-github-actions/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
      <category>devops</category>
    </item>
    <item>
      <title>Create a trigger in MySQL in SQLAlchemy migration</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Tue, 30 May 2023 20:19:21 +0000</pubDate>
      <link>https://forem.com/sumandari/create-a-trigger-in-mysql-in-sqlalchemy-migration-3ida</link>
      <guid>https://forem.com/sumandari/create-a-trigger-in-mysql-in-sqlalchemy-migration-3ida</guid>
      <description>&lt;p&gt;Create a trigger in MySQL in SQLAlchemy migration with op.execute.&lt;/p&gt;

&lt;p&gt;I wanted to have an update in last_update field, every time a user or an admin changed the data directly in the database (without python sqlalchemy). Instead of using signal, I created trigger inside &lt;code&gt;op.execute&lt;/code&gt; method&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('new_table',
        sa.Column('no_id', sa.Integer(), nullable=False),
        sa.Column('car', sa.Integer(), nullable=False),
        sa.Column('house', sa.Integer(), nullable=False),
        sa.Column('tier_id', sa.Integer(), nullable=False),
        sa.Column('last_updated', sa.TIMESTAMP(), nullable=True),
        sa.Column('timestamp', sa.TIMESTAMP(), server_default=sa.text('CURRENT_TIMESTAMP'), nullable=True),
        sa.ForeignKeyConstraint(['no_id'], ['companies.company_id'], ondelete='CASCADE'),
        sa.ForeignKeyConstraint(['tier_id'], ['tiers.id'], ondelete='CASCADE'),
        sa.PrimaryKeyConstraint('no_id')
    )
    op.execute('''
        CREATE TRIGGER tier_after_insert_trigger
        BEFORE UPDATE ON tier_table
        FOR EACH ROW
        BEGIN
            SET NEW.last_updated = CURRENT_TIMESTAMP();
        END;
    ''')
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.execute('DROP TRIGGER IF EXISTS tier_after_insert_trigger')
    op.drop_table('new_table')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>python</category>
      <category>sqlalchemy</category>
      <category>mysql</category>
    </item>
    <item>
      <title>When should I refactor my code into a decorator</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Tue, 09 May 2023 21:00:43 +0000</pubDate>
      <link>https://forem.com/sumandari/when-i-should-refactor-my-code-into-a-decorator-557i</link>
      <guid>https://forem.com/sumandari/when-i-should-refactor-my-code-into-a-decorator-557i</guid>
      <description>&lt;p&gt;Continue reading the book, this is a very good book. I suggest you to read it too. &lt;/p&gt;

&lt;p&gt;It says:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;don’t create a decorator in the first place. Wait until you see the pattern&lt;/strong&gt;. &lt;br&gt;
Yes, I totally agree. I created a decorator for user login and payload validation after I used it everywhere and found the pattern. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create the decorator if it will be used more than three times&lt;/strong&gt;. &lt;br&gt;
Okay, that also make senses. Why should I create a decorator if I only use the logic once. &lt;br&gt;
Not sure if it’s twice. But three is a fair number for me. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Keep the code in decorator minimum&lt;/strong&gt;. &lt;br&gt;
Too much line and logic will give me a headache when applying a decorator. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>SOLID principles (Python) in my simple understanding</title>
      <dc:creator>sumandari</dc:creator>
      <pubDate>Sat, 06 May 2023 21:36:42 +0000</pubDate>
      <link>https://forem.com/sumandari/solid-principle-python-in-my-simple-understanding-1aj3</link>
      <guid>https://forem.com/sumandari/solid-principle-python-in-my-simple-understanding-1aj3</guid>
      <description>&lt;p&gt;Nowadays, I have been learning SOLID principle in python.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;S: Single responsibility principle&lt;/strong&gt; -- A class/ module must have only one role.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O: Open/Closed principle&lt;/strong&gt; -- open to extension but closed to modification. This can be achieved by using an interface (a base/ mixin class that can be used polymorphically). We don't modify the base class itself, instead, we create new interfaces to full fill the requirements. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;L: Liskov substitution principle&lt;/strong&gt; -- different parts of a program can be used interchangeably, as long as they behave in the same way. e.g. Each car class has different properties, like its speed and handling, but they all behave in a similar way, because they all have to move around the track and avoid crashing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I: Interface segregation principle&lt;/strong&gt; -- interfaces should be small. Small enough to only do one thing but still contains methods needed. e.g: When you create a mixing class with context manager logic, it should contain at least two methods: &lt;code&gt;__enter__&lt;/code&gt; and &lt;code&gt;__exit__&lt;/code&gt;. They must go together, or the outcome will not be a valid context manager at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;D: Dependency inversion principle&lt;/strong&gt; --  high-level modules should not depend on low-level modules. Rather, both should depend on abstractions. Using general ideas instead of specific details to make it easier to modify and add new features to a program.&lt;/p&gt;

&lt;p&gt;references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean Code in Python - Second Edition by Mariano Anaya&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>beginners</category>
      <category>python</category>
      <category>systemdesign</category>
    </item>
  </channel>
</rss>
