Как загрузить данные только в том случае, если они не существуют в БД с помощью приспособлений Django
У меня есть некоторые начальные данные для загрузки с помощью команды ./manage.py loaddata. Она работает правильно, проблема в том, что когда я вызываю функцию loaddata, я хочу загрузить эти данные, если их там еще нет, если данные уже загружены, то я хочу пропустить этот шаг
my_app/models.py
class Person(models.Model):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
Решение 1
Поместите атрибут pk
в ваш файл приспособлений. Таким образом, независимо от того, сколько раз вы вызовете loaddata
, он всегда будет просто записывать в один и тот же набор записей, нацеленных на указанные первичные ключи.
my_app/fixtures.json
[
{
"model": "my_app.Person",
"pk": 1,
"fields": {
"first_name": "John",
"last_name": "Lennon"
}
},
{
"model": "my_app.Person",
"pk": 2,
"fields": {
"first_name": "Paul",
"last_name": "McCartney"
}
}
]
Решение 2
Обернитесь вокруг реализации команды loaddata
.
- Прочитайте исходный файл JSON
- Отфильтруйте элементы, которые уже существуют в вашей базе данных
- Запишите отфильтрованные элементы в новый (временный) JSON файл
- Продолжайте использовать исходную функциональность для
loaddata
- Опционально, удалите временно созданный JSON файл
my_app/fixtures.json
[
{
"model": "my_app.Person",
"fields": {
"first_name": "John",
"last_name": "Lennon"
}
},
{
"model": "my_app.Person",
"fields": {
"first_name": "Paul",
"last_name": "McCartney"
}
}
]
my_app/management/commands/loaddata.py
import json
import os
from django.core.management.commands import loaddata
from my_app.models import Person
def should_add_record(record):
if record['model'] != 'my_app.Person':
return True
return not Person.objects.filter(
first_name=record['fields']['first_name'],
last_name=record['fields']['last_name'],
).exists()
class Command(loaddata.Command):
def handle(self, *args, **options):
args = list(args)
# Read the original JSON file
file_name = args[0]
with open(file_name) as json_file:
json_list = json.load(json_file)
# Filter out records that already exists
json_list_filtered = list(filter(should_add_record, json_list))
if not json_list_filtered:
print("All data are already previously loaded")
return
# Write the updated JSON file
file_dir_and_name, file_ext = os.path.splitext(file_name)
file_name_temp = f"{file_dir_and_name}_temp{file_ext}"
with open(file_name_temp, 'w') as json_file_temp:
json.dump(json_list_filtered, json_file_temp)
# Pass the request to the actual loaddata (parent functionality)
args[0] = file_name_temp
super().handle(*args, **options)
# You can choose to not delete the file so that you can see what was added to your records
os.remove(file_name_temp)
Выход
Пустая база данных
>>> Person.objects.all()
<QuerySet []>
Триггер loaddata
$ python manage.py loaddata my_app/fixtures.json
Installed 2 object(s) from 1 fixture(s)
Обновленная база данных
>>> Person.objects.all()
<QuerySet [<Person: Person object (1)>, <Person: Person object (2)>]>
>>> Person.objects.values()
<QuerySet [{'id': 1, 'first_name': 'John', 'last_name': 'Lennon'}, {'id': 2, 'first_name': 'Paul', 'last_name': 'McCartney'}]>
Повторное выполнение loaddata
$ python manage.py loaddata my_app/fixtures.json
All data are already previously loaded
- Явная печать будет показана только при использовании решения 2, в то время как обычная печать по умолчанию будет видна при использовании решения 1. В любом случае, оба решения не добавят никаких новых записей в базу данных.
Записи в базе данных остаются как есть
>>> Person.objects.all()
<QuerySet [<Person: Person object (1)>, <Person: Person object (2)>]>
>>> Person.objects.values()
<QuerySet [{'id': 1, 'first_name': 'John', 'last_name': 'Lennon'}, {'id': 2, 'first_name': 'Paul', 'last_name': 'McCartney'}]>
Ссылка: