Advertisement
GeorgiLukanov87

Python-ORM

May 22nd, 2024
550
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 33.71 KB | None | 0 0
  1.  
  2. ### Django Models
  3.  
  4. ```
  5. ORM - Object Relational Mapping
  6. ```
  7.  
  8. 1. Django models
  9.    - Всеки модел е отделна таблица
  10.    - Всяка променлова използваща поле от `models` е колона в тази таблица
  11.    - Моделите ни позволяват да не ни се налага писането на low level SQL
  12.  
  13. 2. Създаване на модели
  14.    - Наследяваме `models.Model`
  15.    
  16.  
  17. 3. Migrations
  18.    - `makemigrations`
  19.    - `migrate`
  20.  
  21. 4. Други команди
  22.    - `dbshell` - отваря конзола, в коятоо можем да пишем SQL
  23.    - `CTRL + ALT + R` - отваря manage.py console
  24.  
  25. ---
  26.  
  27. ### Migrations and Admin
  28.  
  29. 1. Django Migrations Advanced
  30.    - Миграциите ни помагат надграждаме промени в нашите модели
  31.    - Както и да можем да пазим предишни стейтове на нашата база
  32.    - Команди:
  33.      - makemigrations
  34.      - migrate
  35.      - Връщане до определена миграция - migrate main_app 0001
  36.      - Връщане на всички миграции - migrate main_app zero
  37.      - showmigrations - показва всички апове и миграциите, които имат
  38.      - showmigrations app_name - показва миграциите за един app
  39.      - showmigrations --list - showmigrations -l
  40.      - squashmigrations app_name migration_to_which_you_want_to_sqash - събира миграциите до определена миграция в една миграция
  41.      - sqlmigrate app_name migration_name - дава ни SQL-а на текущата миграция - използваме го, за да проверим дали миграцията е валидна
  42.      - makemigrations --empty main_app - прави празна миграция в зададен от нас app
  43.  
  44. 2. Custom/Data migrations
  45.    - Когато например добавим ново поле, искаме да го попълним с данни на база на вече съществуващи полета, използваме data migrations
  46.    - RunPython
  47.      - викайки функция през него получаваме достъп до всички апове и техните модели (първи параметър), Scheme Editor (втори параметър)
  48.      - добра практика е да подаваме фунцкия и reverse функция, за да можем да връяа безпроблемно миграции
  49.    - Scheme Editor - клас, който превръща нашия пайтън код в SQL, ползваме го когато правим create, alter и delete на таблица
  50.      - използвайки RunPython в 95% от случаите няма да ни се наложи да ползавме Scheme Editor, освен, ако не правим някаква временна таблица
  51.        индекси или промяна на схемата на таблицата
  52.    - Стъпки:
  53.      
  54.       2.1. Създаваме празен файл за миграция: makemigrations --empty main_app - прави празна миграция в зададен от нас app
  55.      
  56.       2.2. Дефиниране на операции - Използваме RunPython за да изпълним data migrations
  57.      
  58.       2.3. Прилагане на промените - migrate
  59.  
  60. Пример с временна таблица:
  61.  
  62. Да приемем, че имате модел с име „Person“ във вашето Django приложение и искате да създадете временна таблица, за да съхранявате някои изчислени данни въз основа на съществуващите данни в таблицата „Person“.
  63. В този случай можете да използвате мигриране на данни, за да извършите тази операция:
  64.  
  65. 1. **Create the Data Migration:**
  66.  
  67. Run the following command to create a data migration:
  68.  
  69. ```bash
  70. python manage.py makemigrations your_app_name --empty
  71. ```
  72.  
  73. This will create an empty data migration file.
  74.  
  75. 2. **Edit the Data Migration:**
  76.  
  77. Open 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:
  78.  
  79. ```python
  80. from django.db import migrations, models
  81.  
  82. def create_temporary_table(apps, schema_editor):
  83.    # Get the model class
  84.    Person = apps.get_model('your_app_name', 'Person')
  85.  
  86.    # Access the SchemaEditor to create a temporary table
  87.    schema_editor.execute(
  88.        "CREATE TEMPORARY TABLE temp_person_data AS SELECT id, first_name, last_name FROM your_app_name_person"
  89.    )
  90.  
  91. def reverse_create_temporary_table(apps, schema_editor):
  92.    schema_editor.execute("DROP TABLE temp_person_data")
  93.  
  94. class Migration(migrations.Migration):
  95.  
  96.    dependencies = [
  97.        ('your_app_name', 'previous_migration'),
  98.    ]
  99.  
  100.    operations = [
  101.        migrations.RunPython(create_temporary_table, reverse_create_temporary_table),
  102.    ]
  103. ```
  104.  
  105. 3. Django admin
  106.   - createsuperuser
  107.   - Register model, example:
  108.  
  109.   ```python
  110.      @admin.register(OurModelName)
  111.      class OurModelNameAdmin(admin.ModelAdmin):
  112.     pass
  113.   ```
  114. 4. Admin site customizations
  115.  - __str__ метод в модела, за да го визуализираме в админ панела по-достъпно
  116.  
  117.  - list_display - Показваме различни полета още в админа
  118.    Пример:
  119.    ```python
  120.    class EmployeeAdmin(admin.ModelAdmin):
  121.         list_display = ['job_title', 'first_name', 'email_address']
  122.    ```
  123.  
  124.  - List filter - добавя страничен панел с готови филтри
  125.    Пример:
  126.  
  127.      ```python
  128.       class EmployeeAdmin(admin.ModelAdmin):
  129.         list_filter = ['job_level']
  130.      ```
  131.  
  132.  - Searched fields - казваме, в кои полета разрешаваме да се търси, по дефолт са всички
  133.    Пример:
  134.    
  135.    ```python
  136.    class EmployeeAdmin(admin.ModelAdmin):
  137.        search_fields = ['email_address']
  138.    ```
  139.  
  140.  - Layout changes - избираме, кои полета как и дали да се появяват при добавяне или промяна на запис
  141.    Пример:
  142.    
  143.    ```python
  144.    class EmployeeAdmin(admin.ModelAdmin):
  145.        fields = [('first_name', 'last_name'), 'email_address']
  146.    ```
  147.  
  148.  - list_per_page
  149.  
  150.  - fieldsets - променяме визуално показването на полетата
  151.    Пример:
  152.    ```python
  153.      fieldsets = (
  154.           ('Personal info',
  155.            {'fields': (...)}),
  156.           ('Advanced options',
  157.            {'classes': ('collapse',),
  158.           'fields': (...),}),
  159.      )
  160.    ```
  161.  
  162. ---
  163.  
  164. ### Data Operations in Django with queries
  165.  
  166.  
  167. 1. CRUD overview
  168.   - CRUD - Create, Read, Update, Delete
  169.   - Използваме го при:
  170.     - Web Development
  171.     - Database Management
  172.   - Дава ни един консистентен начин, за това ние да създаваме фунцкионалност за CRUD
  173.   - Можем да го правим през ORM-a на Джанго
  174.  
  175. 2. Мениджър в Django:
  176.    - Атрибут на ниво клас на модел за взаимодействия с база данни.
  177.    - Отговорен за CRUD
  178.    - Custom Manager: Подклас models.Model.
  179.       - Защо персонализирани мениджъри:
  180.         - Капсулиране на общи или сложни заявки.
  181.         - Подобрена четимост на кода.
  182.         - Избягвайме повторенията и подобряваме повторната употреба.
  183.         - Промяна наборите от заявки според нуждите.
  184.  
  185. 3. Django Queryset
  186.   - QuerySet - клас в пайтън, които изпозваме, за да пазим данните от дадена заявка
  187.   - Данните не се взимат, докато не бъдат потърсени от нас
  188.   - cars = Cars.objects.all() # <QuerySet []>
  189.   - print(cars)  # <QuerySet [Car object(1)]>
  190.  
  191.   - QuerySet Features:
  192.     - Lazy Evaluation - примера с колите, заявката не се вика, докато данните не потрябват
  193.     - Retrieving objects - можем да вземаме всички обекти или по даден критерии
  194.     - Chaining filters - MyModel.objects.filter(category='electronics').filter(price__lt=1000)
  195.     - query related objects - позволява ни да търсим в таблици, с които имаме релации, през модела: # Query related objects using double underscores
  196. related_objects = Order.objects.filter(customer__age__gte=18)
  197.     - Ordering - ordered_objects = Product.objects.order_by('-price')
  198.     - Pagination
  199.      ```python
  200.       from django.core.paginator import Paginator
  201.  
  202.        # Paginate queryset with 10 objects per page
  203.        paginator = Paginator(queryset, per_page=10)
  204.        page_number = 2
  205.        print([x for x in paginator.get_page(2)])
  206.      ```
  207.  
  208. 4. Django Simple Queries
  209.   - Object Manager - default Objects
  210.   - Methods:
  211.     - `all()`
  212.     - `first()`
  213.     - `get(**kwargs)`
  214.     - `create(**kwargs)`
  215.     - `filter(**kwargs)`
  216.     - `order_by(*fields)`
  217.     - `delete()`
  218.  
  219. 5. Django Shell and SQL Logging
  220.   - Django Shell
  221.     - Дава ни достъп до целия проект
  222.     - python manage.py shell
  223.   - SQL logging
  224.     -  Enable SQL logging
  225.  
  226. ```python
  227. LOGGING = {
  228.    'version': 1,
  229.    'disable_existing_loggers': False,
  230.    'handlers': {
  231.        'console': {
  232.            'class': 'logging.StreamHandler',
  233.        },
  234.    },
  235.    'root': {
  236.        'handlers': ['console'],
  237.        'level': 'DEBUG',  # Other levels CRITICAL, ERROR, WARNING, INFO, DEBUG
  238.    },
  239.    'loggers': {
  240.        'django.db.backends': {  # responsible for the sql logs
  241.            'handlers': ['console'],
  242.            'level': 'DEBUG',
  243.            'propagate': False,
  244.        },
  245.    },
  246. }
  247. ```
  248.  
  249. ---
  250.  
  251.  
  252. ### Working with queries
  253.  
  254. Working with Queries
  255.  
  256.  
  257. 1. Useful Methods
  258.   - filter() - връща subset от обекти; приема kwargs; връща queryset;
  259.   - exclude() - връща subset от обекти; приема kwargs; връща queryset;
  260.   - order_by() - връща сортираните обекти; - за desc;
  261.   - count() - като len, но по-бързо; count връща само бройката без да му трябвата реалните обекти;
  262.   - get() - взима един обект по даден критерии;
  263.  
  264.  
  265. 2. Chaning methods
  266.   - всеки метод работи с върнатия от предишния резултат
  267.  
  268.  
  269. 3. Lookup keys
  270.   - Използват се във filter, exclude, get;
  271.   - __exact __iexact - матчва точно;
  272.   - __contains __icontains - проверява дали съдържа;
  273.   - __startswith __endswith
  274.   - __gt __gte
  275.   - __lt __lte
  276.   - __range=(2, 5) - both inclusive
  277.  
  278. 4. Bulk methods
  279.   - използват се за да извършим операции върху много обекти едновременно
  280.   - bulk_create - създава множество обекти навъеднъж;
  281.   - filter().update()
  282.   - filter().delete()
  283.  
  284. ---
  285.  
  286. ###  Django Relations
  287.  
  288. Django Models Relations
  289.  
  290.  
  291. 1. Database Normalization
  292.   - Efficient Database Organization
  293.     - Data normalization - разбива големи таблици на по-малки такива, правейки данните по-организирани
  294.     - Пример: Все едно имаме онлайн магазин и вместо да пазим име, адрес и поръчка в една таблица, можем да разбием на 3 таблици и така да не повтаряме записи
  295.  
  296.    - Guidelines and Rules
  297.      - First Normal Form
  298.         - First Normal Form (1NF): елеминираме поврарящите се записи, всяка таблица пази уникални стойности
  299.  
  300.        - Second Normal Form (2NF): извършваме първото като го правим зависимо на PK
  301.          - Пример: Онлайн магазин с данни и покупки Customers и Orders са свързани с PK, вместо всичко да е в една таблица
  302.  
  303.     - Third Normal Form (3NF):
  304.          - премахване на преходни зависимости
  305.          - Таблица служители пази id, служител, град, адрес => разделяме ги на 3 таблици и ги навързваме, без да е задължително по PK, може и по city_id вече employee е независимо
  306.        
  307.        - Boyce-Codd Normal Form (BCNF):
  308.          - По-строга версия на 3NF
  309.          - Тук правим да се навързват по PK
  310.  
  311.      - Fourth Normal Form (4NF):
  312.          - Ако данни от една таблица се използват в други две то това не е добре
  313.          - Пример: Имаме Курс X и Курс Y, на X Му трябват книгите A и B, на Y, A и C,
  314.            това, което правим е да направим таблица с книгите А и таблица с Книгите Б
  315.        
  316.         - Fifth Normal Form (5NF) - Project-Join Normal Form or PJ/NF:
  317.           - Кратко казано да не ни се налага да минаваме през таблици с данни, които не ни трябват, за да достигнем до таблица с данни, която ни трябва
  318.  
  319.   - Database Schema Design
  320.      - Създаването на различни ключове и връзки между таблиците
  321.  
  322.   - Minimizing Data Redundancy
  323.     - Чрез разбиването на таблици бихме имали отново намалено повтаряне на информация
  324.     - Имаме книга и копия, копията са в отделна таблица, и са линкнати към оригинала
  325.  
  326.   - Ensuring Data Integrity & Eliminating Data Anomalies
  327.     - Това ни помага да update-ваме и изтриваме данните навсякъде еднакво
  328.     - отново благодарение на някакви constraints можем да променим една стойност в една таблица и тя да се отрази във всички
  329.  
  330.   - Efficiency and Maintainability
  331.     - Благодарение на по-малките таблици, ги query–ваме и update-ваме по-бързо
  332.  
  333. 2. Релации в Django Модели
  334.   - Получават се използвайки ForeignKey полета
  335.   - related_name - можем да направим обартна връзка
  336.     - По дефолт тя е името + _set
  337.  
  338.   - Пример:
  339.   ```py
  340.   class Author(models.Model):
  341.       name = models.CharField(max_length=100)
  342.  
  343.   class Post(models.Model):
  344.       title = models.CharField(max_length=200)
  345.       content = models.TextField()
  346.       author = models.ForeignKey(Author, on_delete=models.CASCADE)
  347.   ```
  348.  
  349. - Access all posts written by an author
  350. ```py
  351. author = Author.objects.get(id=1)
  352. author_posts = author.post_set.all()
  353. ```
  354.  
  355. 3. Types of relationships
  356.   - Many-To-One (One-To-Many)
  357.   - Many-To-Many
  358.     - Няма значение, в кой модел се слага
  359.     - Django автоматично създава join таблица или още наричана junction
  360.     - Но, ако искаме и ние можем да си създадем:
  361.      ```py
  362.      class Author(models.Model):
  363.          name = models.CharField(max_length=100)
  364.      
  365.      class Book(models.Model):
  366.          title = models.CharField(max_length=200)
  367.          authors = models.ManyToManyField(Author, through='AuthorBook')
  368.      
  369.      class AuthorBook(models.Model):
  370.          author = models.ForeignKey(Author, on_delete=models.CASCADE)
  371.          book = models.ForeignKey(Book, on_delete=models.CASCADE)
  372.          publication_date = models.DateField()
  373.      ```
  374.  
  375.   - OneToOne, предимно се слага на PK
  376.   - Self-referential Foreign Key
  377.      - Пример имаме работници и те могат да са мениджъри на други работници
  378.        
  379.   ```py
  380.   class Employee(models.Model):
  381.       name = models.CharField(max_length=100)
  382.       supervisor = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
  383.   ```
  384.  
  385.    - Lazy Relationships - обекта от релацията се взима, чрез заявка, чак когато бъде повикан
  386.  
  387. ---
  388.  
  389. ### Models Inheritance and Customization
  390.  
  391. 1. Типове наследяване
  392.   - Multi-table
  393.     - Разширяваме модел с полетата от друг модел, като не копираме самите полета, а използваме създадения от django pointer, който прави One-To-One Relationship
  394.     - Пример:
  395.  
  396.     ```py
  397.     class Person(models.Model):
  398.         name = models.CharField(max_length=100)
  399.         date_of_birth = models.DateField()
  400.        
  401.         def is_student(self):
  402.             """Check if this person is also a student."""
  403.             return hasattr(self, 'student')
  404.    
  405.     class Student(Person):
  406.         student_id = models.CharField(max_length=15)
  407.         major = models.CharField(max_length=50)
  408.     ```
  409.  
  410.  
  411.   - Abstract Base Classes
  412.     - При това наследяване не се създават две нови таблици, а само една и тя е на наследяващия клас(Child), като абстрактния клас(Parent) е само шаблон
  413.     - Постигаме го чрез промяна на Meta класа:
  414.       ```py
  415.       class AbstractBaseModel(models.Model):
  416.           common_field1 = models.CharField(max_length=100)
  417.           common_field2 = models.DateField()
  418.    
  419.           def common_method(self):
  420.               return "This is a common method"
  421.    
  422.           class Meta:
  423.               abstract = True
  424.       ```
  425.  
  426.   - Proxy Models
  427.     - Използваме ги, за да добавим функционалност към модел, който не можем да достъпим
  428.     - Можем да добавяме методи, но не и нови полета
  429.     - Пример:
  430.  
  431.     ```py
  432.     class Article(models.Model):
  433.         title = models.CharField(max_length=200)
  434.         content = models.TextField()
  435.         published_date = models.DateField()
  436.    
  437.     class RecentArticle(Article):
  438.         class Meta:
  439.             proxy = True
  440.    
  441.         def is_new(self):
  442.             return self.published_date >= date.today() - timedelta(days=7)
  443.        
  444.         @classmethod
  445.         def get_recent_articles(cls):
  446.             return cls.objects.filter(published_date__gte=date.today() - timedelta(days=7))
  447.     ```
  448.  
  449. 2. Основни Built-In Методи
  450.   - `save()` - използва се за запазване на записи
  451.     ```py
  452.         def save(self, *args, **kwargs):
  453.             # Check the price and set the is_discounted field
  454.             if self.price < 5:
  455.                 self.is_discounted = True
  456.             else:
  457.                 self.is_discounted = False
  458.    
  459.             # Call the "real" save() method
  460.             super().save(*args, **kwargs)
  461.     ```
  462.   - `clean()` - използва се, когато искаме да валидираме логически няколко полета, например имаме тениска в 3 цвята, но ако е избран XXL цветовете са само 2.
  463.  
  464.  
  465. 3. Custom Model Properties
  466.   - Както и в ООП, можем чрез @property декоратора да правим нови атрибути, които в случая не се запазват в базата
  467.   - Използваме ги за динамични изчисления на стойностти
  468.  
  469. 4. Custom Model Fields
  470.   - Ползваме ги когато, Django няма field, които ни върши работа
  471.   - Имаме методи като:
  472.     - from_db_value - извиква се, когато искаме да взмем стойността от базата в пайтън
  473.     - to_python - извиква се когато правим десериализация или clean
  474.     - get_prep_value - обратното на from_db_value, от Python към базата, предимно ползваме за сериализации
  475.     - pre_save - използва се за last minute changes, точно преди да запазим резултата в базата
  476.  
  477.     ```py
  478.     class RGBColorField(models.TextField):
  479.         # Convert the database format "R,G,B" to a Python tuple (R, G, B)
  480.         def from_db_value(self, value, expression, connection):
  481.             if value is None:
  482.                 return value
  483.             return tuple(map(int, value.split(',')))
  484.    
  485.         # Convert any Python value to our desired format (tuple)
  486.         def to_python(self, value):
  487.             if isinstance(value, tuple) and len(value) == 3:
  488.                 return value
  489.             if isinstance(value, str):
  490.                 return tuple(map(int, value.split(',')))
  491.             raise ValidationError("Invalid RGB color format.")
  492.    
  493.         # Prepare the tuple format for database insertion
  494.         def get_prep_value(self, value):
  495.             # Convert tuple (R, G, B) to "R,G,B" for database storage
  496.             return ','.join(map(str, value))
  497.     ```
  498.  
  499. ---
  500.  
  501. ### Advanced Django Models Techniques
  502.  
  503.  
  504. 1. Validation in Models
  505.   - Built-in Validators
  506.     - MaxValueValidator, MinValueValidator - приема два аргумета (limit, message)
  507.     - MaxLengthValidator, MinLengthValidator - приема два аргумета (limit, message)
  508.     - RegexValidator - приема два аргумета (regex, message)
  509.     ```py
  510.     class SampleModel(models.Model):
  511.         name = models.CharField(
  512.             max_length=50,
  513.             validators=[MinLengthValidator(5)]  # Name should have a minimum length of 5 characters
  514.         )
  515.    
  516.         age = models.IntegerField(
  517.             validators=[MaxValueValidator(120)]  # Assuming age shouldn't exceed 120 years
  518.         )
  519.    
  520.         phone = models.CharField(
  521.             max_length=15,
  522.             validators=[
  523.             RegexValidator(
  524.                 regex=r'^\+?1?\d{9,15}$',
  525.                     message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed."
  526.         )]  # A simple regex for validating phone numbers
  527.         )
  528.     ```
  529.  - Custom Validators - функции, които често пишем в отделен файл. При грешка raise-ваме ValidationError
  530.  
  531.  
  532. 2. Meta Options and Meta Inheritance
  533.    - В мета класа можем да променяме:
  534.      - Името на таблицата
  535.      - Подреждането на данните
  536.      - Можем да задаваме constraints
  537.      - Можем да задаваме типа на класа(proxy, abstract)
  538.     ```py
  539.     class SampleModel(models.Model):
  540.         name = models.CharField(max_length=50)
  541.         age = models.IntegerField()
  542.         email = models.EmailField()
  543.    
  544.         class Meta:
  545.             # Database table name
  546.             db_table = 'custom_sample_model_table'
  547.    
  548.             # Default ordering (ascending by name)
  549.             ordering = ['name'] - Случва се на SELECT, не на INSERT
  550.    
  551.             # Unique constraint (unique combination of name and email)
  552.             unique_together = ['name', 'email']
  553.     ```
  554.     - Meта наследяване:
  555.       - Ако наследим абстрактен клас и не презапишем мета класа, то наслеяваме мета класа на абстрактния клас
  556.     ```py
  557.     class BaseModel(models.Model):
  558.         name = models.CharField(max_length=100)
  559.        
  560.         class Meta:
  561.             abstract = True
  562.             ordering = ['name']
  563.    
  564.     class ChildModel(BaseModel):
  565.         description = models.TextField()
  566.         # ChildModel inherits the Meta options
  567.     ```
  568.  
  569. 3. Indexing
  570.    - Индексирането ни помага, подреждайки елементите в определен ред или създавайки друга структура, чрез, която да търсим по-бързо.
  571.    - Бързо взимаме записи, но ги запазваме по-бавно
  572.    - В Django можем да сложим индекс на поле, като добавим key-word аргумента db_index=True
  573.    - Можем да направим и индекс, чрез мета класа, като можем да правим и композитен индекс
  574.     ```py
  575.     class Meta:
  576.     indexes=[
  577.     models.Index(fields=["title", "author"]),  # прави търсенето по два критерия, по-бързо
  578.     models.Index(fields=["publication_date"])
  579.     ]
  580.     ```
  581.  
  582.  
  583. 4. Django Model Mixins
  584.    - Както знаем, миксините са класове, които използваме, за да отделим обща функционалност
  585.  
  586.     ```py
  587.     class TimestampMixin(models.Model):
  588.         created_at = models.DateTimeField(auto_now_add=True)
  589.         updated_at = models.DateTimeField(auto_now=True)
  590.         class Meta:
  591.                 abstract = True
  592.     ```
  593.  
  594. ---
  595.  
  596. ### Advanced Django Queries
  597.  
  598. 1. Custom managers
  599.    - Използваме ги, за да изнсем бизнез логиката, за често използвани заявки на едно място
  600.    - Правим го наследявайки мениджъра по подразбиране.
  601.    
  602.     ```py
  603.         class BookManager(models.Manager):
  604.             def average_rating(self):
  605.                 # Calculate the average rating of all books
  606.                 return self.aggregate(avg_rating=models.Avg('rating'))['avg_rating']
  607.    
  608.             def highest_rated_book(self):
  609.                 # Get the highest-rated book
  610.                 return self.order_by('-rating').first()
  611.     ```
  612.  
  613. 2. Annotations and Aggregations
  614.    - Анотации - използваме ги, за да добавяме нови полета във върнатия резултат, често на база някакви изчисления. Връща QuerySet.
  615.    - Пример:
  616.    
  617.     ```py
  618.     # Annotating the queryset to get the count of books for each author
  619.     authors_with_book_count = Book.objects.values('author').annotate(book_count=Count('id'))
  620.     ```
  621.    - Агрегации - връщат едно поле(една стойност), често резултат от агрегиращи функции. Връща dict
  622.    
  623.     ```py
  624.     # Annotating the queryset to get the average rating of all books
  625.     average_rating = Book.objects.aggregate(avg_rating=Avg('rating'))
  626.     ```
  627.  
  628. 3. select_related & prefetch_related
  629.    - select_related - редуцира броя на заявките при One-To-One и Many-To-One заявки
  630.      - вместо lazily да взимаме свързаните обекти правим JOIN още при първата заявка
  631.      - Пример:
  632.  
  633.     ```py
  634.     from django.db import models
  635.    
  636.     class Author(models.Model):
  637.         name = models.CharField(max_length=100)
  638.    
  639.     class Book(models.Model):
  640.         title = models.CharField(max_length=100)
  641.         author = models.OneToOneField(Author, on_delete=models.CASCADE)
  642.    
  643.     books_with_authors = Book.objects.select_related('author')
  644.     # SELECT * FROM "myapp_book" JOIN "myapp_author" ON ("myapp_book"."author_id" = "myapp_author"."id")
  645.  
  646.     ```
  647.    
  648.    - prefetch_related - редуцира броя на заявките при Many-To-Many(не само) до броя на релациите + 1
  649.    - Пример:
  650.  
  651.     ```py
  652.     class Author(models.Model):
  653.         name = models.CharField(max_length=100)
  654.    
  655.     class Book(models.Model):
  656.         title = models.CharField(max_length=100)
  657.         authors = models.ManyToManyField(Author)
  658.    
  659.     authors_with_books = Author.objects.prefetch_related('book_set')
  660.    
  661.     # 1. SELECT * FROM "myapp_author"
  662.     # 2. SELECT * FROM "myapp_book" INNER JOIN "myapp_book_authors" ON ("myapp_book"."id" = "myapp_book_authors"."book_id")
  663.     ```
  664.  
  665. 4. Q and F
  666.  
  667.   - Използваме Q object, за да правим заявки изискващи по-сложни условия
  668.   - Пример:
  669.  
  670.     ```py
  671.     q = Q(title__icontains='Django') & (Q(pub_year__gt=2010) | Q(author='John Doe'))
  672.    
  673.     books = Book.objects.filter(q)
  674.    
  675.     ```
  676.  
  677.   - Използваме F object, за да достъпваме, стойностите през, които итерираме на ниво SQL
  678.  
  679.     ```py
  680.     from django.db.models import F
  681.    
  682.     Book.objects.update(rating=F('rating') + 1)
  683.     ```
  684.  
  685. ---
  686. ### SQL Alchemy
  687.  
  688. 1. SQL Alchemy - ORM - Object Relational Mapper
  689.    - ORM - абстракция позволяваща ни да пишем SQL, чрез Python
  690.    - Core - грижи се за транзакциите, изпращането на заявки, sessions и database pooling
  691.  
  692. 2. SetUp:
  693.    1. ```pip install sqlalchemy```
  694.    2. ```pip install psycopg2```
  695.  
  696. 3. Модели
  697.    - Подобно на Django наследяваме базов клас, `Base`, който взимаме като резултат от извикването на `declarative_base()`
  698.  
  699.     ```py
  700.  
  701.     from sqlalchemy.ext.declarative import declarative_base
  702.    
  703.     Base = declarative_base()
  704.    
  705.     class User(Base):
  706.         __tablename__ = 'users'
  707.         id = Column(Integer, primary_key=True)
  708.         name = Column(String)
  709.     ```
  710.  
  711. 4. Миграции
  712.    
  713.    4.1 SetUp:
  714.     - Не са включени в SQLAlchemy, за тях можем да използваме `Alembic`
  715.      
  716.     - ```pip install alembic```
  717.    
  718.     - ```alembic init alembic``` - създава ни файловата структура за миграциите<\br>
  719.        
  720.     - ```sqlalchemy.url = postgresql+psycopg2://username:password@localhost/db_name``` - във файла alembic.ini
  721.    
  722.     - ```py target_metadata = Base.metadata``` - във файла env.py, за да можем да поддържаме autogenerate
  723.    
  724.    4.2 Команди:
  725.     - ```alembic revision --autogenerate -m "Add User Table"``` - създава миграция със съобщени, както `makemigrations`
  726.        
  727.     - ```alembic upgrade head``` - прилага миграциите, както `migrate`
  728.        
  729.     - ```alembic downgrade -1``` - връща миграция
  730.  
  731. 6. CRUD
  732.    - Отваряме връзка с базата, пускайки нова сесия
  733.    - Винаги затваряме сесията, след приключване на работа
  734.    - Трябва да комитнем резултата, подобно на Django, където ползвахме `save()`
  735.  
  736.     ```py
  737.     from sqlalchemy import create_engine
  738.     from sqlalchemy.orm import sessionmaker
  739.    
  740.     engine = create_engine('sqlite:///example.db')
  741.     Session = sessionmaker(bind=engine)
  742.     session = Session()
  743.    
  744.     with Session() as session: # a good practice
  745.     ...
  746.     ```
  747.    
  748.    5.1 Add:
  749.       ```py
  750.          new_user = User(username='john_doe', email='john@example.com')
  751.          session.add(new_user)
  752.       ```
  753.  
  754.    5.2 Query
  755.       ```py
  756.     users = session.query(User).all()
  757.       ```
  758.  
  759.  
  760.    5.3 Update
  761.       ```py
  762.      
  763.     with engine.connect() as connection:
  764.         # Create an update object
  765.         upd = update(User).where(User.name == 'John').values(nickname='new_nickname')
  766.    
  767.         # Execute the update
  768.         connection.execute(upd)
  769.       ```
  770.  
  771.     or
  772.  
  773.       ```py
  774.     session.query(User).filter(User.name == 'John').update({"nickname": "new_nickname"}, synchronize_session=False)
  775.     session.commit()
  776.       ```
  777.  
  778.    5.4 Delete
  779.        ```py
  780.    
  781.         del_stmt = delete(User).where(User.name == 'John')
  782.        ```
  783.  
  784.  
  785. 8. Transactions
  786.     - `session.begin()`
  787.  
  788.     - `session.commit()`
  789.  
  790.     - `session.rollback()`
  791.  
  792.  
  793. 9. Relationships
  794.    1. Many to One
  795.       ```py
  796.          user_id = Column(Integer, ForeignKey('users.id'))
  797.          user = relationship('User')
  798.       ```
  799.    2. One to One
  800.      - `uselist=false`
  801.     ```py
  802.     class User(Base):
  803.         __tablename__ = 'users'
  804.         id = Column(Integer, primary_key=True)
  805.         profile = relationship("UserProfile", back_populates="user", uselist=False)
  806.    
  807.     class UserProfile(Base):
  808.         __tablename__ = 'profiles'
  809.         id = Column(Integer, primary_key=True)
  810.         user_id = Column(Integer, ForeignKey('users.id'))
  811.         user = relationship("User", back_populates="profile")  
  812.     ```
  813.  
  814.    3. Many to many
  815.      ```py
  816.  
  817.     user_group_association = Table('user_group', Base.metadata,
  818.         Column('user_id', Integer, ForeignKey('users.id')),
  819.         Column('group_id', Integer, ForeignKey('groups.id'))
  820.     )
  821.    
  822.     class Group(Base):
  823.         __tablename__ = 'groups'
  824.         id = Column(Integer, primary_key=True)
  825.         users = relationship("User", secondary=user_group_association, back_populates="groups")
  826.    
  827.     class User(Base):
  828.         __tablename__ = 'users'
  829.         id = Column(Integer, primary_key=True)
  830.         groups = relationship("Group", secondary=user_group_association, back_populates="users")
  831.  
  832.     ```
  833.  
  834.  
  835. 10. Database pooling
  836. ```py engine = create_engine(DATABASE_URL, pool_size=10, max_overflow=20)``` - задава първоначални връзки и максимално създадени
  837.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement