在 Django Rest 框架中获取图像字段绝对路径 - 非请求流

Posted

技术标签:

【中文标题】在 Django Rest 框架中获取图像字段绝对路径 - 非请求流【英文标题】:Get Image Field Absolute Path in Django Rest Framework - Non Request Flows 【发布时间】:2019-12-24 12:41:52 【问题描述】:

我有一个周期性的 celery 任务,需要在特定的 json 字段中存储对象的表示。

这是简化的模型结构。 父

所以基本上我有一个 'ChildImage' 模型指的是 'ChildWrapper',而后者又指的是 'Parent'。

class Parent(TimeStampedModel):
    label = models.CharField(max_length=30, unique=True)
    live_content = JSONField(blank=True, null=True)
    is_template = models.BooleanField(default=False)
    reference_image = models.ImageField(upload_to=get_web_row_reference_image_path, blank=True, null=True)
    # Around 8 Other Fields

    def __str__(self):
        return '%s' % self.label


class ChildWrapper(TimeStampedModel):
    name = models.CharField(max_length=25, blank=True, null=True)
    row = models.ForeignKey(Parent, on_delete=models.CASCADE, related_name='web_column')
    order = models.PositiveIntegerField(default=0)
    # Around 20 Other Fields

    def __str__(self):
        return '%s' % self.name


class ChildImage(TimeStampedModel):
    image = models.ImageField(upload_to=get_web_image_path)
    column = models.ForeignKey(ChildWrapper, on_delete=models.CASCADE, related_name='web_image')
    # Around 10 Other Fields
 
    def __str__(self):
        return '%s' % self.column

这是为模型定义的序列化程序。

class ChildImageSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChildImage
        fields = '__all__'

class ChildWrapperSerializer(serializers.ModelSerializer):
    web_image = ChildImageSerializer(read_only=True, many=True)
    class Meta:
        model = ChildWrapper
        fields = '__all__'

class ParentSerializer(serializers.ModelSerializer):
    web_column = ChildWrapperSerializer(many=True, read_only=True)
    class Meta:
        model = Parent
        fields = '__all__'

这是执行所需的周期性 celery 任务

@app.task(bind=True)
def update_data(self):
    # Get Parent By a condition.
    parent = Parent.objects.filter(to_update=True).first()

    parent.live_content = None
    parent.live_content = ParentSerializer(parent).data
    print(parent.live_content)
    parent.save()

上面的任务得到子图像的输出,像这样,图像字段是相对路径而不是绝对路径。


    "id": 1
    "image": '/api/col/info.jpg'

有没有办法获取图片字段的绝对路径?


    "id": 1
    "image": "http://localhost:8000/admin/media/api/col/info.jpg"

PS: 我无法将 Request context 作为 ParentSerializer(parent, context='request': request) 传递给序列化程序,因为这里不涉及任何请求对象。

【问题讨论】:

如果您安装了django.contrib.sites,您可以使用Site.objects.get_current() 获取主机名(这将是您在数据库中配置的站点)。 settings.MEDIA_URL 是你的路径前缀。 如果没有请求对象,则无法访问主机,因此您必须使用预定义的主机并将其添加到 URL 或使用设置中允许的主机之一。 【参考方案1】:

我认为您有两种方法可以解决此问题。

第一个,是传递请求。您可以采用这种方法:

class ChildImageSerializer(serializers.ModelSerializer):
    img_url = serializers.SerializerMethodField()
    class Meta:
        model = ChildImage
        fields = '__all__'

    def get_img_url(self, obj):
        return self.context['request'].build_absolute_uri(obj.image.url)

class ChildWrapperSerializer(serializers.ModelSerializer):
    web_image = serializers.SerializerMethodField()
    class Meta:
        model = ChildWrapper
        fields = '__all__'

    def get_web_image(self, obj):
        serializer_context = 'request': self.context.get('request') 
        children = ChildImage.objects.filter(row=obj)
        serializer = ChildImageSerializer(children, many=True, context=serializer_context)
        return serializer.data

class ParentSerializer(serializers.ModelSerializer):
    web_column = serializers.SerializerMethodField()
    class Meta:
        model = Parent
        fields = '__all__'

    def get_web_column(self, obj):
        serializer_context = 'request': self.context.get('request') 
        children = ChildWrapper.objects.filter(row=obj)
        serializer = ChildWrapperSerializer(children, many=True, context=serializer_context)
        return serializer.data

这里我使用SerializerMethodField 将请求传递给下一个序列化程序。

第二种方法是使用Django Sites Framework(@dirkgroten 提到)。您可以执行以下操作:

class ChildImageSerializer(serializers.ModelSerializer):
    img_url = serializers.SerializerMethodField()
    class Meta:
        model = ChildImage
        fields = '__all__'

    def get_img_url(self, obj):
        return 'http://%s%s%s' % (Site.objects.get_current().domain, settings.MEDIA_URL, obj.img.url)

更新:我完全错过了芹菜部分。对于生产,我认为您不必担心它们在 S3 中,绝对路径应该来自 obj.image.url。在 dev 和 stage 中,您可以使用给定的示例获取绝对路径。所以,试试这样:

class ChildImageSerializer(serializers.ModelSerializer):
    img_url = serializers.SerializerMethodField()
    class Meta:
        model = ChildImage
        fields = '__all__'

    def get_img_url(self, obj):
        if settings.DEBUG:  # debug enabled for dev and stage
            return 'http://%s%s%s' % (Site.objects.get_current().domain, settings.MEDIA_URL, obj.img.url)
        return obj.img.url

或者,有一种方法可以在 celery 中使用 django-crequest 获取请求,但我不确定它是否方便您。

【讨论】:

第一个过程给了我一个类似'return self.context['request'].build_absolute_uri(obj.image.url), KeyError: 'request''的错误。由于 celery 中没有请求对象。如果图像存储在本地,则第二个过程会起作用。但它们托管在实时服务器的 aws s3(云前端)中。暂存和开发已存储在本地。 @KarthikRP 请查看答案的更新部分 将检查和更新。似乎 crequest 适用于您想要保存请求的情况,在代码中的某处使用它并删除它。由于这是一个周期性的 celery 任务,所以一开始就没有设置和使用的请求。【参考方案2】:

另一种解决方案是对主机进行硬编码:

from django.conf import settings

IMG_HOST = 
    '/home/me/path/to/project': 'http://localhost:8000',
    '/home/user/path/to/project': 'https://AWS_HOST',
[str(settings.BASE_DIR)]

class ChildImageSerializer(serializers.ModelSerializer):
    image = serializers.SerializerMethodField()

    def get_image(self, obj):
        if obj.image:
            return IMG_HOST + obj.image.url

    class Meta:
        model = ChildImage
        fields = '__all__'

【讨论】:

很好。,但对我没有帮助。我有一个开发服务器,将调试设置为 True 的登台服务器,媒体的生产托管在 AWS S3(云前端)【参考方案3】:

搞定了,

已将 MEDIA_URL 添加到我的设置文件中,如 here 所述。

似乎 DRF 使用 MEDIA_URL 作为 url(FileField 和 ImageField)的默认前缀,即使对于非请求/响应流也是如此。

由于我有一个用于暂存、开发和生产的不同设置文件,因此我可以更轻松地为每个环境设置不同的 URL。

即使我没有使用“django-versatileimagefield”库,那里的建议仍然有效。

【讨论】:

感谢您的链接!一个小细节:需要在末尾添加斜线'/'【参考方案4】:

我通过在视图中添加, context='request': request 解决了这个问题。

serializer = Business_plansSerializer(business_plans[start:end], many=True, context='request': request)

【讨论】:

以上是关于在 Django Rest 框架中获取图像字段绝对路径 - 非请求流的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 django rest 框架在模板中获取模型选择字段名称而不是 id

Django Rest Framework - 在序列化程序中获取相关模型字段

如何获取主键相关字段嵌套序列化器django rest框架的所有值

在 Django Rest 框架 URL 中通过唯一 ID 但不是 PK 获取详细信息

如何在 django rest 框架中定义列表字段?

如何在 Django REST 框架中更改字段名称