Преобразование объекта Queryset в экземпляр модели

avatar
Tristan Tran
1 июля 2021 в 20:16
155
1
1

У меня есть две следующие модели:

class Genre(models.Model):
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name


class Book(models.Model):
    genre= models.ForeignKey(Genre, related_name="book",  on_delete=models.CASCADE)
    name = models.CharField(max_length=20)

    def __str__(self):
        return self.name
def save_book(book_list):
    #book_list is a list of dict: [{'name': 'xxx', 'genre': 'xxx'}, {...}]
    for book in book_list:
        book_genre = book['genre']
        genre= Genre.objects.get(name=book_genre )
        book = {'name': book['name'],
                'genre': genre}
        new_book = Book(**book)
        new_book.save()

В приведенном выше коде вместо обращения к базе данных с каждым Genre.objects.get(name=book_genre ) я хочу использовать Genre.objects.all(), чтобы получить их все сразу. Однако этот код:

def save_book(book_list):
    #book_list is a list of dict: [{'name': 'xxx', 'genre': 'xxx'}, {...}]
    genres= Genre.objects.all()
    for book in book_list:
        book_genre = book['genre']
        genre= genres.filter(name=book_genre)
        book = {'name': book['name'],
                'genre': genre}
        new_book = Book(**book)
        new_book.save()

выдает ошибку

ValueError: Cannot assign "<QuerySet [Genre:]>": "Book.genre" must be a "Genre" instance.

Как мне преобразовать член набора запросов в экземпляр модели для назначения по внешнему ключу, как указано выше?

Источник
Willem Van Onsem
1 июля 2021 в 20:18
0

Поскольку вы используете .filter(..), они все равно будут делать запросы к базе данных: по одному на каждый .filter(..).

Tristan Tran
1 июля 2021 в 20:19
0

Я понимаю. Как в этом примере сохранить набор запросов в памяти и избежать многократного обращения к базе данных?

Ответы (1)

avatar
Willem Van Onsem
1 июля 2021 в 20:22
2

Поскольку вы используете .filter(…) [Django-doc], они по-прежнему будут делать запросы к базе данных: по одному для каждого <678695854145196>.

Что вы можете сделать, так это создать словарь, который отображает имена на Genres:

def save_book(book_list):
    #book_list is a list of dict: [{'name': 'xxx', 'genre': 'xxx'}, {...}]
    genre_names = [b['genre'] for b in book_list]
    genres= {g.name: g for g in Genre.objects.filter(name__in=genre_names) }
    books = [
        Book(name=book['name'], genre=genres[book['genre']])
        for book in book_list
    ]
    Book.objects.bulk_create(books)

Таким образом, мы сначала создаем словарь для всех нужных нам жанров, а затем создаем экземпляры Book. Мы используем .bulk_create(…) [Django-doc] для создания записей в одном запросе.