-
Notifications
You must be signed in to change notification settings - Fork 0
Kharin b #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Kharin b #93
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,131 @@ | ||
| # Написать класс-обертку над SQLite (с возможностями менеджера контекста), | ||
| # которая может на вход принимать строки SQL запросов и возвращать данные в формате json. | ||
| # Класс должен иметь, как минимум, методы select и execute. | ||
| import sqlite3 | ||
|
|
||
|
|
||
| class MySQLite: | ||
|
|
||
| def __init__(self, path): | ||
| self._path = path | ||
| self._name_table = None | ||
|
|
||
| # Методы my_execute_values, enter, exit и все методы связанные с ними были написаны мной до того как я задал Вам вопрос. | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enter и exit - нужны, т.к. в задании требовался менеджер контекста. |
||
| # Мне было просто очень жаль удалять их, поэтому я их оставил... | ||
| # Тем более, что до этого написал неправильный, но рабочий код на 100+ строк и удалил :( | ||
| # Можете, пожалуйста, глянуть эти методы и сказать что с ними не так? Они не избыточны? | ||
| # Стоит избегать таких длинных методов в практике? | ||
|
|
||
| def my_execute_values(self): | ||
| self._name_table = self._choice_tabl() | ||
| if not self._name_table: | ||
| print("В базе данных не существует таблиц. Невозможно установить значения.") | ||
| return None | ||
| columns = self._cur.execute(f"SELECT * FROM {self._name_table}").description | ||
| columns = tuple([i[0] for i in columns]) | ||
| print(f"В таблице {self._name_table} содержатся столбцы: {columns}") | ||
| values = [] | ||
| for i in columns: | ||
| value = input(f"Введите значение для столбца {i}") | ||
| values.append(value) | ||
| values = tuple(values) | ||
| self._database.execute(f"INSERT INTO {self._name_table}{str(columns)}VALUES {str(values)}") | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Код уязвим для SQL-инъекций. |
||
|
|
||
| def _choice_tabl(self): | ||
| # скрипт выбирает таблицу по умолчанию, для работы с ней внутри my_execute_values() | ||
| name_table = self._cur.execute("SELECT name from sqlite_master where type= 'table'").fetchall() | ||
| len_list_name = len(name_table) | ||
| match len_list_name: | ||
| case 0: | ||
| return None | ||
| case 1: | ||
| return name_table[0][0] | ||
| case _: | ||
| while True: | ||
| name = input(f"В базе данных более одной таблицы. Список таблиц: {name_table}\n" | ||
| f"Выберете таблицу с которой будут происходить дальнейшие операции: ") | ||
| if name in name_table: | ||
| return name | ||
| else: | ||
| print("Выбор непонятен") | ||
|
|
||
| def my_execute(self, s): | ||
| if not self.proverka(s, ["INSERT", "UPDATE", "DELETE"]): | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Здесь и в стр.60: под неизменяемый набор команд, к которому мы применяем операцию in лучше другой тип данных использовать, не список! |
||
| print("Неверная команда. Метод my_execute не возвращает значение") | ||
| return None | ||
| self._cur.execute(s) | ||
| self._database.commit() | ||
|
|
||
| def my_select(self, s, mod=None): | ||
| if not self.proverka(s, ["SELECT"]): | ||
| print("Неверная команда. Метод my_select возвращает значение и не сохраняет изменения в базе данных") | ||
| return None | ||
| res = self._cur.execute(s) | ||
| if mod: | ||
| res = self._mod(mod, res) | ||
| if type(res) is sqlite3.Cursor: | ||
| res = res.description | ||
| return res | ||
|
|
||
| @staticmethod | ||
| def _mod(mod, res): | ||
| match mod: | ||
| case "fa": | ||
| res = res.fetchall() | ||
| case "fo": | ||
| res = res.fetchone() | ||
| case "fm": | ||
| while True: | ||
| size = input("Введите максимальный размер выводимых данных, " | ||
| "или нажмите Enter для значения по умолчанию(1): ") | ||
| if size.isdecimal(): | ||
| size = int(size) | ||
| break | ||
| elif size == "": | ||
| size = 1 | ||
| break | ||
| else: | ||
| continue | ||
| res = res.fetchmany(size) | ||
| case _: | ||
| print("Неверно указан модификатор") | ||
| return None | ||
| return res | ||
|
|
||
| @staticmethod | ||
| def proverka(com, lst_com): | ||
| for i in lst_com: | ||
| if i in com: | ||
| break | ||
| else: | ||
| return False | ||
| return True | ||
|
|
||
| def __enter__(self): | ||
| self._database = sqlite3.connect(self._path) | ||
| self._cur = self._database.cursor() | ||
|
|
||
| def __exit__(self, exc_type, exc_val, exc_tb): | ||
| while True: | ||
| reply = input("Сохранить базу данных?(y/n)") | ||
| match reply: | ||
| case "y": | ||
| self._database.commit() | ||
| break | ||
| case "n": | ||
| break | ||
| case _: | ||
| print("Выбор непонятен") | ||
| self._database.close() | ||
|
|
||
|
|
||
| base = MySQLite("MyDataBase.db") | ||
| with base: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Стоит объединить строки 122 и 123 (with MySQLite("MyDataBase.db") as base:), чтоб кто-нибудь не рискнул по случайности, вставить между ними код, переопределяющий переменную base для каких-то своих целей. |
||
| base.my_execute("SELECT * FROM l")# Не выполнится | ||
| print(base.my_select("SELECT name from sqlite_master where type= 'table'", mod="fa")) | ||
| tabl_name = base.my_select("SELECT name from sqlite_master where type= 'table'", mod="fo") | ||
| print(base.my_select("SELECT name from sqlite_master where type= 'table'", mod="fm")) | ||
| if tabl_name: | ||
| print(base.my_select(f"SELECT * FROM {tabl_name[0]}")) | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| # Написать скрипт, работающий под SQLite/MySQL/PostgreSQL, который создает 3 сущности: | ||
| # производители, покупатели, товары. Необходимо добавить демо-данные и выполнить следующие выборки: | ||
| # вывести все товары с указанием информации о производителе | ||
| # найти все товары, которые никто не покупал | ||
| import sqlite3 | ||
| import random | ||
| import os | ||
|
|
||
|
|
||
| class MyDB: | ||
|
|
||
| def __init__(self, path): | ||
| self._path = path | ||
| self._company = ["Кштымский Автозавод", "Autodesk", "HP", "Xaomi"] | ||
| self._name = ["Леопольд", "Матильда", "Иван", "Элеонора"] | ||
| self._title = [("Подшипники", "Пятое колесо"), ("3D max", "AutoCAD"), ("Принтер", "Ноутбук"), | ||
| ("Смартфон", "Самокат")] | ||
| self._city = ["Кштымск", "Сан_Рафаэль", "Пало-Альто", "Пекин"] | ||
|
|
||
| def my_select(self, s): | ||
| return self._cur.execute(s).fetchall() | ||
|
|
||
| def company_product(self): | ||
| self._cur.execute("SELECT product.id, title, manufacturer.company, manufacturer.city FROM product " | ||
| "JOIN manufacturer ON manufacturer.company=product.company") | ||
| a = self._cur.fetchone() | ||
| while a: | ||
| print(a) | ||
| a = self._cur.fetchone() | ||
|
|
||
| def not_bought(self): | ||
| res = self._cur.execute("select title from product except select product from buyer").fetchall() | ||
| return res | ||
|
|
||
| def __enter__(self): | ||
| self._database = sqlite3.connect(self._path) | ||
| self._cur = self._database.cursor() | ||
| self._create_db() | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Код, изменяющий структуру БД (DDL) всё-таки стоит держать отдельно от DML. И уж точно не стоит его вызывать безусловно. |
||
|
|
||
| def _create_db(self): | ||
| self._database.execute("CREATE TABLE manufacturer" | ||
| " (id INT PRIMARY KEY NOT NULL," | ||
| " company TEXT NOT NULL," | ||
| " city TEXT );") | ||
| self._database.execute("CREATE TABLE buyer" | ||
| " (id INT PRIMARY KEY NOT NULL," | ||
| " product TEXT NOT NULL," | ||
| " name TEXT NOT NULL);") | ||
| self._database.execute("CREATE TABLE product" | ||
| " (id INT PRIMARY KEY NOT NULL," | ||
| " title TEXT NOT NULL," | ||
| " company TEXT NOT NULL);") | ||
| self._data_choice() | ||
|
|
||
| def _data_choice(self): | ||
| id = 1 | ||
| for id_com in range(len(self._company)): | ||
| value = str(id_com+1), self._company[id_com], self._city[id_com] | ||
| self._database.execute(f"INSERT INTO manufacturer (id, company, city)VALUES( ?, ?, ?);", value) | ||
| for product_n in range(2): | ||
| value = str(id), self._title[id_com][product_n], self._company[id_com] | ||
| self._database.execute(f"INSERT INTO product (id, title, company)""VALUES (?, ?, ?);", value) | ||
| id += 1 | ||
| for sales in range(1, random.randint(3,8)): | ||
| product = random.choice(self._title)[random.choice((0, 1))] | ||
| name = random.choice(self._name) | ||
| value = str(sales), product, name | ||
| self._database.execute("INSERT INTO buyer (id, product, name)VALUES (?, ?, ?);", value) | ||
|
|
||
| def __exit__(self, exc_type, exc_val, exc_tb): | ||
| while True: | ||
| reply = input("Сохранить базу данных?(y/n)") | ||
| match reply: | ||
| case "y": | ||
| self._database.commit() | ||
| self._database.close() | ||
| break | ||
| case "n": | ||
| self._database.close() | ||
| os.remove(self._path) | ||
| break | ||
| case _: | ||
| print("Выбор непонятен") | ||
|
|
||
|
|
||
|
|
||
|
|
||
| base = MyDB("MyData.db") | ||
| with base: | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Строки 88 и 89 стоит объединить. |
||
| print("Сводная таблица продуктов и информации о производителе:") | ||
| base.company_product() | ||
| print("=================================================") | ||
| print(f"Товары небыли куплены: {base.not_bought()}") | ||
| pass | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pass лишний |
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enter и exit - нужны, т.к. в задании требовался менеджер контекста.
Что касается my_execute_values, choice_tabl (да и exit в какой-то мере) - мне в них категорически не нравится ввод данных. Только представьте, как писать автотесты к таким методам!
Ввод данных в подавляющем большинстве случаев стоит отделять от обработки этих данных. Это разные задачи. Можно запрашивать данные в основном коде, в отдельных функциях и затем передавать в методы класса в качестве параметров.