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})
Related
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.
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.
I want to create a non class-based form to facilitate uniform logging in by users across all front-end apps. Currently, it looks like this
My serializer class:
class EmployeeLoginSerializer(serializers.Serializer):
username = serializers.CharField(min_length=6)
password = serializers.CharField(min_length=6)
def validate_credentials(self, attrs):
print('validating')
try:
emp: Employee = Employee.objects.get(username=attrs['username'])
if crypto.verify_password(attrs['password'], emp.password_hash):
return attrs
except Exception:
raise serializers.ValidationError("Incorrect username or password")
raise serializers.ValidationError('Incorrect username or password')
My view class:
class TestView(APIView):
serializer_class = EmployeeLoginSerializer
def get(self, request, *args, **kwargs):
return Response({'Message': 'Get works'})
def post(self, request, *args, **kwargs):
print(request.POST)
serializer = self.serializer_class(data=request.POST)
if serializer.is_valid():
return Response({'Message': 'Credentials were correct'})
My issue is that serializer.is_valid() doesn't seem to be calling on validate automatically. I know I could just call serializer.validate() manually but all the docs and questions on StackOverflow seem to show validate() being called by is_valid() automatically so I get that feeling that that wouldn't be the best practice. Is there something I'm missing?
The is_valid() method will call validate() method of the serializer and validate_FIELD_NAME() methods.
In your code, the validate_credentials() seems a regular class method which won't detect by DRF since the credentials isn't a field on the serializer.
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.
In order to have a non-readonly PrimaryKeyRelatedField, you are required to provide a queryset that contains valid options.
How can I properly populate that queryset based on the current request (user)?
The key is to subclass PrimaryKeyRelatedField and overload the get_queryset method, using the user information from the request context:
class UserFilteredPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
request = self.context.get('request', None)
queryset = super(UserFilteredPrimaryKeyRelatedField, self).get_queryset()
if not request or not queryset:
return None
return queryset.filter(user=request.user)
You can then use this new serializer just like the (unfiltered) original:
class MySerializer(serializers.ModelSerializer):
related = UserFilteredPrimaryKeyRelatedField(queryset=MyModel.objects)
Whenever the serializer accesses the queryset, it will be filtered such that only objects owned by the current user are returned.
View has a
self.request.user
attribute which you can then use to fetch user related queryset
eg
queryset = Products.objects.get(customer=self.request.user)