Если вы когда-либо использовали Django, вы, вероятно, использовали его команды manage.py для выполнения различных действий, например:
python manage.py runserver
python manage.py migrate
python manage.py createsuperuser
Существует множество полезных встроенных команд, которые вы можете использовать, если посмотрите в документации (django-admin и manage.py). Например:
- dumpdata — экспортировать данные из приложения в JSON или другой формат
- loaddata — импортировать данные в базу данных
- migrate — синхронизировать базу данных с текущим набором моделей и миграций.
Вы также можете создать свою собственную команду для административных задач, которые у вас могут возникнуть. Например, если вам нужно провести плановое обслуживание, вы можете создать такую команду и запустить ее как запланированное задание (например, через celery).
В этой статье я покажу вам, как вы можете создать свою собственную команду manage.py для заполнения данных из API-запроса в вашу базу данных.
Полный исходный код этого примера приложения Django можно найти на GitHub
Сценарий
Допустим, мы по какой-то причине хотим заполнить имеющуюся у нас модель базы данных данными о пиве из API. Если вы интересуетесь пивом, вы можете вспомнить, что у Brewdog была серия сортов пива под названием «Hello, my name is…».
Мы будем получать информацию об этих сортах пива из бесплатного API на сайте https://punkapi.com.
Предварительный просмотр данных о пиве
Модель Django
В приложении Django постройте модель для данных:
# models.py
from django.db import models
class Beer(models.Model):
name = models.CharField(max_length=100)
tagline = models.CharField(max_length=200)
first_brewed = models.CharField(max_length=20)
description = models.TextField()
added = models.DateTimeField(auto_now_add=True)
edited = models.DateTimeField(auto_now=True)
def __str__(self):
return f'Beer: {self.name}'
Перенесите модель в базу данных.
$ python manage.py makemigrations beers
$ python manage.py migrate
Создание пользовательской команды
Для добавления новой команды создайте структуру папок в папке apps в проекте.
Цитата из документации Django
Для этого добавьте каталог management/commands в приложение. Django зарегистрирует команду manage.py для каждого модуля Python в этом каталоге, имя которого не начинается с символа подчеркивания.
Поэтому в данном случае создайте update_beers.py
в следующей папке:
beers/
__init__.py
models.py
management/
__init__.py
commands/
update_beers.py
Код команды
Код команды должен включать класс Command, который является подклассом Djangos BaseCommand. BaseCommand имеет один метод, который должен быть реализован, и это метод handle
. Он будет содержать логику нашей команды.
Основная структура необходимого кода показана ниже.
from django.core.management import BaseCommand
from beers.models import Beer
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Update Beer table from punkapi.com"
def handle(self, *args, **options):
# Implement the logic here
pass
В данном случае мы хотим делать запросы к API для получения данных, которые затем будут записаны в базу данных. Мы также будем использовать библиотеку requests.
Полный код команды может выглядеть следующим образом. (Это просто для демонстрации. В реальном мире мы, вероятно, не будем использовать название пива для запроса, если оно уже существует в базе данных).
from django.core.management import BaseCommand
import requests
from beers.models import Beer
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super(Command, self).__init__(*args, **kwargs)
help = "Update Beer table from punkapi.com"
def handle(self, *args, **options):
# Request data from the beer API
response = requests.get('https://api.punkapi.com/v2/beers/?beer_name=my_name_is')
# Loop through the response
for beer in response.json():
try:
beer_row = Beer.objects.get(name=beer['name'])
except Beer.DoesNotExist:
beer_row = None
if beer_row:
# Beer already exists - do nothing
self.stdout.write(f'{beer_row.name} already exists.')
continue
else:
# Add beer to db
self.stdout.write('Create new row')
beer_row = Beer()
beer_row.name = beer['name']
beer_row.tagline = beer['tagline']
beer_row.first_brewed = beer['first_brewed']
beer_row.description = beer['description']
beer_row.save()
self.stdout.write('#########################')
self.stdout.write('Updated Beer list')
self.stdout.write('#########################')
Здесь мы проверяем, существует ли пиво в базе данных. Если нет, мы добавляем его в базу данных.
Выполнение пользовательской команды
Выполните пользовательскую команду, запустив ее:
python manage.py update_beers
В результате будет показан результат:
Результат выполнения команды
Если все прошло успешно, API будет запрошен для получения данных о пиве, и данные будут записаны в базу данных.
Другой способ сделать это
Что если мы хотим получать из api любые обновления для существующего пива?
Перепишите метод handle следующим образом.
def handle(self, *args, **options):
response = requests.get('https://api.punkapi.com/v2/beers/?beer_name=my_name_is')
for beer in response.json():
object, created = Beer.objects.update_or_create(
name=beer['name'],
defaults={
'tagline': beer['tagline'],
'first_brewed': beer['first_brewed'],
'description': beer['description']
}
)
object.save()
if created:
self.stdout.write('New beer added')
else:
self.stdout.write(f'{beer["name"]} updated')
Здесь мы используем метод Djangos update_or_create
, который возвращает кортеж, где object — это созданный или обновленный объект, а created — булево число, указывающее, был ли создан новый объект.
Заключение
В этой статье я показал, как вы можете создать свою собственную пользовательскую команду manage.py в Django. В статье показано, как можно создать пользовательскую команду для получения данных из API через библиотеку requests
и хранения их в базе данных.
Если вы хотите настроить пользовательскую команду как запланированную задачу, посмотрите на последний ресурс по ссылке ниже.
Ресурсы
- Написание пользовательских команд django-admin (документация Django)
- Полный исходный код этого приложения Beer на GitHub
- Обработка периодических задач в Django с помощью Celery и Docker