Почему выдает ошибку в django model?
Выдает такую вот ошибку, в чем дело? RuntimeWarning: Accessing the database during app initialization is discouraged. To fix this warning, avoid executing queries in AppConfig.ready() or when your app modules are imported. warnings.warn(self.APPS_NOT_READY_WARNING_MSG, category=RuntimeWarning)
вот мой код в файле models.py:
from clickhouse_backend import models
import uuid
from main.parser import data
class Currency(models.ClickhouseModel):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
date = models.DateField()
unit = models.Int128Field()
rate = models.Float64Field()
class Meta:
engine = models.MergeTree(
order_by=["id"]
)
for el in data:
el_to_db = Currency(date = el['date'], unit=el['unit'], rate=el['rate'])
el_to_db.save()
также сам файл парсер:
from urllib.request import urlopen
from bs4 import BeautifulSoup
from pathlib import Path
from datetime import datetime
inner_html_code = str(urlopen('https://www.cbr.ru/currency_base/dynamics/?UniDbQuery.Posted=True&UniDbQuery.so=1&UniDbQuery.mode=1&UniDbQuery.date_req1=&UniDbQuery.date_req2=&UniDbQuery.VAL_NM_RQ=R01239&UniDbQuery.From=01.01.2025&UniDbQuery.To=20.12.2025').read(),'utf-8')
inner_soup = BeautifulSoup(inner_html_code, "html.parser")
inner_soup = inner_soup.find('div', {"class": 'table-wrapper'})
# очищаем код от выбранных элементов
def delete_div(code,tag,arg):
# находим все указанные теги с параметрами
for div in code.find_all(tag, arg):
# и удаляем их из кода
div.decompose()
delete_div(inner_soup, "div", {'class':'table-caption'})
delete_div(inner_soup, "td", {'colspan':'3'})
array = []
for tag in inner_soup.select('td'):
# добавляем адрес ссылки в нашу общую переменную
array.append(tag.get_text(strip=True))
array_str = str(array)
array_str = array_str.replace('[', '').replace(']', '').replace("'", "").replace(", ", " ").replace(",", ".")
file = open('array.txt', 'w')
file.write(array_str)
file.close()
def get_array_info(path):
file_with_array = open(path, 'r')
array_rows = file_with_array.readlines()
for array_rows in array_rows:
array_rows = array_rows.split(" ")
n=3
array_rows = [array_rows[i:i + n] for i in range(0, len(array_rows), n)]
rows_list = []
for row in array_rows:
row_dict = {}
row_dict['date'] = datetime.strptime(row[0], '%d.%m.%Y').date()
row_dict['unit'] = int(row[1])
row_dict['rate'] = float(row[2])
rows_list.append(row_dict)
file_with_array.close()
return rows_list
path_with_file = Path(r'C:\Users\Никита\Desktop\Django\Курс валют\venv\app\array.txt')
data = get_array_info(path_with_file)
Django во время инициализации, помимо прочего, импортирует модули models из приложений и регистрирует объявленные там модели. А во время импорта модуля выполняется весь код, который не завёрнут в функции - то есть он выполняется в тот момент, когда Django ещё не завершил свою инициализацию.
В строке el_to_db = Currency(...), которая не находится ни в какой функции, вы пытаетесь создавать объекты БД в тот момент, когда сам модуль models ещё продолжает инициализироваться - то есть пока сам Django ещё запускается. Так делать нельзя, работа с моделями должна выполняться строго после завершения инициализации Django.
Вообще, выполнять какую-либо работу вне функций - плохой тон. По-хорошему место начала работы должно быть строго одно - блок if __name__ == "__main__": в скрипте, который является точкой входа.
В Django такой точкой входа обычно является скрипт manage.py (или django-admin.py по вкусу), поэтому писать свою точку входа вам не нужно. Вместо этого стоит создать management-команду и уже в ней вызвыать функции, выполняющие нужную работу.
Для начала стоит перестать запускать парсер во время инициализации - уберите строку
data = get_array_info(path_with_file), она там ни к чему.Глобальную переменую
path_with_fileтакже стоит убрать - вместо неё стоит использовать аргумент командной строки, что позволит использовать разные файлы для парсинга без необходимости исправлять код.В файле
models.pyуберите блокfor el in data:, чтобы не пытаться использовать модели до завершения инициализации Django.Рядом с файлом
models.pyсоздайте каталогmanagement, в нём каталогcommands, а в нём два файла -management/commands/__init__.py(оставьте его пустым, он просто по традиции объявляет Python-пакет) иmanagement/commands/parse.py, гдеparseбудет названием команды.
Код файла parse.py может выглядеть примерно так (замените вашеприложение на название Django-приложения, в котором находятся модели):
from django.core.management.base import BaseCommand
from main.parser import get_array_info
from вашеприложение.models import Currency
class Command(BaseCommand):
help = 'Runs the parser'
def add_arguments(self, parser):
parser.add_argument('path_to_file', help='Path to file')
def handle(self, *args, **options):
# Django вызовет эту функцию в правильный момент после своей инициализации
path_to_file = options['path_to_file']
data = get_array_info(path_to_file) # Парсер запускается здесь
for el in data:
# Модели используются здесь
el_to_db = Currency(date = el['date'], unit=el['unit'], rate=el['rate'])
el_to_db.save()
Теперь можно запустить команду и указать путь к файлу:
python manage.py parse "C:\Users\Никита\Desktop\Django\Курс валют\venv\app\array.txt"
Django вызовет функцию add_arguments, в которой команда объявляет, что она готова принять один аргумент командной строки, а после полной инициализации вызовет функцию handle, в которой options['path_to_file'] вытаскивает аргумент по его названию, а дальше можно делать что угодно.
Подробнее в документации: https://docs.djangoproject.com/en/5.2/howto/custom-management-commands/
Весь код, связанный с urlopen, inner_soup и 'array.txt', тоже нужно завернуть в функцию и запускать через management-команду, которую можно назвать, например, download_and_save, а вместо 'array.txt' получать название сохраняемого файла из аргумента командной строки. Но чтобы не удлиннять и без того длинный ответ, я не стану это делать и оставлю в качестве домашнего задания автору вопроса.
Django во время инициализации, помимо прочего, импортирует модули models из приложений и регистрирует объявленные там модели. А во время импорта модуля выполняется весь код, который не завёрнут в функции - то есть он выполняется в тот момент, когда Django ещё не завершил свою инициализацию.
В строке el_to_db = Currency(...), которая не находится ни в какой функции, вы пытаетесь создавать объекты БД в тот момент, когда сам модуль models ещё продолжает инициализироваться - то есть пока сам Django ещё запускается. Так делать нельзя, работа с моделями должна выполняться строго после завершения инициализации Django.
Вообще, выполнять какую-либо работу вне функций - плохой тон. По-хорошему место начала работы должно быть строго одно - блок if __name__ == "__main__": в скрипте, который является точкой входа.
В Django такой точкой входа обычно является скрипт manage.py (или django-admin.py по вкусу), поэтому писать свою точку входа вам не нужно. Вместо этого стоит создать management-команду и уже в ней вызвыать функции, выполняющие нужную работу.
Для начала стоит перестать запускать парсер во время инициализации - уберите строку
data = get_array_info(path_with_file), она там ни к чему.Глобальную переменую
path_with_fileтакже стоит убрать - вместо неё стоит использовать аргумент командной строки, что позволит использовать разные файлы для парсинга без необходимости исправлять код.В файле
models.pyуберите блокfor el in data:, чтобы не пытаться использовать модели до завершения инициализации Django.Рядом с файлом
models.pyсоздайте каталогmanagement, в нём каталогcommands, а в нём два файла -management/commands/__init__.py(оставьте его пустым, он просто по традиции объявляет Python-пакет) иmanagement/commands/parse.py, гдеparseбудет названием команды.
Код файла parse.py может выглядеть примерно так (замените вашеприложение на название Django-приложения, в котором находятся модели):
from django.core.management.base import BaseCommand
from main.parser import get_array_info
from вашеприложение.models import Currency
class Command(BaseCommand):
help = 'Runs the parser'
def add_arguments(self, parser):
parser.add_argument('path_to_file', help='Path to file')
def handle(self, *args, **options):
# Django вызовет эту функцию в правильный момент после своей инициализации
path_to_file = options['path_to_file']
data = get_array_info(path_to_file) # Парсер запускается здесь
for el in data:
# Модели используются здесь
el_to_db = Currency(date = el['date'], unit=el['unit'], rate=el['rate'])
el_to_db.save()
Теперь можно запустить команду и указать путь к файлу:
python manage.py parse "C:\Users\Никита\Desktop\Django\Курс валют\venv\app\array.txt"
Django вызовет функцию add_arguments, в которой команда объявляет, что она готова принять один аргумент командной строки, а после полной инициализации вызовет функцию handle, в которой options['path_to_file'] вытаскивает аргумент по его названию, а дальше можно делать что угодно.
Подробнее в документации: https://docs.djangoproject.com/en/5.2/howto/custom-management-commands/
Весь код, связанный с urlopen, inner_soup и 'array.txt', тоже нужно завернуть в функцию и запускать через management-команду, которую можно назвать, например, download_and_save, а вместо 'array.txt' получать название сохраняемого файла из аргумента командной строки. Но чтобы не удлиннять и без того длинный ответ, я не стану это делать и оставлю в качестве домашнего задания автору вопроса.