DRF How to patch when file upload is required...? - django-rest-framework

DRF How do you patch when file upload is required and you don't want to post the file again?
I keep getting this response: {'xyz_file': [u'No file was submitted.']}
I don't have xyz_file required on the serializer. This is not a field on the model because I don't want to save it in the db.
class XYZSerializer(ParentSerializer):
xyz_file = serializers.FileField(source='get_file_field', use_url=False, validators=[xyz_extensions_validator])
class Meta:
model = models.XYZModel
fields = ('name', 'xyz_file', 'active',)
I've tried overwriting the update method in the view and serializer. Neither worked.

Ok so this is how i fixed my problem.
In my serializer I added this method:
def exclude_fields(self, fields_to_exclude=None):
if isinstance(fields_to_exclude, list):
for f in fields_to_exclude:
f in self.fields.fields and self.fields.fields.pop(f) or next()
In my viewset I overrode the update method with this:
def update(self, request, *args, **kwargs):
partial = False
if 'PATCH' in request.method:
partial = True
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
if 'xyz_file' in request.data and not request.data['xyz_file']:
serializer.exclude_fields(['xyz_file'])
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
serializer.save()
return Response(serializer.data)
So the idea is to remove the field from even being validated. Also if you wanted to run this on a field that is on the model, the popping of the field will prevent you from saving the non validated field.

If you are using PATCH HTTP method, then you could turn on partial updates, which doesnt require any fields I believe.
Then you define your serializer inside your update method in your view:
serializer = XYZSerializer(instance=xyz,data=request.data,partial=True)
Is written here http://www.django-rest-framework.org/api-guide/serializers/#partial-updates.

Related

django - pass request.user to ModelForm

i am having a tricky issue. In my views.py i am passing a form in a DetailView. But i am not using a FormMixin. That has the reason that i only want my template to render the form. In the template i use that form to trigger an UpdateView.
class UpdateDetailView(LoginRequiredMixin, DetailView):
model = Balance
def get_context_data(self, **kwargs):
context = super(UpdateDetailView, self).get_context_data(**kwargs)
context['form'] = ManagerForm
return context
def get_queryset(self, *args, **kwargs):
request = self.request
pk = self.kwargs.get('pk')
select = self.kwargs.get('select')
queryset = Balance.objects.filter(pk=pk).filter(owner = request.user).filter(select = select)
return queryset
class BalanceUpdateView(LoginRequiredMixin, UpdateView):
form_class = ManagerForm
model = Balance
def get_success_url(self):
return reverse('url-year', kwargs={'year': self.object.year, 'select': self.object.select})
So far, so good. The issue is that the form in the UpdateDetailView the ChoiceFields are showing select option of other users which one is not supposed to see. Thus i need to override the queryset of the Modelform. Usually i could use the get_form_kwargs method to pass the request.user to the form. But since the UpdateDetailView is not a FormView it doesnt have that (i know, coz i tried desperately). I also tried context['form'] = ManagerForm(initial = {'user': self.request.user}) in the get_context_data method. But i was unable to access user properly with the __init__ method in the forms.py. I got a key error. If i passed it as arg then i get an attribute error. Does anyone have a solution to that problem? Do i need to use a FormMixin?
Thank you for your replies!

How to serialize multiple query set to json Response in django rest frame work

I am getting a list of committees by user using a get method in which I am sending a user id but I am getting an error Committee object is not serialize I have serializer created but I dont't know how to serializer the queryset of that particular user id result.
below is my views.py file
def get(self, request, user_id):
get_committees = Committee.objects.filter(user=Profile.objects.get(id=user_id))
data = {
"status": "success",
"data":get_committees
}
res = Response(serializer.data, status=status.HTTP_200_OK)
below is my serializer.py
class MyCommitteesSerializer(serializers.ModelSerializer):
def get_queryset(self, user_id):
my_committees =
Committee.objects.filter(user=Profile.objects.get(id=user_id))
return my_committees
from your code in get method
"data":get_committees
this get_committees is a list and you are trying to return a list but you can only return json, and so you are getting the message.
You can do this in a better way
define your serializer as -
class MyCommitteesSerializer(serializers.ModelSerializer):
class Meta:
model = Committee
fields = ""_all__"
and define your views as -
class MyCommitteesView(generics.ListAPIView):
serializer_class = MyCommitteesSerializer
def get_queryset(self):
queryset = Committee.objects.filter(user=Profile.objects.get(id=self.request.user_id))
return queryset
the serializer will take care of serialization and you can customize this according to your needs.
or else you will have to manually convert your get_committes into json format before returning.

how to get validated data from serializers to views in django rest framework

I am returning some data from def create method in seriaizers.py file I want to acccess that data in views.py I dont know how to get it.
serilizers.py
def create(self, validated_data):
//returning something
return validated_data
views.py
if serializer.is_valid():
serializer.save()
//I want to return that validated_data here so how to get it here here
Response(validated_data)
To get the validated data, use serializer.validated_data. See the Deserializing Objects section of the manual.
Also, I want to point out a potential footgun here: you're supposed to return a model instance, from the create() method, rather than the validated data.

Django REST serializer required field

I have a simple serializer with one required field:
class MySerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__'
read_only_fields = ('field1', 'field2')
In my model there is an 'url' field which is required to create new object (method: POST). I would like to set required: False for PUT method. How can I achieve that? Thanks for any clues...
I assume you want to change/set one or multiple fields of an existing MyModel instance.
In such case, you need to pass a partial=True keyword argument to serializer. Then even if you PUT or PATCH without url field in data, your serializer.is_valid() would evaluate to True.
https://www.agiliq.com/blog/2019/04/drf-polls/#edit-a-poll-question should help if my assumption about your question is correct.
I found this answer helpful: Django Rest Framework set a field read_only after record is created .
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance is not None:
self.fields.get('url').read_only = True
This code works fine.

creating two models in one serializer of django rest framework

During registration of a user I would like to have both a User object and a EmailContact object created in one api call. The two objects should not be linked.
I have the following serializer:
class RegistrationSerializer(serializers.Serializer):
userserializer=UserAccountSerializer() #reuse existing modelserializer
emailcontactserializer=EmailContactSerializer() #reuse existing modelserializer
def create(self, validated_data):
emailcontact_data = validated_data.pop('emailcontactserializer')
user_data = validated_data.pop('userserializer')
emailcontact= EmailContact.objects.create(**emailcontact_data)
user= User.objects.create(**user_data)
return user
and the following Apiview:
class RegistrationAPIView(APIView):
permission_classes = (AllowAny,)
serializer_class = RegistrationSerializer
def post(self, request):
serializer = self.serializer_class(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)
The error I get is the following (occurs after the serializer.save()):
AttributeError at /api/register
Got AttributeError when attempting to get a value for field userserializer on serializer RegistrationSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'userserializer'.
In your RegistrationSerializer.create() method, you're returning a User object. The serializer will try to serialize that object into this representation:
{
'userserializer': x,
'emailcontactserializer': y
}
But it's complaining because the User you returned doesn't have a userserializer field.
If you really want to return a User from this API call, you could make your RegistrationSerializer a ModelSerializer with Meta.model=User, and override perform_create to pop out the emailcontact_data. (I'd name the field something like RegistrationSerializer.email_contact to make the representation clearer, IMO the phrase "serializer" shouldn't be present on the client-visible portion of the API).
Alternatively, if you want to render both of your sub-serializers, you can create a RegistrationSerializer instance in RegistrationSerializer.create by passing in the data, something like
return RegistrationSerializer(data={'emailcontactserializer':
emailcontact_data, 'userserializer': user_data})

Resources