I tried many times, but I just register the last one
im my model
class PropertyImage(models.Model):
property = models.ForeignKey(Property, default=None, on_delete=models.CASCADE,)
images = models.ImageField(upload_to=upload, null=True, blank=True)
def __str__(self):
return str(self.images)
serializer
class PropertyImageSerializers (serializers.ModelSerializer):
class Meta:
model = PropertyImage
#fields =('name','')
fields = '__all__'
my class view handler the post request, I tried to user method FOR to loop all images and save
view
def post(self, request, *args, **kwargs):
property_id = request.data['property']
form_data = {}
for images in request.FILES.getlist('images'):
form_data['property']= property_id
form_data['images']=images
print(form_data)
serializer = PropertyImageSerializers(data=form_data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
the for not give me the loop, does't matter how many images I send
I get this error message: AttributeError: 'PropertyImageSerializers'
object has no attribute 'property' but im my model you can see I have
the property
property is not an attribute of the PropertyImageSerializers class, that's why AttributeError
I think you will find your answer here
Update:
You can do it like this
def post(self, request, *args, **kwargs):
property_id = request.data['property']
form_data = {}
form_data['property']= property_id
success = True
response = []
for images in request.FILES.getlist('images'):
form_data['images']=images
print(form_data)
serializer = PropertyImageSerializers(data=form_data)
if serializer.is_valid():
serializer.save()
response.append(serializer.data)
else:
success = False
if success:
return Response(response, status=status.HTTP_201_CREATED)
return Response(response,status=status.HTTP_400_BAD_REQUEST)
Related
enter image description here
views
#swagger_auto_schema(request_body=BoardDetailSerializer, operation_summary="update method")
def patch(self, request, pk):
board = self.get_object(pk)
serializer = BoardDetailSerializer(board, data=request.data, partial=True)
if serializer.is_valid():
serializer.update()
return Response(serializer.data, status=status.HTTP_200_OK)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializers
class BoardSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=30)
background = serializers.ImageField(required=False, default=None)
def create(self, validated_data):
board = Board(
title=validated_data['title'],
background=validated_data['background'],
)
board.save()
return board
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['column'] = ColumnSerializer(instance.column.all(), many=True, context=self.context).data
return representation
[
1. - ---
](https://i.stack.imgur.com/A4yjf.png)
Idk what to do. I removed the 'Partial = True', and restarted patch request many time, but this is not workin.
I am trying to upload both text data and a file to django server using angular for the front-end,
here is my models in django
#models.py
class UploadChapter(models.Model):
chapter_number = models.CharField(max_length=50, choices=chapters)
chapter_file = models.FileField(upload_to="files")
student = models.ForeignKey(User,on_delete=models.CASCADE)
supervisor = models.ForeignKey(Supervisor, on_delete=models.CASCADE)
status = models.CharField(max_length=100, choices=status, default="Pending")
def __str__(self):
return f'{self.student} - {self.chapter_number}'
for the serializer I wrote this code
#serializers.py
class UploadChapterSerializer(serializers.ModelSerializer):
class Meta:
model = UploadChapter
fields = ['chapter_number', 'chapter_file', 'student', 'supervisor', 'status']
and for the views i wrote this code
#views.py
#csrf_exempt
def ChapterApi(request, id=0):
if request.method=='GET':
chapters=UploadChapter.objects.all()
chaptersSerializer=UploadChapterSerializer(chapters, many=True)
return JsonResponse(chaptersSerializer.data, safe=False)
elif request.method=='POST':
chaptersData=JSONParser().parse(request)
chaptersSerializer=UploadChapterSerializer(data=chaptersData)
if chaptersSerializer.is_valid():
chaptersSerializer.save()
return JsonResponse('Added Successfully', safe=False)
return JsonResponse('Failed', safe=False)
so am getting errors from the JSONParser and I really dont know how to do this, I need help please
I found an answer for myself, i wrote a class-based view instead of the function based, here is the code for it
class ChapterView(APIView):
parser_classes = (MultiPartParser, FormParser)
def post(self, request, *args, **kwargs):
chapter_serializer = UploadChapterSerializer(data=request.data)
if chapter_serializer.is_valid():
chapter_serializer.save()
return JsonResponse('Chapter Sent Successfully', safe=False)
else:
return JsonResponse('upload fail', safe=False)
def get(self, request):
chapter = UploadChapter.objects.all()
chapterSerializer = UploadChapterSerializer(chapter, many=True)
return JsonResponse(chapterSerializer.data, safe=False)
When I receive a POST request on a Django REST framework APIView class, I would like to filter/validate the parameters that are passed to prevent them from being modified. For example, for this serializer:
class MediaSerializer(serializers.ModelSerializer):
class Meta:
model = Media
fields = ('id', 'title', 'content', 'url', 'createdByUser', 'karma', 'type', 'issue', 'creationDate', 'updatedDate')
Some parameters such as the id, creationDate or createdByUser shouldn't be modified. So for my class class MediaDetail(APIView) I have:
def validateRequest(self):
user = self.request.data.get('createdByUser', None)
karma = self.request.data.get('karma', None)
creationDate = self.request.data.get('creationDate', None)
if user is not None or karma is not None or creationDate is not None:
return Response(status=status.HTTP_400_BAD_REQUEST)
#method_decorator(login_required)
def post(self, request, pk, format=None):
self.validateRequest()
media = self.get_object(pk)
self._throwIfNotMediaAuthor(media, request.user)
serializer = MediaSerializer(media, data=request.data)
if serializer.is_valid():
# serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Is there a better way to make this validation? Maybe on the serializer? I couldn't find enough documentation.
Yes, you can use the read_only_fields parameter on your serializer's Meta.
Example on how to use inside your current view (modified it a little assuming you want to create an object when POSTing as per REST's guidelines):
class MediaSerializer(serializers.ModelSerializer):
class Meta:
model = Media
read_only_fields = ('id', 'karma', 'createdByUser', 'creationDate')
...
#method_decorator(login_required)
def post(self, request, pk, format=None):
serializer = MediaSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(createdByUser=request.user, creationDate=timezone.now().date(), karma=initial_value)
return Response(serializer.data, status=status.HTTP_201_CREATED)
I need to update categories in many Article in one request.
In ArticleViewSet I have:
def get_serializer_class(self):
if self.action in ['partial_update', 'update']:
return ArticlePostSerializer
return ArticleSerializer
So ArticlePostSerializer need to be changed.
This is my serializers code:
class ArticleShortCategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = 'id', 'name'
class ArticleSerializer(serializers.ModelSerializer):
categories = serializers.SerializerMethodField()
def get_categories(self, obj):
return ArticleShortCategorySerializer(obj.categories, many=True).data
class Meta:
model = Article
read_only_fields = 'id'
fields = ('categories', 'text') + read_only_fields
class ArticlePostSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = 'id', 'categories', 'text'
I tried to add:
class ArticlePostListSerializer(serializers.ListSerializer):
and
class Meta:
list_serializer_class = ArticlePostListSerializer
But it doen't work.
How to change this code to do multiple update.
My json request
{
[id: 90, categories: [10,12,14]],
[id: 93, categories: [10,12,14]],
[id: 95, categories: [10,12,14]]
}
Here is sample of CreateMixins OR UpdateMixins you requested.
======================= VIEW ================================
class OrderCreate(mixins.CreateModelMixin,viewsets.GenericViewSet):
pagination_class = None
def get_queryset(self):
return []
def get_serializer_class(self):
return serializers.OrderSerializer
======================= Serializer =============================
class OrderDetailSerializer(serializers.ModelSerializer):
class Meta:
model = crm_models.OrderDetail
fields = (
'product',
'quantity',
'rate_per_unit',
'order_quantity'
)
class OrderSerializer(serializers.ModelSerializer):
order_details = OrderDetailSerializer(many = True)
class Meta:
model = crm_models.OrderMaster
fields = (
'order',
'invoice_number',
'client',
'beat_check',
'target_customer',
'order_editor_client_employee',
'order_marked',
'order_saved',
'edit_marked',
'edit_saved',
'adhoc',
'order_details'
)
def create(self, validated_data,*args,**kwargs):
ordersdetails_data = validated_data.pop('order_details')
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
validated_data['client'] = user.client
validated_data['order_editor_client_employee'] = user
validated_data['adhoc'] = validated_data['adhoc'] if 'adhoc' in validated_data else False
orderObj = super(OrderSerializer, self).create(validated_data,*args,**kwargs)
orderdetails = []
for details in ordersdetails_data:
orderdetails.append(crm_models.OrderDetail(
product= details['product'],
quantity = details['quantity'],
rate_per_unit = details['rate_per_unit'],
order_quantity = details['order_quantity'],
order = orderObj
))
crm_models.OrderDetail.objects.bulk_create(orderdetails)
return orderObj
In Update view function name would be changed to update, you can find more documentation http://www.django-rest-framework.org/api-guide/generic-views/#createmodelmixin
I found K. Moe's answer to this question: Django Rest Framework POST Update if existing or create much easier to understand and implement. You only need to add a create method in the serializer and use mixins.CreateModelMixin, generics.GenericAPIView in the view. Then you can use a POST request, instead of PUT/PATCH. It allows to create AND update data stored in your database. My code for the view:
class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):
def post(self, request, format=None):
is_many = isinstance(request.data, list)
if is_many:
serializer = ZipCodeSerializer(data=request.data, many=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
serializer = ZipCodeSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
#Greg Holst, why so much duplication? Why not:
class ZipCodeList(mixins.CreateModelMixin, generics.GenericAPIView):
def post(self, request, format=None):
serializer = ZipCodeSerializer(data=request.data, many=isinstance(request.data, list))
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Anyway, that only worked for me for creating new objects, didn't work to create-or-update in one sweep (it kept telling me these objects already exist), so this is what I did:
class ContributorSyncListAPIView(ListAPIView):
permission_classes = (isSuperUserPermission,)
allowed_methods = ("GET", "PUT")
lookup_field = "airtable_id"
serializer_class = ContributorSyncSerializer # Doesn't get used in Put, just in Get.
model = Contributor
def get_queryset(self):
return self.model.objects.all()
def put(self, request, format=None):
objects = list(request.data)
# objects is a list of OrderedDicts
try:
for o in objects:
try:
instance = self.model.objects.get(
**{self.lookup_field: o.get(self.lookup_field)}
)
for key, value in o.items():
setattr(instance, key, value)
except self.model.DoesNotExist:
instance = self.model(**o)
instance.save()
return Response(objects, status=status.HTTP_200_OK)
except Exception as e:
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
Note my code above is very light on validation just because it was for a process of syncing models from one environment to another by a superuser, different environments with an identical codebase; so the assumption was the data had already been validated when the data was entered into the first environment. For any other purpose you'd want to validate more. But this is what I had to do to handle a list of objects that may need to be created or updating, on an object-by-object basis.
Edit
I suspect the whole problem with my UpdateApiView is with the url. No matter how I change it, will return 404.
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone'),
it returns
{
"detail": "Not found."
}
[18/Apr/2016 01:39:02] "PATCH /api/verify-phone/phone_id=00980 HTTP/1.1" 404 4941
Why?
views.py
class VerifyPhone(generics.UpdateAPIView):
permission_classes = (AllowAny,)
serializer_class = serializers.VerifyPhoneSerializer
allowed_methods = ['PATCH']
lookup_field = 'phone_id'
def get_queryset(self):
phone_id = self.request.query_params.get('phone_id', None)
queryset = User.objects.filter(phone_id=phone_id)
return queryset
def update(self, request, *args, **kwargs):
print('inside update')
print(request.data)
partial = kwargs.pop('partial', False)
instance = self.get_object()
print(instance)
serializer = self.get_serializer(instance, data=request.data, partial=partial)
print(serializer)
serializer.is_valid(raise_exception=True)
self.perform_update(serializer)
print('done perform update')
return Response(serializer.data)
serializers.py
class VerifyPhoneSerializer(serializers.ModelSerializer):
regex = r'\d+'
verification_code = serializers.RegexField(regex, max_length=7, min_length=7, allow_blank=False)
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
class Meta:
model = User
fields = ('verification_code', 'phone_id')
def validate(self, data):
verification = api.tokens.verify(data['phone_id'], data['verification_code'])
if verification.response.status_code != 200:
raise serializers.ValidationError("Invalid verification code.")
return data
def update(self, instance, validated_data):
instance.phone_number_validated = True
instance.save()
return instance
Second question Is this correct to get phone_id from the views?
phone_id = serializers.HyperlinkedRelatedField(view_name='verify-phone', lookup_field='phone_id', read_only=True)
Looking at your api url def, I think you should call:
/api/verify-phone/00980
instead of
/api/verify-phone/phone_id=00980
I also think something is wrong with the url def itself (the ^ before \d):
url(r'verify-phone/(?P<phone_id>^\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
should be
url(r'verify-phone/(?P<phone_id>\d+)$', view.VerifyPhone.as_view(), name='verify-phone')
or
url(r'verify-phone/(?P<phone_id>\d{5})$', view.VerifyPhone.as_view(), name='verify-phone')