Django Admin - загрузка нескольких изображений в несколько объектов модели за одну загрузку - PullRequest
4 голосов
/ 18 июня 2019

У меня есть модель, у которой в базе данных есть записи, близкие к 150K.
Сегодня можно загружать изображения для каждой записи отдельно при редактировании этой конкретной записи.

У нас естьнеобходимо загрузить около 4k файлов изображений, по одному для каждой записи, и я хотел бы добиться этого, разрешив загрузку нескольких файлов, где имя файла изображения будет соответствовать атрибуту имени модели.

Пример базовой модели:

class Entry(models.Model):
    name = models.CharField(unique=True, max_length=200, db_index=True)
    image = models.ImageField(null=True, blank=True)

Есть ли способ создать представление, которое будет поддерживать одну кнопку загрузки для нескольких файлов (где каждый файл представляет отдельную запись модели в БД) и обработать все остальное в обработчике загрузки / хранилище?

1 Ответ

2 голосов
/ 28 июня 2019

Обновлять тысячи записей одновременно сложно по умолчанию.

Одной из проблем является производительность на стороне базы данных. лучшее, что я мог придумать, используя update и переписывая некоторые методы. Предположим, у вас уже есть изображения в некотором каталоге.

from django.db.models import fields
from django.db.models import F
from django.db.models.expressions import Value , CombinedExpression
from django.db.models import QuerySet

class TextValue(Value):
    def as_sql(self, compiler, connection):
        connection.ops.check_expression_support(self)
        return '%s', [self.value]

class Expr(F):
    ADD = '||'  # standard concat row value + value in PostgreSQL

    #overwrite method to support text
    def _combine(self, other, connector, reversed):
        if not hasattr(other, 'resolve_expression'):
            other = TextValue(other, output_field=fields.CharField())

        return CombinedExpression(self, connector, other)


class Entry(models.Model):
    name = models.CharField(unique=True, max_length=200, db_index=True)
    image = models.ImageField(upload_to= 'media/' , null=True, blank=True , default='default.png')

теперь имея этот код, вы можете массово обновить

entries= Entry.objects.all() 
entries.update(**{'image': Expr('name') + '.png'})

лучшая часть того, чтобы делать это, как производительность. это единственный запрос, который выполняется

{'sql': 'UPDATE "entry" SET "image" = ("entry"."name" || \'.png\')', 'time': '0.024'}]

обновление

делает то же самое, что и администратор, и сохраняет один экземпляр модели для каждой записи.

from django.contrib import  admin
from django import forms


class EntryForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(EntryForm, self).__init__(*args, **kwargs)
        self.fields['image'].widget.attrs.update(
            {'multiple': True, 'accept': 'image/jpg,image/png,image/gif', })


class EntryAdmin(admin.ModelAdmin):
    form = EntryForm

    def save_model(self, request, obj, form, change):
        files = request.FILES.getlist('image')

        # if image exist
        if files:
            for image_field in files:
                try:
                    instance = Entry.objects.get(name=image_field.name[:-4])
                    instance.image = image_field
                    instance.save()
                except Entry.DoesNotExist:
                    pass
        else:
            return super().save_model(request, obj, form, change)



admin.site.register(Entry , EntryAdmin)

вы можете смешать эти две части, чтобы получить производительность теста

...