it-swarm.asia

Django Rest Framework Dosya Yükleme

Bir dosya yüklemek için Django Rest Framework ve AngularJs kullanıyorum. Görünüm dosyam şöyle görünüyor:

class ProductList(APIView):
    authentication_classes = (authentication.TokenAuthentication,)
    def get(self,request):
        if request.user.is_authenticated(): 
            userCompanyId = request.user.get_profile().companyId
            products = Product.objects.filter(company = userCompanyId)
            serializer = ProductSerializer(products,many=True)
            return Response(serializer.data)

    def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(data=request.DATA)

Post post yönteminin tüm verileri döndürmesi gerektiğinden, birkaç sorum var:

  • request.FILES'da herhangi bir şey olup olmadığını kontrol etme
  • dosya alanı nasıl serileştirilir?
  • ayrıştırıcıyı nasıl kullanmalıyım?
60
Pawan

FileUploadParser komutunu kullanın, hepsi isteğe bağlıdır.

class FileUploadView(views.APIView):
    parser_classes = (FileUploadParser,)

    def put(self, request, filename, format=None):
        file_obj = request.FILES['file']
        # do some stuff with uploaded file
        return Response(status=204)
40

Aynı yığını kullanıyorum ve ayrıca bir dosya yükleme örneği de arıyordum, ancak benim durumum APIView yerine ModelViewSet kullandığım için daha kolay. Anahtar pre_save kanca olduğu ortaya çıktı. Bunun gibi açısal dosya yükleme modülüyle birlikte kullanmaya başladım:

# Django
class ExperimentViewSet(ModelViewSet):
    queryset = Experiment.objects.all()
    serializer_class = ExperimentSerializer

    def pre_save(self, obj):
        obj.samplesheet = self.request.FILES.get('file')

class Experiment(Model):
    notes = TextField(blank=True)
    samplesheet = FileField(blank=True, default='')
    user = ForeignKey(User, related_name='experiments')

class ExperimentSerializer(ModelSerializer):
    class Meta:
        model = Experiment
        fields = ('id', 'notes', 'samplesheet', 'user')

// AngularJS
controller('UploadExperimentCtrl', function($scope, $upload) {
    $scope.submit = function(files, exp) {
        $upload.upload({
            url: '/api/experiments/' + exp.id + '/',
            method: 'PUT',
            data: {user: exp.user.id},
            file: files[0]
        });
    };
});
58
ybendana

Sonunda Django kullanarak resim yükleyebiliyorum. İşte çalışma kodum

views.py

class FileUploadView(APIView):
    parser_classes = (FileUploadParser, )

    def post(self, request, format='jpg'):
        up_file = request.FILES['file']
        destination = open('/Users/Username/' + up_file.name, 'wb+')
        for chunk in up_file.chunks():
            destination.write(chunk)
            destination.close()

        # ...
        # do some stuff with uploaded file
        # ...
        return Response(up_file.name, status.HTTP_201_CREATED)

urls.py

urlpatterns = patterns('', 
url(r'^imageUpload', views.FileUploadView.as_view())

yüklemek için curl isteği

curl -X POST -S -H -u "admin:password" -F "[email protected];type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
23
Vipul J

Bunun için 1 gün geçirdikten sonra, anladım ki ...

Dosya yüklemesi ve bazı verileri göndermesi gereken biri için, çalışmasını sağlamak için hiçbir fwd yol yoktur. Bunun için json api özelliklerinde bir açık sayı var. Gördüğüm bir olasılık, burada gösterildiği gibi multipart/related kullanmaktır, ancak onu drf içinde uygulamak çok zor.

Sonunda benim uygulamaya koyduğum, isteği formdata olarak göndermek oldu. Her bir dosyayı dosya olarak ve diğer tüm verileri metin olarak gönderirsiniz .. __ Şimdi veriyi metin olarak göndermek için iki seçeneğiniz vardır. durum 1) her veriyi anahtar değer çifti olarak gönderebilirsiniz veya durum 2) data adında tek bir anahtara sahip olabilir ve tüm json değerini değer olarak dize olarak gönderebilirsiniz. 

İlk yöntem, basit alanlarınız varsa kutudan çıkar, ancak serileştirmeleriniz varsa bir sorun çıkarır. Çok parçalı ayrıştırıcı, iç içe geçmiş alanları ayrıştıramaz.

Aşağıda her iki durum için de uygulama sunuyorum

Models.py

class Posts(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    caption = models.TextField(max_length=1000)
    media = models.ImageField(blank=True, default="", upload_to="posts/")
    tags = models.ManyToManyField('Tags', related_name='posts')

serializers.py -> özel bir değişiklik gerekmez, seri hale getiricimi burada yazılabilir ManyToMany Field uygulamasından dolayı çok uzun göstermez.

views.py

class PostsViewset(viewsets.ModelViewSet):
    serializer_class = PostsSerializer
    #parser_classes = (MultipartJsonParser, parsers.JSONParser) use this if you have simple key value pair as data with no nested serializers
    #parser_classes = (parsers.MultipartParser, parsers.JSONParser) use this if you want to parse json in the key value pair data sent
    queryset = Posts.objects.all()
    lookup_field = 'id'

Şimdi, ilk yöntemi izliyorsanız ve yalnızca Json dışı verileri anahtar değer çiftleri olarak gönderiyorsanız, özel bir ayrıştırıcı sınıfına ihtiyacınız yoktur. DRF'd MultipartParser işi yapacak. Ancak ikinci vaka için veya iç içe geçmişleştiricilere sahipseniz (gösterdiğim gibi) aşağıda gösterildiği gibi özel ayrıştırıcıya ihtiyacınız olacaktır.

utils.py

from Django.http import QueryDict
import json
from rest_framework import parsers

class MultipartJsonParser(parsers.MultiPartParser):

    def parse(self, stream, media_type=None, parser_context=None):
        result = super().parse(
            stream,
            media_type=media_type,
            parser_context=parser_context
        )
        data = {}

        # for case1 with nested serializers
        # parse each field with json
        for key, value in result.data.items():
            if type(value) != str:
                data[key] = value
                continue
            if '{' in value or "[" in value:
                try:
                    data[key] = json.loads(value)
                except ValueError:
                    data[key] = value
            else:
                data[key] = value

        # for case 2
        # find the data field and parse it
        data = json.loads(result.data["data"])

        qdict = QueryDict('', mutable=True)
        qdict.update(data)
        return parsers.DataAndFiles(qdict, result.files)

Bu seri hale getirici temelde herhangi bir json içeriğini değerlerde ayrıştırır.

Her iki durumda da postacıdaki istek örneği: vaka 1 case 1

Durum 2 case2

4
nithin

Bu sorunu ModelViewSet ve ModelSerializer ile çözdüm. Umarım bu topluluğa yardımcı olur.

Ayrıca, görünümler yerine serileştiricinin kendisinde doğrulama ve Object-> JSON (ve bunun tersi) oturum açmayı da tercih ederim.

Örnek olarak anlayalım.

Diyelim ki, FileUploader API oluşturmak istiyorum. ID, file_path, file_name, size, owner vb gibi alanları veritabanında depolayacağı yer. Aşağıdaki örnek modele bakınız:

class FileUploader(models.Model):
    file = models.FileField()
    name = models.CharField(max_length=100) #name is filename without extension
    version = models.IntegerField(default=0)
    upload_date = models.DateTimeField(auto_now=True, db_index=True)
    owner = models.ForeignKey('auth.User', related_name='uploaded_files')
    size = models.IntegerField(default=0)

Şimdi, API'ler için istediğim şey bu:

  1. ALMAK:

GET bitiş noktasını ateşlediğimde, yüklenen her dosya için yukarıdaki alanların hepsini istiyorum.

  1. POST:

Ancak, kullanıcının dosya oluşturması/yüklemesi için neden tüm bu alanları geçme konusunda endişelenmesi gerekiyor. Sadece dosyayı yükleyebilir ve daha sonra, sanırım, seri hale getirici yüklenen FILE alanlarını geri alabilir.

Searilizer: Soru: Amacımı yerine getirmek için seri oluşturucunun altında yarattım. Fakat bunu uygulamanın doğru yolu olup olmadığından emin değilim.

class FileUploaderSerializer(serializers.ModelSerializer):
    # overwrite = serializers.BooleanField()
    class Meta:
        model = FileUploader
        fields = ('file','name','version','upload_date', 'size')
        read_only_fields = ('name','version','owner','upload_date', 'size')

   def validate(self, validated_data):
        validated_data['owner'] = self.context['request'].user
        validated_data['name'] = os.path.splitext(validated_data['file'].name)[0]
        validated_data['size'] = validated_data['file'].size
        #other validation logic
        return validated_data

    def create(self, validated_data):
        return FileUploader.objects.create(**validated_data)

Referans için Viewset:

class FileUploaderViewSet(viewsets.ModelViewSet):
    serializer_class = FileUploaderSerializer
    parser_classes = (MultiPartParser, FormParser,)

    # overriding default query set
    queryset = LayerFile.objects.all()

    def get_queryset(self, *args, **kwargs):
        qs = super(FileUploaderViewSet, self).get_queryset(*args, **kwargs)
        qs = qs.filter(owner=self.request.user)
        return qs
3
Jadav Bheda

Tecrübelerime göre, dosya alanları hakkında özel bir şey yapmanıza gerek yoktur, sadece dosya alanından yararlanmanızı söyleyin:

from rest_framework import routers, serializers, viewsets

class Photo(Django.db.models.Model):
    file = Django.db.models.ImageField()

    def __str__(self):
        return self.file.name

class PhotoSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Photo
        fields = ('id', 'file')   # <-- HERE

class PhotoViewSet(viewsets.ModelViewSet):
    queryset = models.Photo.objects.all()
    serializer_class = PhotoSerializer

router = routers.DefaultRouter()
router.register(r'photos', PhotoViewSet)

api_urlpatterns = ([
    url('', include(router.urls)),
], 'api')
urlpatterns += [
    url(r'^api/', include(api_urlpatterns)),
]

ve dosyaları yüklemeye hazırsınız:

curl -sS http://example.com/api/photos/ -F '[email protected]/path/to/file'

Modelinizde bulunan her ekstra alan için -F field=value ekleyin. Ve kimlik doğrulama eklemeyi unutmayın.

1
x-yuri
def post(self,request):
        serializer = ProductSerializer(data=request.DATA, files=request.FILES)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
0
Syed Faizan
    from rest_framework import status
    from rest_framework.response import Response
    class FileUpload(APIView):
         def put(request):
             try:
                file = request.FILES['filename']
                #now upload to s3 bucket or your media file
             except Exception as e:
                   print e
                   return Response(status, 
                           status.HTTP_500_INTERNAL_SERVER_ERROR)
             return Response(status, status.HTTP_200_OK)
0
sidhu Munagala

Django-rest-framework istek verisinde Parsers tarafından ayrıştırılır.
http://www.Django-rest-framework.org/api-guide/parsers/

Varsayılan olarak Django-rest-framework ayrıştırıcı sınıfı JSONParser alır. Verileri json'a ayrıştırır. bu nedenle, dosyalar onunla ayrıştırılmaz.
Dosyaların diğer verilerle birlikte ayrıştırılmasını istiyorsak, aşağıdaki ayrıştırıcı sınıflardan birini kullanmalıyız.

FormParser
MultiPartParser
FileUploadParser
0
Anjaneyulu Batta

Daha temiz ve bakımı kolay olduğunu düşündüğüm bir seçenek daha yazmak istiyorum. Görüntü setimize CRUD URL'leri eklemek için defaultRouter'ı kullanacağız ve aynı görünüm içinde yükleyici görünümünü belirten bir tane daha sabit URL ekleyeceğiz.

**** views.py 

from rest_framework import viewsets, serializers
from rest_framework.decorators import action, parser_classes
from rest_framework.parsers import JSONParser, MultiPartParser
from rest_framework.response import Response
from rest_framework_csv.parsers import CSVParser
from posts.models import Post
from posts.serializers import PostSerializer     


class PostsViewSet(viewsets.ModelViewSet):

    queryset = Post.objects.all()
    serializer_class = PostSerializer 
    parser_classes = (JSONParser, MultiPartParser, CSVParser)


    @action(detail=False, methods=['put'], name='Uploader View', parser_classes=[CSVParser],)
    def uploader(self, request, filename, format=None):
        # Parsed data will be returned within the request object by accessing 'data' attr  
        _data = request.data

        return Response(status=204)

Projenin ana url.py

**** urls.py 

from rest_framework import routers
from posts.views import PostsViewSet


router = routers.DefaultRouter()
router.register(r'posts', PostsViewSet)

urlpatterns = [
    url(r'^posts/uploader/(?P<filename>[^/]+)$', PostsViewSet.as_view({'put': 'uploader'}), name='posts_uploader')
    url(r'^', include(router.urls), name='root-api'),
    url('admin/', admin.site.urls),
]

.- README.

Büyü, sınıf metodumuza 'uploader' a @ action dekoratör eklediğimizde olur. "Method = ['put']" argümanını belirterek, yalnızca PUT isteklerine izin veriyoruz; dosya yükleme için mükemmel. 

Ayrıca, içeriğinizi ayrıştırıcı ayrıştırıcıyı seçebileceğinizi göstermek için "parser_classes" bağımsız değişkenini de ekledim. CSVParser'ı rest_framework_csv paketinden ekledim, bu işlevsellik gerekirse yalnızca belirli türdeki dosyaları nasıl kabul edebileceğimizi göstermek için benim durumumda sadece "Content-Type: text/csv" kabul ediyorum. Not: Özel Parsers ekliyorsanız, onları yükleme yöntemi ayrıştırıcılarına erişmeden önce izin verilen media_type ana (sınıf) ayrıştırıcılarla karşılaştıracağından, bunları ViewSet'te parsers_class içinde belirtmeniz gerekir. 

Şimdi Django'ya bu yönteme nasıl gideceğimizi ve URL'lerimizde nereye uygulanabileceğini söylemeliyiz. O zaman sabit URL'yi ekleriz (Basit amaçlar). Bu URL, daha sonra yöntemde aktarılacak olan bir "dosya adı" argümanını alır. Bir listedeki http protokolünü ('PUT') belirten, PostsViewSet.as_view yöntemine bu yöntemi "uploader" olarak geçmemiz gerekiyor. 

Aşağıdaki URL’ye girdiğimizde 

 http://example.com/posts/uploader/ 

"Content-Type" ve Content-Disposition: ekini belirten başlıklarla bir PUT isteği bekleyecektir; filename = "something.csv".

curl -v -u user:pass http://example.com/posts/uploader/ --upload-file ./something.csv --header "Content-type:text/csv"
0
Wolfgang Leon