save upload file to dynamic directory and get the

2019-08-17 13:35发布

问题:

I'm uploading a file and additionally some data like file's id and file's title to the server. I have the view below to handle the request and I want to save the file to a dynamic path like upload/user_id/thefile.txt.

With code the below the file will be saved in and upload folder directly and also my product_video table will create a new record with the related id and the title.
Now I don't have any idea how can I save the file in a dynamically generated directory like: upload/user_id/thefile.txt and how to save the produced path to the database table column video_path?

view class:

class FileView(APIView):
    parser_classes = (MultiPartParser, FormParser)

    def post(self, request, *args, **kwargs):
        if request.method == 'POST' and request.FILES['file']:
            myfile = request.FILES['file']
            serilizer = VideoSerializer(data=request.data)
            if serilizer.is_valid():
                serilizer.save()

            fs = FileSystemStorage()
            fs.save(myfile.name, myfile)

            return Response("ok")

        return Response("bad")

and serializer clas:

class VideoSerializer(ModelSerializer):
    class Meta:
        model = Product_Video
        fields = [
            'p_id',
            'title',
            'video_length',
            'is_free',
        ]

and related model class:

def user_directory_path(instance, filename):
    return 'user_{0}/{1}'.format(instance.user.id, filename)


class Product_Video(models.Model):
    p_id = models.ForeignKey(Product, on_delete=models.CASCADE, to_field='product_id', related_name='product_video')
    title = models.CharField(max_length=120, null=True,blank=True)
    video_path = models.FileField(null=True, upload_to=user_directory_path,storage=FileSystemStorage)
    video_length = models.CharField(max_length=20, null=True, blank=True)
    is_free = models.BooleanField(default=False)

回答1:

You put the cart before the horse. Start from the beginning, first things first. And the first thing is the user story, then the model layer.

There are Products and a Product can have many ProductVideos. A Product has an Author (the Author can have many Products). When you upload a ProductVideo for a particular Product, you want to save it in a directory which contains the Author ID.

Therefore we specify a callable for the FileField which should dynamically find out the id of the Author:

def user_directory_path(instance, filename):
    # get the id of the author
    # first get the Product, then get its author's id
    user_id = str(instance.p_id.author.id)
    # little bit cosmetics
    # should the filename be in uppercase
    filename = filename.lower()
    return 'user_{0}/{1}'.format(user_id, filename)

When saving an instance of Product_Video the uploaded file should be stored in a directory with a dynamically created pathname based on the author's ID.

Further I'd suggest you to follow established coding conventions:

  • don't use snake_case for class names, especially don't mix it with capital letters. Use PascalCase, preferably singular nouns, for class names: ProductVideo
  • if your model class is ProductVideo, keep that name in all subsequent classes: ProductVideoSerializer for the model serializer and ProductVideoAPIView for the generic view.
  • use verbs for methods and standalone functions, get_user_directory or upload_to_custom_path is better than user_directory_path.
  • also avoid suffixing the foreign key fields with _id. Use rather product instead of p_id. Django will allow you to access the related object by calling product and get the id of the object by calling product_id. Using the name p_id means you'll access the id of the related object by calling p_id_id which looks very weird and confusing.