Django: доступ к первичному ключу в файле models.filefield(upload_to)

Я хочу сохранить свои файлы, используя первичный ключ записи.

Вот мой код:

def get_nzb_filename(instance, filename):
    if not instance.pk:
        instance.save() # Does not work.
    name_slug = re.sub('[^a-zA-Z0-9]', '-', instance.name).strip('-').lower()
    name_slug = re.sub('[-]+', '-', name_slug)
    return u'files/%s_%s.nzb' % (instance.pk, name_slug)

class File(models.Model):
    nzb = models.FileField(upload_to=get_nzb_filename)
    name = models.CharField(max_length=256)

Я знаю, что при первом сохранении объекта первичный ключ недоступен, поэтому я готов взять дополнительный удар, чтобы сохранить объект, чтобы получить первичный ключ, а затем продолжить.

Приведенный выше код не работает. Он выдает следующую ошибку:

maximum recursion depth exceeded while calling a Python object

Я предполагаю, что это бесконечный цикл. Вызов метода save вызовет метод get_nzb_filename, который снова вызовет метод save и т.д.

Я использую последнюю версию соединительной линии Django.

Как я могу получить первичный ключ, чтобы я мог использовать его для сохранения моих загруженных файлов?


Обновить @muhuk:

Мне нравится ваше решение. Можете ли вы помочь мне реализовать его? Я обновил свой код до следующего, и ошибка 'File' object has no attribute 'create'. Возможно, я использую то, что вы выписали из контекста?

def create_with_pk(self):
    instance = self.create()
    instance.save()
    return instance

def get_nzb_filename(instance, filename):
    if not instance.pk:
        create_with_pk(instance)
    name_slug = re.sub('[^a-zA-Z0-9]', '-', instance.name).strip('-').lower()
    name_slug = re.sub('[-]+', '-', name_slug)
    return u'files/%s_%s.nzb' % (instance.pk, name_slug)

class File(models.Model):
    nzb = models.FileField(upload_to=get_nzb_filename, blank=True, null=True)
    name = models.CharField(max_length=256)

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

Ответ 1

Кажется, вам нужно предварительно создать свои модели File с пустыми полями файлов. Затем выберите один и сохраните его с данным файлом.

У вас может быть такой метод пользовательского менеджера:

def create_with_pk(self):
    instance = self.create()
    instance.save()     # probably this line is unneeded
    return instance

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

ИЗМЕНИТЬ

create_with_pk должен быть метод пользовательского менеджера, в вашем коде это обычный метод. Следовательно, self не имеет смысла. Все это правильно документировано с примерами.

Ответ 2

Вы можете сделать это, установив upload_to во временное место и создав собственный способ сохранения.

Метод save должен сначала вызвать super, чтобы сгенерировать первичный ключ (это сохранит файл во временное местоположение). Затем вы можете переименовать файл с помощью первичного ключа и перенести его в нужное место. Позвоните супер еще раз, чтобы сохранить изменения, и вам хорошо идти! Это работало хорошо для меня, когда я столкнулся с этой точной проблемой.

Например:

class File( models.Model ):
    nzb = models.FileField( upload_to='temp' )

    def save( self, *args, **kwargs ):
        # Call save first, to create a primary key
        super( File, self ).save( *args, **kwargs )

        nzb = self.nzb
        if nzb:
            # Create new filename, using primary key and file extension
            oldfile = self.nzb.name
            dot = oldfile.rfind( '.' )
            newfile = str( self.pk ) + oldfile[dot:]

            # Create new file and remove old one
            if newfile != oldfile:
                self.nzb.storage.delete( newfile )
                self.nzb.storage.save( newfile, nzb )
                self.nzb.name = newfile 
                self.nzb.close()
                self.nzb.storage.delete( oldfile )

        # Save again to keep changes
        super( File, self ).save( *args, **kwargs )

Ответ 3

Контекст

Была та же проблема. Решил, что он присваивает id текущему объекту, сначала сохраняя объект.

Метод

  • создать пользовательскую функцию upload_to
  • определить, имеет ли объект pk
  • Если нет, сначала сохраните экземпляр, извлеките pk и назначьте его объекту
  • создайте свой путь с помощью

Пример рабочего кода:

class Image(models.Model):
    def upload_path(self, filename):
        if not self.pk:
            i = Image.objects.create()
            self.id = self.pk = i.id
        return "my/path/%s" % str(self.id)
    file = models.ImageField(upload_to=upload_path)

Ответ 4

Ty, есть ли причина, по которой вы загрузили свой собственный фильтр slugify?

Django поставляется со встроенным фильтром slugify, вы можете использовать его так:

from django.template.defaultfilters import slugify

slug = slugify(some_string)

Не уверен, что вы знали, что он доступен для использования...