Django Rest Framework와 AngularJs를 사용하여 파일을 업로드하고 있습니다. 내보기 파일은 다음과 같습니다.
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 메소드의 마지막 줄은 모든 데이터를 반환해야하므로 몇 가지 질문이 있습니다.
- 에 아무것도 있는지 확인하는 방법
request.FILES
? - 파일 필드를 직렬화하는 방법?
- 파서를 어떻게 사용해야합니까?
답변
FileUploadParser를 사용하면 모두 요청에 포함됩니다. 대신 put 메서드를 사용하면 문서에서 예제를 찾을 수 있습니다. 🙂
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)
답변
동일한 스택을 사용하고 있으며 파일 업로드의 예를 찾고 있었지만 APIView 대신 ModelViewSet을 사용하기 때문에 제 경우가 더 간단합니다. 열쇠는 pre_save 훅으로 밝혀졌습니다. 나는 다음과 같이 angular-file-upload 모듈과 함께 사용하게되었습니다.
# 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]
});
};
});
답변
마지막으로 Django를 사용하여 이미지를 업로드 할 수 있습니다. 내 작업 코드는 다음과 같습니다.
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() # File should be closed only after all chuns are added
# ...
# 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())
업로드 요청 컬
curl -X POST -S -H -u "admin:password" -F "file=@img.jpg;type=image/jpg" 127.0.0.1:8000/resourceurl/imageUpload
답변
이것에 하루를 보낸 후 나는 그것을 알아 냈습니다 …
파일을 업로드하고 일부 데이터를 보내야하는 사람에게는 파일을 작동시킬 수있는 직접적인 방법이 없습니다. 이에 대한 json api 사양에 미해결 문제 가 있습니다. 내가 본 한 가지 가능성은 여기 에 multipart/related
표시된 것처럼 사용 하는 것이지만 drf에서 구현하기가 매우 어렵다고 생각합니다.
마지막으로 구현 한 것은 요청을 formdata
. 각 파일은 파일로 , 다른 모든 데이터는 텍스트 로 보냅니다 . 이제 데이터를 텍스트로 보내기 위해 두 가지 선택이 있습니다. 경우 1) 각 데이터를 키 값 쌍으로 보낼 수 있습니다. 또는 경우 2) data 라는 단일 키를 가지고 전체 json을 값의 문자열로 보낼 수 있습니다 .
첫 번째 방법은 간단한 필드가있는 경우 즉시 작동하지만 중첩 된 직렬화가있는 경우 문제가됩니다. 멀티 파트 구문 분석기는 중첩 된 필드를 구문 분석 할 수 없습니다.
아래에서는 두 경우 모두에 대한 구현을 제공하고 있습니다.
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-> 특별한 변경이 필요하지 않습니다. 쓰기 가능한 ManyToMany 필드 구현으로 인해 여기에 내 serializer가 너무 긴 것으로 표시되지 않습니다.
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'
이제 첫 번째 방법을 따르고 비 JSON 데이터 만 키 값 쌍으로 보내는 경우 사용자 지정 파서 클래스가 필요하지 않습니다. DRF의 MultipartParser가 작업을 수행합니다. 그러나 두 번째 경우 또는 중첩 된 직렬 변환기가있는 경우 (내가 보여준 것처럼) 아래와 같이 사용자 정의 파서가 필요합니다.
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)
이 serializer는 기본적으로 값의 모든 json 콘텐츠를 구문 분석합니다.
답변
제 경험상 파일 필드에 대해 특별히 할 필요는 없습니다. 파일 필드를 사용하라고 말하면됩니다.
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)),
]
파일을 업로드 할 준비가되었습니다.
curl -sS http://example.com/api/photos/ -F 'file=@/path/to/file'
추가 -F field=value
모델이있는 각 여분 필드. 그리고 인증을 추가하는 것을 잊지 마십시오.
답변
ModelViewSet 및 ModelSerializer로이 문제를 해결했습니다. 이것이 커뮤니티에 도움이되기를 바랍니다.
또한 뷰가 아닌 serializer 자체에서 유효성 검사 및 Object-> JSON (또는 그 반대) 로그인을 선호합니다.
예를 들어 이해합시다.
FileUploader API를 만들고 싶습니다. id, file_path, file_name, size, owner 등과 같은 필드를 데이터베이스에 저장할 위치입니다. 아래 샘플 모델을 참조하십시오.
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)
이제 API의 경우 이것이 내가 원하는 것입니다.
- 가져 오기:
GET 엔드 포인트를 실행할 때 업로드 된 모든 파일에 대해 위의 모든 필드를 원합니다.
- 게시하다:
하지만 사용자가 파일을 생성 / 업로드하기 위해이 모든 필드를 전달하는 것에 대해 걱정해야하는 이유입니다. 그녀는 파일을 업로드하기 만하면 직렬화 기가 업로드 된 FILE에서 나머지 필드를 가져올 수 있습니다.
Searilizer :
질문 : 내 목적을 달성하기 위해 아래에 serializer를 만들었습니다. 그러나 그것을 구현하는 올바른 방법인지 확실하지 않습니다.
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)
참조 용 뷰셋 :
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
답변
Django Rest Framework 용 ModelViewset을 사용하는 가장 쉬운 예제에 관심이있는 사람이 있다면.
모델은
class MyModel(models.Model):
name = models.CharField(db_column='name', max_length=200, blank=False, null=False, unique=True)
imageUrl = models.FileField(db_column='image_url', blank=True, null=True, upload_to='images/')
class Meta:
managed = True
db_table = 'MyModel'
직렬 변환기,
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = "__all__"
그리고보기는,
class MyModelView(viewsets.ModelViewSet):
queryset = MyModel.objects.all()
serializer_class = MyModelSerializer
Postman에서 테스트,
