{"id":25070759,"url":"https://github.com/eroydev/pythonorm-","last_synced_at":"2025-10-05T22:40:28.734Z","repository":{"id":245492692,"uuid":"817028475","full_name":"ERoydev/PythonORM-","owner":"ERoydev","description":null,"archived":false,"fork":false,"pushed_at":"2024-08-05T18:29:05.000Z","size":34457,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-06T21:38:15.805Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ERoydev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-18T21:54:19.000Z","updated_at":"2024-08-05T18:29:08.000Z","dependencies_parsed_at":"2024-08-05T21:39:15.251Z","dependency_job_id":null,"html_url":"https://github.com/ERoydev/PythonORM-","commit_stats":null,"previous_names":["eroydev/pythonorm-"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ERoydev%2FPythonORM-","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ERoydev%2FPythonORM-/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ERoydev%2FPythonORM-/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ERoydev%2FPythonORM-/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ERoydev","download_url":"https://codeload.github.com/ERoydev/PythonORM-/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246498164,"owners_count":20787268,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-06T21:34:15.210Z","updated_at":"2025-10-05T22:40:19.343Z","avatar_url":"https://github.com/ERoydev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Python-ORM-2024\n\n# Important commands\n\n### Zip project on Mac/Linux\n```bash\n   zip -r project.zip . -x \"*.idea*\" -x \"*venv*\" -x \"*__pycache__*\"\n```\n\n---\n\n### Zip project on Windows\n```bash\n   tar.exe -a -cf project.zip main_app orm_skeleton caller.py manage.py requirements.txt\n```\n\n---\n\n### Creation of Venv\n```bash\n\tpython3 -m venv ./venv\n\tsource ./venv/bin/activate\n\tvenv\\Scripts\\activate\n```\n\n---\n\n### dbshell on Docker\n\n```bash\n\tdocker exec -t \u003ccontainer_name\u003e /bin/bash\n\tpsql -U your_user_me -d your_db_name\n```\n\n---\n\n# Helpers\n\n- [Populate Django DB Script](https://github.com/DiyanKalaydzhiev23/PopulateDjangoModel)\n\n# Theory Tests\n\n---\n\n- [Django Models Basics](https://forms.gle/JwTbUtEkddw2Kc2R7)\n\n---\n\n- [Migrations and Django Admin](https://forms.gle/7G2KzMujkCzHDgPb8)\n\n---\n\n- [Data Operations with Django Queries](https://forms.gle/Pzay1RHaUuQCb1X68)\n\n---\n\n- [Working with Queries](https://forms.gle/kieTF55zwmK2eAaM7)\n\n---\n\n- [Django Relations](https://forms.gle/6uvQdwzqfxt87kD36)\n\n---\n\n- [Models Inheritance](https://forms.gle/jgC7Mk67gmaaNGvd7)\n\n---\n\n- [Advanced Models Techniques](https://forms.gle/gcqt9VcQvbmuaYbCA)\n\n---\n\n- [Advanced Django Queries](https://forms.gle/Q1BJDMCQ8NfSBbWg8)\n\n---\n\n- [SQL Alchemy](https://forms.gle/NP7SRghks5ra1jGp8)\n\n---\n\n# Plans\n\n--- \n\n### Django Models\n\n```\nORM - Object Relational Mapping\n```\n\n0. ORM - Предимства и недостатъци\n   - Pros:\n     -  Не ни се налага писането на low level SQL\n     -  По-лесна поддръжка\n     -  Добър при CRUD операции\n   - Cons:\n     - Не много оптимизиран за по-сложни заявки\n     - Възможно е да влага излишна сложност в някои от заявките\n\n2. Django models\n   - Всеки модел е отделна таблица\n   - Всяка променлова използваща поле от `models` е колона в тази таблица\n\n3. Създаване на модели\n   - Наследяваме `models.Model`\n    \n\n4. Migrations\n   - `makemigrations` - създава миграции\n   - `migrate` - прилага миграциите\n  \n5. Други команди\n   - `dbshell` - отваря конзола, в която можем да пишем SQL\n   - `CTRL + ALT + R` - отваря manage.py console\n\n---\n\n### Migrations and Admin\n\n1. Django Migrations Advanced\n   - Миграциите ни помагат надграждаме промени в нашите модели\n   - Както и да можем да пазим предишни стейтове на нашата база\n   - Команди:\n     - makemigrations\n     - migrate\n     - Връщане до определена миграция - migrate main_app 0001\n     - Връщане на всички миграции - migrate main_app zero\n     - showmigrations - показва всички апове и миграциите, които имат\n     - showmigrations app_name - показва миграциите за един app\n     - showmigrations --list - showmigrations -l \n     - squashmigrations app_name migration_to_which_you_want_to_sqash - събира миграциите до определена миграция в една миграция\n     - sqlmigrate app_name migration_name - дава ни SQL-а на текущата миграция - използваме го, за да проверим дали миграцията е валидна\n     - makemigrations --empty main_app - прави празна миграция в зададен от нас app\n\n2. Custom/Data migrations\n   - Когато например добавим ново поле, искаме да го попълним с данни на база на вече съществуващи полета, използваме data migrations\n   - RunPython\n     - викайки функция през него получаваме достъп до всички апове и техните модели (първи параметър), Scheme Editor (втори параметър)\n     - добра практика е да подаваме фунцкия и reverse функция, за да можем да връяа безпроблемно миграции\n   - Scheme Editor - клас, който превръща нашия пайтън код в SQL, ползваме го когато правим create, alter и delete на таблица\n     - използвайки RunPython в 95% от случаите няма да ни се наложи да ползавме Scheme Editor, освен, ако не правим някаква временна таблица\n       индекси или промяна на схемата на таблицата\n   - Стъпки:\n  \n      2.1. Създаваме празен файл за миграция: makemigrations --empty main_app - прави празна миграция в зададен от нас app\n      \n      2.2. Дефиниране на операции - Използваме RunPython за да изпълним data migrations\n      \n      2.3. Прилагане на промените - migrate\n\nПример с временна таблица:\n\nДа приемем, че имате модел с име „Person“ във вашето Django приложение и искате да създадете временна таблица, за да съхранявате някои изчислени данни въз основа на съществуващите данни в таблицата „Person“. \nВ този случай можете да използвате мигриране на данни, за да извършите тази операция:\n\n1. **Create the Data Migration:**\n\nRun the following command to create a data migration:\n\n```bash\npython manage.py makemigrations your_app_name --empty\n```\n\nThis will create an empty data migration file.\n\n2. **Edit the Data Migration:**\n\nOpen the generated data migration file and modify it to use `RunPython` with a custom Python function that utilizes the `SchemaEditor` to create a temporary table. Here's an example:\n\n```python\nfrom django.db import migrations, models\n\ndef create_temporary_table(apps, schema_editor):\n    # Get the model class\n    Person = apps.get_model('your_app_name', 'Person')\n\n    # Access the SchemaEditor to create a temporary table\n    schema_editor.execute(\n        \"CREATE TEMPORARY TABLE temp_person_data AS SELECT id, first_name, last_name FROM your_app_name_person\"\n    )\n\ndef reverse_create_temporary_table(apps, schema_editor):\n    schema_editor.execute(\"DROP TABLE temp_person_data\")\n\nclass Migration(migrations.Migration):\n\n    dependencies = [\n        ('your_app_name', 'previous_migration'),\n    ]\n\n    operations = [\n        migrations.RunPython(create_temporary_table, reverse_create_temporary_table),\n    ]\n```\n\n3. Django admin\n   - createsuperuser\n   - Register model, example:\n   \n   ```python\n      @admin.register(OurModelName)\n      class OurModelNameAdmin(admin.ModelAdmin):\n   \tpass\n   ```\n4. Admin site customizations\n  - __str__ метод в модела, за да го визуализираме в админ панела по-достъпно\n\n  - list_display - Показваме различни полета още в админа\n    Пример: \n    ```python\n    class EmployeeAdmin(admin.ModelAdmin):\n    \tlist_display = ['job_title', 'first_name', 'email_address']\n    ```\n\n  - List filter - добавя страничен панел с готови филтри\n    Пример:\n\n      ```python\n       class EmployeeAdmin(admin.ModelAdmin):\n       \tlist_filter = ['job_level']\n      ```\n\n  - Searched fields - казваме, в кои полета разрешаваме да се търси, по дефолт са всички\n    Пример:\n    \n    ```python\n    class EmployeeAdmin(admin.ModelAdmin):\n        search_fields = ['email_address']\n    ```\n  \n  - Layout changes - избираме, кои полета как и дали да се появяват при добавяне или промяна на запис\n    Пример:\n    \n    ```python\n    class EmployeeAdmin(admin.ModelAdmin):\n        fields = [('first_name', 'last_name'), 'email_address']\n    ```\n\n  - list_per_page\n   \n  - fieldsets - променяме визуално показването на полетата\n    Пример:\n    ```python\n      fieldsets = (\n           ('Personal info',\n            {'fields': (...)}),\n           ('Advanced options',\n            {'classes': ('collapse',),\n           'fields': (...),}),\n      )\n    ```\n\n---\n\n### Data Operations in Django with queries\n\n\n1. CRUD overview\n   - CRUD - Create, Read, Update, Delete\n   - Използваме го при: \n     - Web Development\n     - Database Management\n   - Дава ни един консистентен начин, за това ние да създаваме фунцкионалност за CRUD\n   - Можем да го правим през ORM-a на Джанго\n\n2. Мениджър в Django:\n    - Атрибут на ниво клас на модел за взаимодействия с база данни.\n    - Отговорен за CRUD\n    - Custom Manager: Подклас models.Model.\n       - Защо персонализирани мениджъри:\n         - Капсулиране на общи или сложни заявки.\n         - Подобрена четимост на кода.\n         - Избягвайме повторенията и подобряваме повторната употреба.\n         - Промяна наборите от заявки според нуждите.\n\n3. Django Queryset\n   - QuerySet - клас в пайтън, които изпозваме, за да пазим данните от дадена заявка\n   - Данните не се взимат, докато не бъдат потърсени от нас\n   - cars = Cars.objects.all() # \u003cQuerySet []\u003e\n   - print(cars)  # \u003cQuerySet [Car object(1)]\u003e\n\n   - QuerySet Features: \n     - Lazy Evaluation - примера с колите, заявката не се вика, докато данните не потрябват\n     - Retrieving objects - можем да вземаме всички обекти или по даден критерии\n     - Chaining filters - MyModel.objects.filter(category='electronics').filter(price__lt=1000)\n     - query related objects - позволява ни да търсим в таблици, с които имаме релации, през модела: # Query related objects using double underscores\nrelated_objects = Order.objects.filter(customer__age__gte=18)\n     - Ordering - ordered_objects = Product.objects.order_by('-price')\n     - Pagination \n      ```python\n       from django.core.paginator import Paginator\n\n        # Paginate queryset with 10 objects per page\n        paginator = Paginator(queryset, per_page=10)\n        page_number = 2\n        print([x for x in paginator.get_page(2)])\n      ```\n\n4. Django Simple Queries\n   - Object Manager - default Objects\n   - Methods:\n     - `all()`\n     - `first()`\n     - `get(**kwargs)`\n     - `create(**kwargs)`\n     - `filter(**kwargs)`\n     - `order_by(*fields)`\n     - `delete()`\n\n5. Django Shell and SQL Logging\n   - Django Shell\n     - Дава ни достъп до целия проект\n     - python manage.py shell\n   - SQL logging\n     -  Enable SQL logging\n\n```python\nLOGGING = {\n    'version': 1,\n    'disable_existing_loggers': False,\n    'handlers': {\n        'console': {\n            'class': 'logging.StreamHandler',\n        },\n    },\n    'root': {\n        'handlers': ['console'],\n        'level': 'DEBUG',  # Other levels CRITICAL, ERROR, WARNING, INFO, DEBUG\n    },\n    'loggers': {\n        'django.db.backends': {  # responsible for the sql logs\n            'handlers': ['console'],\n            'level': 'DEBUG',\n            'propagate': False,\n        },\n    },\n}\n```\n\n---\n\n### Working with queries\n\n\n1. Useful Methods\n   - filter() - връща subset от обекти; приема kwargs; връща queryset;\n   - exclude() - връща subset от обекти; приема kwargs; връща queryset;\n   - order_by() - връща сортираните обекти; - за desc;\n   - count() - като len, но по-бързо; count връща само бройката без да му трябвата реалните обекти;\n   - get() - взима един обект по даден критерии;\n\n\n2. Chaning methods\n   - всеки метод работи с върнатия от предишния резултат\n\n\n3. Lookup keys\n   - Използват се във filter, exclude, get;\n   - __exact __iexact - матчва точно;\n   - __contains __icontains - проверява дали съдържа;\n   - __startswith __endswith\n   - __gt __gte\n   - __lt __lte\n   - __range=(2, 5) - both inclusive\n\n4. Bulk methods\n   - използват се, за да извършим операции върху много обекти едновременно\n   - bulk_create - създава множество обекти навъеднъж;\n   - filter().update()\n   - filter().delete()\n\n---\n\n\n\n\n###  Django Relations\n\nDjango Models Relations\n\n\n1. Database Normalization\n   - Efficient Database Organization\n     - Data normalization - разбива големи таблици на по-малки такива, правейки данните по-организирани\n     - Пример: Все едно имаме онлайн магазин и вместо да пазим име, адрес и поръчка в една таблица, можем да разбием на 3 таблици и така да не повтаряме записи\n   \n    - Guidelines and Rules\n      - First Normal Form (1NF):\n        - елеминираме поврарящите се записи, всяка таблица пази уникални стойности\n\n      - Second Normal Form (2NF): извършваме първото като го правим зависимо на PK\n        - Пример: Онлайн магазин с данни и покупки Customers и Orders са свързани с PK, вместо всичко да е в една таблица\n\t\n      - Third Normal Form (3NF):\n        - премахване на преходни зависимости\n        - Таблица служители пази id, служител, град, адрес =\u003e разделяме ги на 3 таблици и ги навързваме, без да е задължително по PK, може и по city_id вече employee е независимо\n\t\n      - Boyce-Codd Normal Form (BCNF):\n        - По-строга версия на 3NF\n        - Тук правим да се навързват по PK\n\t\n      - Fourth Normal Form (4NF):\n        - Ако данни от една таблица се използват в други две то това не е добре\n        - Пример: Имаме Курс X и Курс Y, на X Му трябват книгите A и B, на Y, A и C,\n\t    това, което правим е да направим таблица с книгите А и таблица с Книгите Б\n\t \n      - Fifth Normal Form (5NF) - Project-Join Normal Form or PJ/NF:\n        - Кратко казано да не ни се налага да минаваме през таблици с данни, които не ни трябват, за да достигнем до таблица с данни, която ни трябва\n\n   - Database Schema Design\n      - Създаването на различни ключове и връзки между таблиците\n\n   - Minimizing Data Redundancy\n     - Чрез разбиването на таблици бихме имали отново намалено повтаряне на информация\n     - Имаме книга и копия, копията са в отделна таблица, и са линкнати към оригинала\n   \n   - Ensuring Data Integrity \u0026 Eliminating Data Anomalies\n     - Това ни помага да update-ваме и изтриваме данните навсякъде еднакво\n     - отново благодарение на някакви constraints можем да променим една стойност в една таблица и тя да се отрази във всички\n\n   - Efficiency and Maintainability\n     - Благодарение на по-малките таблици, ги query–ваме и update-ваме по-бързо\n\n2. Релации в Django Модели\n   - Получават се използвайки ForeignKey полета\n   - related_name - можем да направим обартна връзка\n     - По дефолт тя е името + _set\n  \n   - Пример:\n   ```py\n   class Author(models.Model):\n       name = models.CharField(max_length=100)\n   \n   class Post(models.Model):\n       title = models.CharField(max_length=200)\n       content = models.TextField()\n       author = models.ForeignKey(Author, on_delete=models.CASCADE)\n   ```\n\n- Access all posts written by an author\n```py\nauthor = Author.objects.get(id=1)\nauthor_posts = author.post_set.all()\n```\n\n3. Types of relationships\n   - Many-To-One (One-To-Many)\n   - Many-To-Many \n     - Няма значение, в кой модел се слага\n     - Django автоматично създава join таблица или още наричана junction\n     - Но, ако искаме и ние можем да си създадем: \n      ```py\n      class Author(models.Model):\n          name = models.CharField(max_length=100)\n      \n      class Book(models.Model):\n          title = models.CharField(max_length=200)\n          authors = models.ManyToManyField(Author, through='AuthorBook')\n      \n      class AuthorBook(models.Model):\n          author = models.ForeignKey(Author, on_delete=models.CASCADE)\n          book = models.ForeignKey(Book, on_delete=models.CASCADE)\n          publication_date = models.DateField()\n      ```\n\n   - OneToOne, предимно се слага на PK\n   - Self-referential Foreign Key\n      - Пример имаме работници и те могат да са мениджъри на други работници\n        \n   ```py\n   class Employee(models.Model):\n       name = models.CharField(max_length=100)\n       supervisor = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)\n   ```\n\n    - Lazy Relationships - обекта от релацията се взима, чрез заявка, чак когато бъде повикан\n\n---\n\n\n### Models Inheritance and Customization\n\n1. Типове наследяване\n   - Multi-table\n     - Разширяваме модел с полетата от друг модел, като не копираме самите полета, а използваме създадения от django pointer, който прави One-To-One Relationship\n     - Пример: \n\n\t```py\n\tclass Person(models.Model):\n\t    name = models.CharField(max_length=100)\n\t    date_of_birth = models.DateField()\n\t    \n\t    def is_student(self):\n\t        \"\"\"Check if this person is also a student.\"\"\"\n\t        return hasattr(self, 'student')\n\t\n\tclass Student(Person):\n\t    student_id = models.CharField(max_length=15)\n\t    major = models.CharField(max_length=50)\n\t```\t\n\n\n   - Abstract Base Classes\n     - При това наследяване не се създават две нови таблици, а само една и тя е на наследяващия клас(Child), като абстрактния клас(Parent) е само шаблон\n     - Постигаме го чрез промяна на Meta класа:\n       ```py\n       class AbstractBaseModel(models.Model):\n           common_field1 = models.CharField(max_length=100)\n           common_field2 = models.DateField()\n    \n           def common_method(self):\n               return \"This is a common method\"\n    \n           class Meta:\n               abstract = True\n       ```\n\n   - Proxy Models\n     - Използваме ги, за да добавим функционалност към модел, който не можем да достъпим\n     - Можем да добавяме методи, но не и нови полета\n     - Пример:\n\n\t```py\n\tclass Article(models.Model):\n\t    title = models.CharField(max_length=200)\n\t    content = models.TextField()\n\t    published_date = models.DateField()\n\t\n\tclass RecentArticle(Article):\n\t    class Meta:\n\t        proxy = True\n\t\n\t    def is_new(self):\n\t        return self.published_date \u003e= date.today() - timedelta(days=7)\n\t    \n\t    @classmethod\n\t    def get_recent_articles(cls):\n\t        return cls.objects.filter(published_date__gte=date.today() - timedelta(days=7))\n\t```\n\n2. Основни Built-In Методи\n   - `save()` - използва се за запазване на записи\n\t```py\n\t    def save(self, *args, **kwargs):\n\t        # Check the price and set the is_discounted field\n\t        if self.price \u003c 5:\n\t            self.is_discounted = True\n\t        else:\n\t            self.is_discounted = False\n\t\n\t        # Call the \"real\" save() method\n\t        super().save(*args, **kwargs)\n\t```\n   - `clean()` - използва се, когато искаме да валидираме логически няколко полета, например имаме тениска в 3 цвята, но ако е избран XXL цветовете са само 2.\n \n\n3. Custom Model Properties\n   - Както и в ООП, можем чрез @property декоратора да правим нови атрибути, които в случая не се запазват в базата\n   - Използваме ги за динамични изчисления на стойностти\n\n4. Custom Model Fields\n   - Ползваме ги когато, Django няма field, които ни върши работа\n   - Имаме методи като:\n     - from_db_value - извиква се, когато искаме да взмем стойността от базата в пайтън\n     - to_python - извиква се когато правим десериализация или clean\n     - get_prep_value - обратното на from_db_value, от Python към базата, предимно ползваме за сериализации\n     - pre_save - използва се за last minute changes, точно преди да запазим резултата в базата\n\n\t```py\n\tclass RGBColorField(models.TextField):\n\t    # Convert the database format \"R,G,B\" to a Python tuple (R, G, B)\n\t    def from_db_value(self, value, expression, connection):\n\t        if value is None:\n\t            return value\n\t        return tuple(map(int, value.split(',')))\n\t\n\t    # Convert any Python value to our desired format (tuple)\n\t    def to_python(self, value):\n\t        if isinstance(value, tuple) and len(value) == 3:\n\t            return value\n\t        if isinstance(value, str):\n\t            return tuple(map(int, value.split(',')))\n\t        raise ValidationError(\"Invalid RGB color format.\")\n\t\n\t    # Prepare the tuple format for database insertion\n\t    def get_prep_value(self, value):\n\t        # Convert tuple (R, G, B) to \"R,G,B\" for database storage\n\t        return ','.join(map(str, value))\n\t```\n\n---\n\n\n### Advanced Django Models Techniques\n \n\n1. Validation in Models\n   - Built-in Validators\n     - MaxValueValidator, MinValueValidator - приема два аргумета (limit, message)\n     - MaxLengthValidator, MinLengthValidator - приема два аргумета (limit, message)\n     - RegexValidator - приема два аргумета (regex, message)\n\t```py\n\tclass SampleModel(models.Model):\n\t    name = models.CharField(\n\t        max_length=50,\n\t        validators=[MinLengthValidator(5)]  # Name should have a minimum length of 5 characters\n\t    )\n\t\n\t    age = models.IntegerField(\n\t        validators=[MaxValueValidator(120)]  # Assuming age shouldn't exceed 120 years\n\t    )\n\t\n\t    phone = models.CharField(\n\t        max_length=15,\n\t        validators=[\n\t\t    RegexValidator(\n\t\t        regex=r'^\\+?1?\\d{9,15}$',\n\t                message=\"Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.\"\n\t\t)]  # A simple regex for validating phone numbers\n\t    )\n\t```\n - Custom Validators - функции, които често пишем в отделен файл. При грешка raise-ваме ValidationError\n\n\n2. Meta Options and Meta Inheritance\n   - В мета класа можем да променяме:\n     - Името на таблицата\n     - Подреждането на данните\n     - Можем да задаваме constraints\n     - Можем да задаваме типа на класа(proxy, abstract)\n\t```py\n\tclass SampleModel(models.Model):\n\t    name = models.CharField(max_length=50)\n\t    age = models.IntegerField()\n\t    email = models.EmailField()\n\t\n\t    class Meta:\n\t        # Database table name\n\t        db_table = 'custom_sample_model_table'\n\t\n\t        # Default ordering (ascending by name)\n\t        ordering = ['name'] - Случва се на SELECT, не на INSERT\n\t\n\t        # Unique constraint (unique combination of name and email)\n\t        unique_together = ['name', 'email']\n\t```\n    - Meта наследяване:\n      - Ако наследим абстрактен клас и не презапишем мета класа, то наслеяваме мета класа на абстрактния клас\n\t```py\n\tclass BaseModel(models.Model):\n\t    name = models.CharField(max_length=100)\n\t    \n\t    class Meta:\n\t        abstract = True\n\t        ordering = ['name']\n\t\n\tclass ChildModel(BaseModel):\n\t    description = models.TextField()\n\t    # ChildModel inherits the Meta options\n\t```\n\n3. Indexing\n   - Индексирането ни помага, подреждайки елементите в определен ред или създавайки друга структура, чрез, която да търсим по-бързо.\n   - Бързо взимаме записи, но ги запазваме по-бавно\n   - В Django можем да сложим индекс на поле, като добавим key-word аргумента db_index=True\n   - Можем да направим и индекс, чрез мета класа, като можем да правим и композитен индекс\n\t```py\n\tclass Meta:\n\tindexes=[\n\tmodels.Index(fields=[\"title\", \"author\"]),  # прави търсенето по два критерия, по-бързо\n\tmodels.Index(fields=[\"publication_date\"])\n\t]\n\t```\n\n\n4. Django Model Mixins\n   - Както знаем, миксините са класове, които използваме, за да отделим обща функционалност\n\n\t```py\n\tclass TimestampMixin(models.Model):\n\t    created_at = models.DateTimeField(auto_now_add=True)\n\t    updated_at = models.DateTimeField(auto_now=True)\n\t\tclass Meta:\n\t    \t    abstract = True\n\t```\n\n---\n\n### Advanced Django Queries\n\n1. Custom managers\n   - Използваме ги, за да изнсем бизнес логиката, за често използвани заявки на едно място\n   - Правим го наследявайки мениджъра по подразбиране.\n\t\n\t```py\n\t    class BookManager(models.Manager):\n\t        def average_rating(self):\n\t            # Calculate the average rating of all books\n\t            return self.aggregate(avg_rating=models.Avg('rating'))['avg_rating']\n\t\n\t        def highest_rated_book(self):\n\t            # Get the highest-rated book\n\t            return self.order_by('-rating').first()\n\t```\n\n2. Annotations and Aggregations\n   - Анотации - използваме ги, за да добавяме нови полета във върнатия резултат, често на база някакви изчисления. Връща QuerySet.\n   - Пример:\n\t\n\t```py\n\t# Annotating the queryset to get the count of books for each author\n\tauthors_with_book_count = Book.objects.values('author').annotate(book_count=Count('id'))\n\t```\n   - Агрегации - връщат едно поле(една стойност), често резултат от агрегиращи функции. Връща dict\n\t\n\t```py\n\t# Annotating the queryset to get the average rating of all books\n\taverage_rating = Book.objects.aggregate(avg_rating=Avg('rating'))\n\t```\n\n3. select_related \u0026 prefetch_related\n   - select_related - редуцира броя на заявките при One-To-One и Many-To-One заявки\n     - вместо lazily да взимаме свързаните обекти правим JOIN още при първата заявка\n     - Пример:\n\n\t```py\n\tfrom django.db import models\n\t\n\tclass Author(models.Model):\n\t    name = models.CharField(max_length=100)\n\t\n\tclass Book(models.Model):\n\t    title = models.CharField(max_length=100)\n\t    author = models.OneToOneField(Author, on_delete=models.CASCADE)\n\t\n\tbooks_with_authors = Book.objects.select_related('author') \n\t# SELECT * FROM \"myapp_book\" JOIN \"myapp_author\" ON (\"myapp_book\".\"author_id\" = \"myapp_author\".\"id\")\n\n\t```\n   \n   - prefetch_related - редуцира броя на заявките при Many-To-Many(не само) до броя на релациите + 1\n   - Пример:\n\n\t```py\n\tclass Author(models.Model):\n\t    name = models.CharField(max_length=100)\n\t\n\tclass Book(models.Model):\n\t    title = models.CharField(max_length=100)\n\t    authors = models.ManyToManyField(Author)\n\t\n\tauthors_with_books = Author.objects.prefetch_related('book_set')\n\t\n\t# 1. SELECT * FROM \"myapp_author\"\n\t# 2. SELECT * FROM \"myapp_book\" INNER JOIN \"myapp_book_authors\" ON (\"myapp_book\".\"id\" = \"myapp_book_authors\".\"book_id\")\n\t```\n\n4. Q and F\n\n  - Използваме Q object, за да правим заявки изискващи по-сложни условия\n  - Пример:\n\n\t```py\n\tq = Q(title__icontains='Django') \u0026 (Q(pub_year__gt=2010) | Q(author='John Doe'))\n\t\n\tbooks = Book.objects.filter(q)\n\t\n\t``` \n\n  - Използваме F object, за да достъпваме, стойностите през, които итерираме на ниво SQL\n\n\t```py\n\tfrom django.db.models import F\n\t\n\tBook.objects.update(rating=F('rating') + 1)\n\t```\n\n---\n\n### SQL Alchemy\n\n1. SQL Alchemy - ORM - Object Relational Mapper\n   - ORM - абстракция позволяваща ни да пишем SQL, чрез Python\n   - Core - грижи се за транзакциите, изпращането на заявки и database pooling\n\n2. SetUp:\n   1. ```pip install sqlalchemy```\n   2. ```pip install psycopg2```\n\n3. Модели\n   - Подобно на Django наследяваме базов клас, `Base`, който взимаме като резултат от извикването на `declarative_base()`\n\n\t```py\n \n\tfrom sqlalchemy.ext.declarative import declarative_base\n\t\n\tBase = declarative_base()\n\t\n\tclass User(Base):\n\t    __tablename__ = 'users'\n\t    id = Column(Integer, primary_key=True)\n\t    name = Column(String)\n\t```\n\n4. Миграции\n   \n   4.1 SetUp:\n   \t- Не са включени в SQLAlchemy, за тях можем да използваме `Alembic`\n   \t  \n\t- ```pip install alembic```\n\t\n \t- ```alembic init alembic``` - създава ни файловата структура за миграциите\u003c\\br\u003e\n       \n\t- ```sqlalchemy.url = postgresql+psycopg2://username:password@localhost/db_name``` - във файла alembic.ini\n\t\n \t- ```py target_metadata = Base.metadata``` - във файла env.py, за да можем да поддържаме autogenerate\n   \n   4.2 Команди:\n   \t- ```alembic revision --autogenerate -m \"Add User Table\"``` - създава миграция със съобщени, както `makemigrations`\n       \n\t- ```alembic upgrade head``` - прилага миграциите, както `migrate`\n       \n\t- ```alembic downgrade -1``` - връща миграция\n\n6. CRUD\n   - Отваряме връзка с базата, пускайки нова сесия\n   - Винаги затваряме сесията, след приключване на работа\n   - Трябва да комитнем резултата, подобно на Django, където ползвахме `save()`\n\n\t```py\n\tfrom sqlalchemy import create_engine\n\tfrom sqlalchemy.orm import sessionmaker\n\t\n\tengine = create_engine('sqlite:///example.db')\n\tSession = sessionmaker(bind=engine)\n\tsession = Session()\n\t\n\twith Session() as session: # a good practice\n\t...\n\t```\n   \n   5.1 Add:\n      ```py \n         new_user = User(username='john_doe', email='john@example.com') \n         session.add(new_user)\n      ```\n\n   5.2 Query\n      ```py\n   \tusers = session.query(User).all()\n      ```\n\n\n   5.3 Update\n      ```py \n      \n\twith engine.connect() as connection:\n\t    # Create an update object\n\t    upd = update(User).where(User.name == 'John').values(nickname='new_nickname')\n\t\n\t    # Execute the update\n\t    connection.execute(upd)\n      ```\n\n\tor\n\n      ```py\n\tsession.query(User).filter(User.name == 'John').update({\"nickname\": \"new_nickname\"}, synchronize_session=False)\t\n\tsession.commit()\n      ```\n\n   5.4 Delete\n       ```py\n   \n    \tdel_stmt = delete(User).where(User.name == 'John')\n       ```\n\n \n8. Transactions\n\t- `session.begin()`\n  \n\t- `session.commit()`\n\n \t- `session.rollback()`\n\n\n9. Relationships\n   1. Many to One\n      ```py\n         user_id = Column(Integer, ForeignKey('users.id'))\n         user = relationship('User')\n      ```\n   2. One to One\n     - `uselist=false`\n\t```py\n\tclass User(Base):\n\t    __tablename__ = 'users'\n\t    id = Column(Integer, primary_key=True)\n\t    profile = relationship(\"UserProfile\", back_populates=\"user\", uselist=False)\n\t\n\tclass UserProfile(Base):\n\t    __tablename__ = 'profiles'\n\t    id = Column(Integer, primary_key=True)\n\t    user_id = Column(Integer, ForeignKey('users.id'))\n\t    user = relationship(\"User\", back_populates=\"profile\")  \n\t```\n\n   3. Many to many\n     ```py\n\n\tuser_group_association = Table('user_group', Base.metadata,\n\t    Column('user_id', Integer, ForeignKey('users.id')),\n\t    Column('group_id', Integer, ForeignKey('groups.id'))\n\t)\n\t\n\tclass Group(Base):\n\t    __tablename__ = 'groups'\n\t    id = Column(Integer, primary_key=True)\n\t    users = relationship(\"User\", secondary=user_group_association, back_populates=\"groups\")\n\t\n\tclass User(Base):\n\t    __tablename__ = 'users'\n\t    id = Column(Integer, primary_key=True)\n\t    groups = relationship(\"Group\", secondary=user_group_association, back_populates=\"users\")\n\n    ```\n\n\n10. Database pooling\n```py engine = create_engine(DATABASE_URL, pool_size=10, max_overflow=20)``` - задава първоначални връзки и максимално създадени\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feroydev%2Fpythonorm-","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feroydev%2Fpythonorm-","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feroydev%2Fpythonorm-/lists"}