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

问题描述 投票:0回答:5

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

这是简化的模型结构。 家长 <-- ChildWrapper <-- Child Image

所以基本上我有一个“ChildImage”模型引用“ChildWrapper”,而“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__'

这是执行所需任务的定期芹菜任务

@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}) 传递给序列化器,因为这里不涉及请求对象。

python django django-rest-framework celery
5个回答
6
投票

我通过在视图中添加

, context={'request': request}
解决了问题。

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

4
投票

成功了,

MEDIA_URL 添加到我的设置文件中,如此处所述。

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

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

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


3
投票

我认为你有两种方法可以解决这个问题。

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

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
来获取请求,但我不确定它对你是否方便。


0
投票

更好的方法是在序列化器对象中添加 context= {'request': request}) 并使用它作为 url 例如

class Student(TimeStampedUUID):

    user = models.OneToOneField(User,on_delete=models.CASCADE,related_name="Student user")
    organization = models.ForeignKey(Organization,on_delete=models.CASCADE)
    first_name = models.CharField(max_length=255,verbose_name=_("Student First Name"))
    last_name = models.CharField(max_length=255,verbose_name=_("Student Last Name"))
   
    profile_picture = ProcessedImageField(upload_to='caregiver_profile_pictures', default='default.png',
                                           processors=[ResizeToFill(100, 50)],
                                           format='JPEG',
                                           options={'quality': 80},validators=[ FileExtensionValidator(allowed_extensions=['jpg', 'png', 'jpeg'])])
    
    

    @property
    def profile_picture_url(self):
        try:
            url = self.profile_picture.url
        except:
            url ='' 
        return url

然后在视图中你可以做

serialized_student = StudentSerializer(students,many=True,context= {'request': request})

-1
投票

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

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__'
© www.soinside.com 2019 - 2024. All rights reserved.