{"action":"created","comment":{"url":"https://api.github.com/repos/PyConPL/Book/pulls/comments/39095243","id":39095243,"diff_hunk":"@@ -0,0 +1,144 @@\n+# Business logic with PostgreSQL and Python\r\n+\r\n+## Introduction\r\n+\r\n+In my opinion PostgreSQL is the most advanced open source database. It has so many powerful features that is really difficult to put all of them here in this one acticle.\r\n+\r\n+Here below I'm going to show you the least known feature of PostgreSQL which is procedural languages and stored functions. By using those stored procedures we are going to build business logic step by step.\r\n+\r\n+## Business logic\r\n+\r\n+By this term we are going to call all of those blocks of code which are responsible for making calculations and taking decisions about data flow. In many cases most of developers decide to build business logic as a part ORM models. It is pretty convenient. Whenever data is being used from database ORM can take control and decide how data is going to be returned to upper objects. Some ORM frameworks like Django allow us to extend base model and start building our own logic in a very complex way.\r\n+\r\n+Example piece of pseudo code in Django ORM\r\n+\r\n+\r\n+    from django.db import models\r\n+\r\n+    class _MyBaseModel(models.Model):\r\n+      def save(self, *args, **kwargs):\r\n+        # we can have some magic here\r\n+        # code code code\r\n+        \r\n+        # call actual save, so \"magic\" will happen before saving data into DB\r\n+        super(_MyBaseModel, self).save(*args, **kwargs) \r\n+        \r\n+    class History(_MyBaseModel):\r\n+      \"\"\" model which always calls custom save method before calling Django's save \"\"\"\r\n+      shops = models.IntegerField('Number of shops', default = 0, null = True)\r\n+      customers = models.IntegerField('Number of customers', default = 0, null=True)\r\n+      customer_type = models.SmallIntegerField('Customer Type', choices = CustomerTypes.CHOICES)\r\n+      \r\n+      def save(self, *args, **kwargs):\r\n+          # also we can have another level of customization here\r\n+          super(History, self).save(*args, **kwargs)\r\n+\r\n+By this simple example you can see how easly it is to customize save method in Django ORM. By having such a custom code you can put some business logic in there. For instance you are able to prevalidate data before saving or modify data before save takes an action. There are unlimited options.\r\n+\r\n+Some developers prefer to have business logic as a bunch of classes and functionalities stored in them. That is also a good solution as long as you keep attention and always in your code there is only one source of truth (code/functionality repetitions).\r\n+\r\n+## Why not ORM?\r\n+\r\n+Using ORM as a business logic container definitely has a lot of pros, although there is one serious problem with it. If you have a project which uses DB that is shared with other projects and those use different languages...then... You are going to use API for such a interactions with third-party software projects. How about if you have to deal with legacy code where there is not such an option to have API? Where to have business logic with one source of truth. How about using DB as a main source of trust?\r\n+\r\n+## Python\r\n+\r\n+Compiling Python is not a difficult process. Let me show you in few simples steps how to do it.\r\n+\r\n+Download python source code\r\n+\r\n+    wget https://www.python.org/ftp/python/2.7.10/Python-2.7.10.tgz\r\n+\r\n+Compile it. Warning, Please pay attention about –enable-shared flag!\r\n+\r\n+    ~/stuff/Python-2.7.10% ./configure —PREFIX=/opt/py –enable-shared\r\n+\r\n+Now it is very important to remember to compile python with **–enable-shared** option. Without it during PostgreSQL compilation you're not going to be able to use compiled Python as procedural language extension.\r","path":"2015/presentations/Business logic with PostgreSQL and Python/text.md","position":56,"original_position":56,"commit_id":"a660ce8bf9445cf30801e3fe4dedcf7da0a259b4","original_commit_id":"a660ce8bf9445cf30801e3fe4dedcf7da0a259b4","user":{"login":"soltysh","id":576341,"avatar_url":"https://avatars.githubusercontent.com/u/576341?v=3","gravatar_id":"","url":"https://api.github.com/users/soltysh","html_url":"https://github.com/soltysh","followers_url":"https://api.github.com/users/soltysh/followers","following_url":"https://api.github.com/users/soltysh/following{/other_user}","gists_url":"https://api.github.com/users/soltysh/gists{/gist_id}","starred_url":"https://api.github.com/users/soltysh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/soltysh/subscriptions","organizations_url":"https://api.github.com/users/soltysh/orgs","repos_url":"https://api.github.com/users/soltysh/repos","events_url":"https://api.github.com/users/soltysh/events{/privacy}","received_events_url":"https://api.github.com/users/soltysh/received_events","type":"User","site_admin":false},"body":"~~Now~~ It is","created_at":"2015-09-09T20:56:51Z","updated_at":"2015-09-09T20:56:51Z","html_url":"https://github.com/PyConPL/Book/pull/40#discussion_r39095243","pull_request_url":"https://api.github.com/repos/PyConPL/Book/pulls/40","_links":{"self":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/comments/39095243"},"html":{"href":"https://github.com/PyConPL/Book/pull/40#discussion_r39095243"},"pull_request":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/40"}}},"pull_request":{"url":"https://api.github.com/repos/PyConPL/Book/pulls/40","id":43922516,"html_url":"https://github.com/PyConPL/Book/pull/40","diff_url":"https://github.com/PyConPL/Book/pull/40.diff","patch_url":"https://github.com/PyConPL/Book/pull/40.patch","issue_url":"https://api.github.com/repos/PyConPL/Book/issues/40","number":40,"state":"open","locked":false,"title":"Finished article for pyConPL 2015","user":{"login":"darkman66","id":4584675,"avatar_url":"https://avatars.githubusercontent.com/u/4584675?v=3","gravatar_id":"","url":"https://api.github.com/users/darkman66","html_url":"https://github.com/darkman66","followers_url":"https://api.github.com/users/darkman66/followers","following_url":"https://api.github.com/users/darkman66/following{/other_user}","gists_url":"https://api.github.com/users/darkman66/gists{/gist_id}","starred_url":"https://api.github.com/users/darkman66/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darkman66/subscriptions","organizations_url":"https://api.github.com/users/darkman66/orgs","repos_url":"https://api.github.com/users/darkman66/repos","events_url":"https://api.github.com/users/darkman66/events{/privacy}","received_events_url":"https://api.github.com/users/darkman66/received_events","type":"User","site_admin":false},"body":"sorry for a small delay, I hope that there are no typos :)","created_at":"2015-09-01T20:28:42Z","updated_at":"2015-09-09T20:56:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b4cc44993a97fd38cbb1d1f76e4297df294848f1","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/PyConPL/Book/pulls/40/commits","review_comments_url":"https://api.github.com/repos/PyConPL/Book/pulls/40/comments","review_comment_url":"https://api.github.com/repos/PyConPL/Book/pulls/comments{/number}","comments_url":"https://api.github.com/repos/PyConPL/Book/issues/40/comments","statuses_url":"https://api.github.com/repos/PyConPL/Book/statuses/a660ce8bf9445cf30801e3fe4dedcf7da0a259b4","head":{"label":"darkman66:master","ref":"master","sha":"a660ce8bf9445cf30801e3fe4dedcf7da0a259b4","user":{"login":"darkman66","id":4584675,"avatar_url":"https://avatars.githubusercontent.com/u/4584675?v=3","gravatar_id":"","url":"https://api.github.com/users/darkman66","html_url":"https://github.com/darkman66","followers_url":"https://api.github.com/users/darkman66/followers","following_url":"https://api.github.com/users/darkman66/following{/other_user}","gists_url":"https://api.github.com/users/darkman66/gists{/gist_id}","starred_url":"https://api.github.com/users/darkman66/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darkman66/subscriptions","organizations_url":"https://api.github.com/users/darkman66/orgs","repos_url":"https://api.github.com/users/darkman66/repos","events_url":"https://api.github.com/users/darkman66/events{/privacy}","received_events_url":"https://api.github.com/users/darkman66/received_events","type":"User","site_admin":false},"repo":{"id":41074122,"name":"Book","full_name":"darkman66/Book","owner":{"login":"darkman66","id":4584675,"avatar_url":"https://avatars.githubusercontent.com/u/4584675?v=3","gravatar_id":"","url":"https://api.github.com/users/darkman66","html_url":"https://github.com/darkman66","followers_url":"https://api.github.com/users/darkman66/followers","following_url":"https://api.github.com/users/darkman66/following{/other_user}","gists_url":"https://api.github.com/users/darkman66/gists{/gist_id}","starred_url":"https://api.github.com/users/darkman66/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/darkman66/subscriptions","organizations_url":"https://api.github.com/users/darkman66/orgs","repos_url":"https://api.github.com/users/darkman66/repos","events_url":"https://api.github.com/users/darkman66/events{/privacy}","received_events_url":"https://api.github.com/users/darkman66/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/darkman66/Book","description":"This is a book which will be created by PyCon PL Organizers, based on PyCon PL Speaker's talks for PyCon PL Attendees.","fork":true,"url":"https://api.github.com/repos/darkman66/Book","forks_url":"https://api.github.com/repos/darkman66/Book/forks","keys_url":"https://api.github.com/repos/darkman66/Book/keys{/key_id}","collaborators_url":"https://api.github.com/repos/darkman66/Book/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/darkman66/Book/teams","hooks_url":"https://api.github.com/repos/darkman66/Book/hooks","issue_events_url":"https://api.github.com/repos/darkman66/Book/issues/events{/number}","events_url":"https://api.github.com/repos/darkman66/Book/events","assignees_url":"https://api.github.com/repos/darkman66/Book/assignees{/user}","branches_url":"https://api.github.com/repos/darkman66/Book/branches{/branch}","tags_url":"https://api.github.com/repos/darkman66/Book/tags","blobs_url":"https://api.github.com/repos/darkman66/Book/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/darkman66/Book/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/darkman66/Book/git/refs{/sha}","trees_url":"https://api.github.com/repos/darkman66/Book/git/trees{/sha}","statuses_url":"https://api.github.com/repos/darkman66/Book/statuses/{sha}","languages_url":"https://api.github.com/repos/darkman66/Book/languages","stargazers_url":"https://api.github.com/repos/darkman66/Book/stargazers","contributors_url":"https://api.github.com/repos/darkman66/Book/contributors","subscribers_url":"https://api.github.com/repos/darkman66/Book/subscribers","subscription_url":"https://api.github.com/repos/darkman66/Book/subscription","commits_url":"https://api.github.com/repos/darkman66/Book/commits{/sha}","git_commits_url":"https://api.github.com/repos/darkman66/Book/git/commits{/sha}","comments_url":"https://api.github.com/repos/darkman66/Book/comments{/number}","issue_comment_url":"https://api.github.com/repos/darkman66/Book/issues/comments{/number}","contents_url":"https://api.github.com/repos/darkman66/Book/contents/{+path}","compare_url":"https://api.github.com/repos/darkman66/Book/compare/{base}...{head}","merges_url":"https://api.github.com/repos/darkman66/Book/merges","archive_url":"https://api.github.com/repos/darkman66/Book/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/darkman66/Book/downloads","issues_url":"https://api.github.com/repos/darkman66/Book/issues{/number}","pulls_url":"https://api.github.com/repos/darkman66/Book/pulls{/number}","milestones_url":"https://api.github.com/repos/darkman66/Book/milestones{/number}","notifications_url":"https://api.github.com/repos/darkman66/Book/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/darkman66/Book/labels{/name}","releases_url":"https://api.github.com/repos/darkman66/Book/releases{/id}","created_at":"2015-08-20T03:46:46Z","updated_at":"2015-08-20T03:46:47Z","pushed_at":"2015-09-09T20:24:06Z","git_url":"git://github.com/darkman66/Book.git","ssh_url":"git@github.com:darkman66/Book.git","clone_url":"https://github.com/darkman66/Book.git","svn_url":"https://github.com/darkman66/Book","homepage":"http://pl.pycon.org/","size":5282,"stargazers_count":0,"watchers_count":0,"language":"TeX","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"PyConPL:master","ref":"master","sha":"71f3663e9a6471b0a36e2f14857ef68d9a705034","user":{"login":"PyConPL","id":5347274,"avatar_url":"https://avatars.githubusercontent.com/u/5347274?v=3","gravatar_id":"","url":"https://api.github.com/users/PyConPL","html_url":"https://github.com/PyConPL","followers_url":"https://api.github.com/users/PyConPL/followers","following_url":"https://api.github.com/users/PyConPL/following{/other_user}","gists_url":"https://api.github.com/users/PyConPL/gists{/gist_id}","starred_url":"https://api.github.com/users/PyConPL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PyConPL/subscriptions","organizations_url":"https://api.github.com/users/PyConPL/orgs","repos_url":"https://api.github.com/users/PyConPL/repos","events_url":"https://api.github.com/users/PyConPL/events{/privacy}","received_events_url":"https://api.github.com/users/PyConPL/received_events","type":"Organization","site_admin":false},"repo":{"id":22459875,"name":"Book","full_name":"PyConPL/Book","owner":{"login":"PyConPL","id":5347274,"avatar_url":"https://avatars.githubusercontent.com/u/5347274?v=3","gravatar_id":"","url":"https://api.github.com/users/PyConPL","html_url":"https://github.com/PyConPL","followers_url":"https://api.github.com/users/PyConPL/followers","following_url":"https://api.github.com/users/PyConPL/following{/other_user}","gists_url":"https://api.github.com/users/PyConPL/gists{/gist_id}","starred_url":"https://api.github.com/users/PyConPL/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/PyConPL/subscriptions","organizations_url":"https://api.github.com/users/PyConPL/orgs","repos_url":"https://api.github.com/users/PyConPL/repos","events_url":"https://api.github.com/users/PyConPL/events{/privacy}","received_events_url":"https://api.github.com/users/PyConPL/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/PyConPL/Book","description":"This is a book which will be created by PyCon PL Organizers, based on PyCon PL Speaker's talks for PyCon PL Attendees.","fork":false,"url":"https://api.github.com/repos/PyConPL/Book","forks_url":"https://api.github.com/repos/PyConPL/Book/forks","keys_url":"https://api.github.com/repos/PyConPL/Book/keys{/key_id}","collaborators_url":"https://api.github.com/repos/PyConPL/Book/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/PyConPL/Book/teams","hooks_url":"https://api.github.com/repos/PyConPL/Book/hooks","issue_events_url":"https://api.github.com/repos/PyConPL/Book/issues/events{/number}","events_url":"https://api.github.com/repos/PyConPL/Book/events","assignees_url":"https://api.github.com/repos/PyConPL/Book/assignees{/user}","branches_url":"https://api.github.com/repos/PyConPL/Book/branches{/branch}","tags_url":"https://api.github.com/repos/PyConPL/Book/tags","blobs_url":"https://api.github.com/repos/PyConPL/Book/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/PyConPL/Book/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/PyConPL/Book/git/refs{/sha}","trees_url":"https://api.github.com/repos/PyConPL/Book/git/trees{/sha}","statuses_url":"https://api.github.com/repos/PyConPL/Book/statuses/{sha}","languages_url":"https://api.github.com/repos/PyConPL/Book/languages","stargazers_url":"https://api.github.com/repos/PyConPL/Book/stargazers","contributors_url":"https://api.github.com/repos/PyConPL/Book/contributors","subscribers_url":"https://api.github.com/repos/PyConPL/Book/subscribers","subscription_url":"https://api.github.com/repos/PyConPL/Book/subscription","commits_url":"https://api.github.com/repos/PyConPL/Book/commits{/sha}","git_commits_url":"https://api.github.com/repos/PyConPL/Book/git/commits{/sha}","comments_url":"https://api.github.com/repos/PyConPL/Book/comments{/number}","issue_comment_url":"https://api.github.com/repos/PyConPL/Book/issues/comments{/number}","contents_url":"https://api.github.com/repos/PyConPL/Book/contents/{+path}","compare_url":"https://api.github.com/repos/PyConPL/Book/compare/{base}...{head}","merges_url":"https://api.github.com/repos/PyConPL/Book/merges","archive_url":"https://api.github.com/repos/PyConPL/Book/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/PyConPL/Book/downloads","issues_url":"https://api.github.com/repos/PyConPL/Book/issues{/number}","pulls_url":"https://api.github.com/repos/PyConPL/Book/pulls{/number}","milestones_url":"https://api.github.com/repos/PyConPL/Book/milestones{/number}","notifications_url":"https://api.github.com/repos/PyConPL/Book/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/PyConPL/Book/labels{/name}","releases_url":"https://api.github.com/repos/PyConPL/Book/releases{/id}","created_at":"2014-07-31T07:04:58Z","updated_at":"2015-09-04T14:14:39Z","pushed_at":"2015-09-09T20:44:27Z","git_url":"git://github.com/PyConPL/Book.git","ssh_url":"git@github.com:PyConPL/Book.git","clone_url":"https://github.com/PyConPL/Book.git","svn_url":"https://github.com/PyConPL/Book","homepage":"http://pl.pycon.org/","size":6559,"stargazers_count":12,"watchers_count":12,"language":"TeX","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":34,"mirror_url":null,"open_issues_count":3,"forks":34,"open_issues":3,"watchers":12,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/40"},"html":{"href":"https://github.com/PyConPL/Book/pull/40"},"issue":{"href":"https://api.github.com/repos/PyConPL/Book/issues/40"},"comments":{"href":"https://api.github.com/repos/PyConPL/Book/issues/40/comments"},"review_comments":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/40/comments"},"review_comment":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/PyConPL/Book/pulls/40/commits"},"statuses":{"href":"https://api.github.com/repos/PyConPL/Book/statuses/a660ce8bf9445cf30801e3fe4dedcf7da0a259b4"}}}}