Skip to main content
 首页 » 编程设计

python之从 Django shell 中将照片上传到 Photologue 的自定义命令

2024年10月01日31虾米姐

我已成功使用 Photologue 来展示 galleries of regularly-created data plot images .当然,既然已经建立了能力,就会创建大量的数据图,并且需要共享它们!

下一步是使用 Django shell 中的 manage.py 编写上传图像并将它们添加到图库的过程脚本;然而,作为 Django 的业余爱好者,我遇到了一些困难。

这是我目前开发的自定义命令addphoto.py:

from django.core.management.base import BaseCommand, CommandError 
from django.utils import timezone 
from photologue.models import Photo, Gallery 
 
import os 
from datetime import datetime 
import pytz 
 
class Command(BaseCommand): 
 
    help = 'Adds a photo to Photologue.' 
 
    def add_arguments(self, parser): 
        parser.add_argument('imagefile', type=str) 
        parser.add_argument('--title', type=str) 
        parser.add_argument('--date_added', type=str, help="datetime string in 'YYYY-mm-dd HH:MM:SS' format [UTC]") 
        parser.add_argument('--gallery', type=str) 
 
    def handle(self, *args, **options): 
 
        imagefile = options['imagefile'] 
 
        if options['title']: 
            title = options['title'] 
        else: 
            base = os.path.basename(imagefile) 
            title = os.path.splitext(base)[0] 
        if options['date_added']: 
            date_added = datetime.strptime(options['date_added'],'%Y-%m-%d %H:%M:%S').replace(tzinfo=pytz.UTC) 
        else: 
            date_added = timezone.now() 
 
        p = Photo(image=imagefile, title=title, date_added=date_added) 
        p.save() 

不幸的是,当使用 --traceback 执行时,会产生以下结果:

./manage.py addphoto '../dataplots/monitoring/test.png' --traceback 
Failed to read EXIF DateTimeOriginal 
Traceback (most recent call last): 
  File "/home/user/mysite/photologue/models.py", line 494, in save 
    exif_date = self.EXIF(self.image.file).get('EXIF DateTimeOriginal', None) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/db/models/fields/files.py", line 51, in _get_file 
    self._file = self.storage.open(self.name, 'rb') 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/files/storage.py", line 38, in open 
    return self._open(name, mode) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/files/storage.py", line 300, in _open 
    return File(open(self.path(name), mode)) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/files/storage.py", line 405, in path 
    return safe_join(self.location, name) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/utils/_os.py", line 78, in safe_join 
    'component ({})'.format(final_path, base_path)) 
django.core.exceptions.SuspiciousFileOperation: The joined path (/home/user/mysite/dataplots/monitoring/test.png) is located outside of the base path component (/home/user/mysite/media) 
Traceback (most recent call last): 
  File "./manage.py", line 22, in <module> 
    execute_from_command_line(sys.argv) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 363, in execute_from_command_line 
    utility.execute() 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/management/__init__.py", line 355, in execute 
    self.fetch_command(subcommand).run_from_argv(self.argv) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/management/base.py", line 283, in run_from_argv 
    self.execute(*args, **cmd_options) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/management/base.py", line 330, in execute 
    output = self.handle(*args, **options) 
  File "/home/user/mysite/photologue/management/commands/addphoto.py", line 36, in handle 
    p.save() 
  File "/home/user/mysite/photologue/models.py", line 553, in save 
    super(Photo, self).save(*args, **kwargs) 
  File "/home/user/mysite/photologue/models.py", line 504, in save 
    self.pre_cache() 
  File "/home/user/mysite/photologue/models.py", line 472, in pre_cache 
    self.create_size(photosize) 
  File "/home/user/mysite/photologue/models.py", line 411, in create_size 
    if self.size_exists(photosize): 
  File "/home/user/mysite/photologue/models.py", line 364, in size_exists 
    if self.image.storage.exists(func()): 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/files/storage.py", line 392, in exists 
    return os.path.exists(self.path(name)) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/core/files/storage.py", line 405, in path 
    return safe_join(self.location, name) 
  File "/home/user/mysite/venv/lib/python3.5/site-packages/django/utils/_os.py", line 78, in safe_join 
    'component ({})'.format(final_path, base_path)) 
django.core.exceptions.SuspiciousFileOperation: The joined path (/home/user/mysite/dataplots/monitoring/cache/test_thumbnail.png) is located outside of the base path component (/home/user/mysite/media) 

显然,图像文件的副本没有放在 media/ 目录中。此外,当 image、title 和 date_added 列填充到网站数据库的 photologue_photos 表中时, slug 列不是。

如何将文件上传到MEDIA_ROOT目录?


以下是 Photologue models.py 文件中的 Photo 和 ImageModel 模型的相关片段,供引用:

class Photo(ImageModel): 
    title = models.CharField(_('title'), 
                         max_length=250, 
                         unique=True) 
    slug = models.SlugField(_('slug'), 
                        unique=True, 
                        max_length=250, 
                        help_text=_('A "slug" is a unique URL-friendly title for an object.')) 
    caption = models.TextField(_('caption'), 
                               blank=True) 
    date_added = models.DateTimeField(_('date added'), 
                                      default=now) 
    is_public = models.BooleanField(_('is public'), 
                                    default=True, 
                                    help_text=_('Public photographs will be displayed in the default views.')) 
    sites = models.ManyToManyField(Site, verbose_name=_(u'sites'), 
                                   blank=True) 
 
    objects = PhotoQuerySet.as_manager() 
 
    def save(self, *args, **kwargs): 
        if self.slug is None: 
            self.slug = slugify(self.title) 
        super(Photo, self).save(*args, **kwargs) 
 
 
class ImageModel(models.Model): 
    image = models.ImageField(_('image'), 
                          max_length=IMAGE_FIELD_MAX_LENGTH, 
                          upload_to=get_storage_path) 
    date_taken = models.DateTimeField(_('date taken'), 
                                  null=True, 
                                  blank=True, 
                                  help_text=_('Date image was taken; is obtained from the image EXIF data.')) 
    view_count = models.PositiveIntegerField(_('view count'), 
                                         default=0, 
                                         editable=False) 
    crop_from = models.CharField(_('crop from'), 
                             blank=True, 
                             max_length=10, 
                             default='center', 
                             choices=CROP_ANCHOR_CHOICES) 
    effect = models.ForeignKey('photologue.PhotoEffect', 
                           null=True, 
                           blank=True, 
                           related_name="%(class)s_related", 
                           verbose_name=_('effect')) 
 
    class Meta: 
        abstract = True 
 
    def __init__(self, *args, **kwargs): 
        super(ImageModel, self).__init__(*args, **kwargs) 
        self._old_image = self.image 
 
    def save(self, *args, **kwargs): 
        image_has_changed = False 
        if self._get_pk_val() and (self._old_image != self.image): 
            image_has_changed = True 
            # If we have changed the image, we need to clear from the cache all instances of the old 
            # image; clear_cache() works on the current (new) image, and in turn calls several other methods. 
            # Changing them all to act on the old image was a lot of changes, so instead we temporarily swap old 
            # and new images. 
            new_image = self.image 
            self.image = self._old_image 
            self.clear_cache() 
            self.image = new_image  # Back to the new image. 
            self._old_image.storage.delete(self._old_image.name)  # Delete (old) base image. 
        if self.date_taken is None or image_has_changed: 
            # Attempt to get the date the photo was taken from the EXIF data. 
            try: 
                exif_date = self.EXIF(self.image.file).get('EXIF DateTimeOriginal', None) 
                if exif_date is not None: 
                    d, t = exif_date.values.split() 
                    year, month, day = d.split(':') 
                    hour, minute, second = t.split(':') 
                    self.date_taken = datetime(int(year), int(month), int(day), 
                                           int(hour), int(minute), int(second)) 
            except: 
                logger.error('Failed to read EXIF DateTimeOriginal', exc_info=True) 
        super(ImageModel, self).save(*args, **kwargs) 
        self.pre_cache() 

这是请求的 get_storage_path 函数:

# Look for user function to define file paths 
PHOTOLOGUE_PATH = getattr(settings, 'PHOTOLOGUE_PATH', None) 
if PHOTOLOGUE_PATH is not None: 
    if callable(PHOTOLOGUE_PATH): 
        get_storage_path = PHOTOLOGUE_PATH 
    else: 
        parts = PHOTOLOGUE_PATH.split('.') 
        module_name = '.'.join(parts[:-1]) 
        module = import_module(module_name) 
        get_storage_path = getattr(module, parts[-1]) 
else: 
    def get_storage_path(instance, filename): 
        fn = unicodedata.normalize('NFKD', force_text(filename)).encode('ascii', 'ignore').decode('ascii') 
        return os.path.join(PHOTOLOGUE_DIR, 'photos', fn) 

请您参考如下方法:

关于您问题的一部分:slug 列在保存 Photo 时为空。

它应该在保存照片时自动填充-作为上面Photologue源代码的复制和粘贴if self.slug is None : self.slug = slugify(self.title) 清楚了。

这表明 Photologue 源代码实际上不是从您的管理命令中调用 - 您可以通过向 Photologue 代码的本地副本添加一些快速调试代码来检查这一点,例如save() 方法中的 print() 语句,并检查它是否正在运行。