Django: доступ к первичному ключу в расположении models.filefield (upload_to) - PullRequest
10 голосов
/ 16 марта 2009

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

Вот мой код:

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. Нет проблем.

Ответы [ 4 ]

3 голосов
/ 16 мая 2013

Вы можете сделать это, установив 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 )
2 голосов
/ 28 декабря 2011

Контекст

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

Метод

  1. создать пользовательскую функцию upload_to
  2. определить, есть ли у объекта pk
  3. если нет, сначала сохраните экземпляр, получите pk и назначьте его объекту
  4. создать свой путь с этим

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

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)
2 голосов
/ 16 марта 2009

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

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

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

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

EDIT

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

0 голосов
/ 23 марта 2011

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

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

from django.template.defaultfilters import slugify

slug = slugify(some_string)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...