client.post is not passing the FK - django

Hope someone can help me out here, I am trying to test a post method on an API, but the post method is not working as expected.
When I pass the payload to the post method, the points and de code fields are loaded but the shopper and card fields, which are both Foreing Keys, are not loaded and a null value is passed, as they are required fields I ended up getting an error.
I did check, and the values for self.shopper.pk and self.card.pk are correct.
MYCARDS_URL = reverse('mycards:mycards-list')
def test_create_mycards(self):
"""Test creating a mycards"""
payload = {
'shopper': self.shopper.pk,
'card': self.card.pk,
'points': 0,
'code': "code",
}
res = APIClient().post(MYCARDS_URL, payload)
I did check to see if was something related to my serializer, but it is all good as you can see:
class MycardsSerializer(serializers.ModelSerializer):
"""Serializer for cards."""
class Meta:
model = MyCards
fields = ['id', 'shopper', 'card', 'updated', 'created']
read_only_fields = ['id', 'created']
class MycardsDetailSerializer(MycardsSerializer):
"""Serializer for card detail view."""
class Meta(MycardsSerializer.Meta):
fields = MycardsSerializer.Meta.fields + [
'points', 'code']
Here is also my viewset which seems to be ok:
class MycardsViewSet(viewsets.ModelViewSet):
"""View for manage card APIs."""
serializer_class = serializers.MycardsDetailSerializer
queryset = MyCards.objects.all()
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Retrieve cards for authenticated user."""
shoppers = list(Shopper.objects.all().filter(
user=self.request.user).values_list('id'))
return self.queryset.filter(shopper=shoppers[0]).order_by('-id')
def get_serializer_class(self):
"""Return the serrializer class for request."""
if self.action == 'list':
return serializers.MycardsSerializer
return self.serializer_class
def perform_create(self, serializer):
"""Create a new recipe."""
serializer.save()
I have printed the serializer.dict from inside the perform_create method and got the following:
{'_args': (), '_kwargs': {'data': <QueryDict: {'shopper': ['5'], 'card': ['15'], 'points': ['0'], 'code': ['code']}>, 'context': {'request': <rest_framework.request.Request: POST '/api/mycards/'>, 'format': None, 'view': <mycards.views.MycardsViewSet object at 0x7f7ebd4ce400>}}, 'instance': None, 'initial_data': <QueryDict: {'shopper': ['5'], 'card': ['15'], 'points': ['0'], 'code': ['code']}>, 'partial': False, '_context': {'request': <rest_framework.request.Request: POST '/api/mycards/'>, 'format': None, 'view': <mycards.views.MycardsViewSet object at 0x7f7ebd4ce400>}, '_creation_counter': 198, 'read_only': False, 'write_only': False, 'required': True, 'default': <class 'rest_framework.fields.empty'>, 'source': None, 'initial': None, 'label': None, 'help_text': None, 'style': {}, 'allow_null': False, 'field_name': None, 'parent': None, 'error_messages': {'required': 'This field is required.', 'null': 'This field may not be null.', 'invalid': 'Invalid data. Expected a dictionary, but got {datatype}.'}, 'url_field_name': 'url', 'fields': {'id': IntegerField(label='ID', read_only=True), 'shopper': PrimaryKeyRelatedField(read_only=True), 'card': PrimaryKeyRelatedField(read_only=True), 'updated': DateTimeField(read_only=True), 'created': DateTimeField(read_only=True), 'points': IntegerField(max_value=2147483647, min_value=-2147483648, required=False), 'code': CharField(allow_blank=True, allow_null=True, max_length=6, required=False)}, '_validators': [], '_validated_data': OrderedDict([('points', 0), ('code', 'code')]), '_errors': {}}
here I could notice 2 things, first that the values are passed to the serializer as noted on the line below:
'shopper': ['5'], 'card': ['15'], 'points': ['0'], 'code': ['code']
but at the end of the serializer.dict I could find the following message:
'_validated_data': OrderedDict([('points', 0), ('code', 'code')]), '_errors':
points and code are going through, but the shopper and the card are not, I just have no idea why is it happening :/

Related

"Incorrect type. Expected pk value, received list." Error DRF React

Error when attempting post request Many to Many Relation
Question is
when I put list in request It shows an Error: "Incorrect type. Expected pk value, received list." .But it is m2m field so how can i
View
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = serializers.PostSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
parser_classes = (MultiPartParser, FormParser)
def post(self, request, *args, **kwargs):
print(request.data['categories'])
file_serializer = serializers.PostSerializer(data=request.data)
print(request.data.dict())
if file_serializer.is_valid():
print(request.data)
file_serializer.save(owner=self.request.user)
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Serializer
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
comments = serializers.PrimaryKeyRelatedField(many=True,queryset=Comment.objects.all())
categories = serializers.PrimaryKeyRelatedField(many=True,queryset=Category.objects.all())
class Meta:
model = Post
fields = ['id', 'title', 'body','owner','notify_users' ,'comments', 'categories','image']
Request I send
{
'title': 'my demo Title',
'body': 'it is my demo /nCode for the desc for body',
'categories': ['1', '2', '3', '4', '5', '6', '7'],
'image': image
}
No error with this Code
{
'title': 'my demo Title',
'body': 'it is my demo /nCode for the desc for body',
'categories': '1',
'image': image
}
GitHub Repo
I am also facing the same issue when I try to add the owner field manually in request.data.
my serializer class
fields = ("file", "name", "size", "owner")
in my views
request.data['owner'] = request.user
file_serializer = FileSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
in my Model
owner = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
Here is some code that should help. m2m field cannot be created automatically. It has to be added to the object. Also you can see categories are being created one by one and not using bulk_create which is not recommended.
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
comments = serializers.PrimaryKeyRelatedField(many=True,queryset=Comment.objects.all())
categories = CategoriesSerializer(many=True)
class Meta:
model = Post
fields = ['id', 'title', 'body','owner','notify_users' ,'comments', 'categories','image']
def create(self, validated_data):
categories = validated_data.pop("categories", [])
post = Post.objects.create(**validated_data)
for category in categories:
post.categories.create(**category)
return post

Data Dissapearing in Django on validation

I am trying to create nested objects (documentregulation) when I create a Document object. To achieve this, I have overwritten the create method on the DocumentSerializer, as per Django Docs. However, when I attempt validated_data.pop('documentregulation_set'), it is empty, even when it is populated in the incoming request.data of my view. Is there something causing my incoming data to not be validated? How would I go about debugging this if so?
// serializers.py
class DocumentRegulationSerializer(serializers.ModelSerializer):
class Meta:
model = DocumentRegulation
fields = ('regulation',)
class DocumentSerializer(serializers.ModelSerializer):
documentregulation_set = DocumentRegulationSerializer(many=True, required=False)
class Meta:
model = Document
fields = ('documentregulation_set', 'id', 'name', 'file', 'text', 'uploaded_at')
extra_kwargs = {
'id': {'read_only': True},
'uploaded_at': {'read_only': True},
}
def create(self, validated_data):
documentregulation_set = validated_data.pop('documentregulation_set')
# create document first
document = Document.objects.create(**validated_data)
# serialize associated regulations
for documentregulation in documentregulation_set:
# get ID of newly-created document, use for relation
#documentregulation['regulation'] = documentregulation['id']
DocumentRegulation.objects.create(document=document, **documentregulation)
return document
//views.py
class DocumentView(generics.ListCreateAPIView):
def create(self, request):
#pprint(self.request.FILES['profile_pic'])
request.data['documentregulation_set'] = json.loads(request.data['documentregulation_set'])
request.data['documentdomain_set'] = json.loads(request.data['documentdomain_set'])
pprint(request.data)
document = DocumentSerializer(data=request.data)
if document.is_valid():
document.save()
return Response(document.data, status=status.HTTP_201_CREATED)
else:
return Response(document.errors, status=status.HTTP_400_BAD_REQUEST)
my incoming data (printed in request.data) looks like:
{'documentregulation_set': [{'label': 'Regulation 1',
'regulation': 2,
'value': 2},
{'label': 'Regulation 2',
'regulation': 4,
'value': 4}],
'file': <InMemoryUploadedFile: test.docx >,
'name': 'testing',
'text': 'test'}
but then my validated data prints out to be:
{'documentregulation_set': [],
'file': <InMemoryUploadedFile: test.docx >,
'name': 'testing',
'text': 'test'}
Problem seems to be with the validation of the documentregulation_set field in the DocumentSerializer serializer. But you can escape the validation in the Meta Class as:
class Meta:
model = Document
fields = '__all__'
extra_kwargs = {
'documentregulation_set': {'validators': []} # escape validation
}
If you need to write custom validators have a look at Writing custom validators
So the final serializer looks like:
class DocumentRegulationSerializere(serializers.ModelSerializer):
"""Serializers for DocumentRegulation object"""
class Meta:
model = DocumentRegulation
fields = ('regulation',)
class DocumentSerializer(serializers.ModelSerializer):
"""Serializer for Document objects"""
documentregulation_set = DocumentRegulationSerializere(many=True)
class Meta:
model = Document
fields = ('documentregulation_set', 'id', 'name', 'file', 'text', 'uploaded_at')
extra_kwargs = {
'id': {'read_only': True},
'uploaded_at': {'read_only': True},
'documentregulation_set': {'validators': []} # escape validation
}
def create(self, validated_data):
doc_reg = []
document_regulation_set = validated_data.pop('documentregulation_set')
document = Document.objects.create(**validated_data)
for document_regulation in document_regulation_set:
reg = DocumentRegulation.objects.create(**document_regulation)
reg.save()
doc_reg.append(reg)
document.documentregulation_set.add(reg)
return document
View
class DocumentView(generics.ListCreateAPIView):
"""List or create new Document ojects"""
queryset = Document.objects.all()
serializer_class = DocumentSerializer

Save multiple models in a single post - Django rest frameweok

I have 4 models
class User(AbstractEmailUser):
first_name = models.CharField(max_length=100, blank=True)
last_name = models.CharField(max_length=100, blank=True)
class Event(models.Model):
name = models.CharField(max_length=200)
address = models.CharField(max_length=200)
date = models.DateField()
class EventLocation(models.Model):
event = models.ForeignKey(Event)
ubigeo = ArrayField(models.CharField(max_length=200), blank=True)
class EventStaff(models.Model):
recycler = models.ForeignKey(User)
event = models.ForeignKey(Event)
When I want to register an event and be able to assign users to this same publication at the time of creation, assign users or do not assign them. I have already created a nested serialier that in the documentation is well explained so that the event is saved and at the same time it is saved in the ubigeo field of the EventLocation table (code of the district of the place):
Class EventLocationSerializer(serializers.ModelSerializer):
class Meta:
model = EventLocation
fields = ('id', 'ubigeo')
class EventSerializer(serializers.ModelSerializer):
event_location = EventLocationSerializer(required=True, write_only=True)
def to_representation(self, instance):
representation = super(EventSerializer, self).to_representation(instance)
event_location = EventLocation.objects.filter(event=instance.id)
if event_location:
representation['event_location'] = event_location.values('ubigeo')[0]
return representation
class Meta:
model = Event
fields = ('id', 'date', 'name', 'address', 'schedule', 'event_location')
def create(self, validated_data):
location_data = validated_data.pop('event_location')
event = Event.objects.create(**validated_data)
EventLocation.objects.create(event=event, **location_data)
return event
and it works correctly, but how would you add the users you want to assign to the event at the same time? I know I have to save them in the EventStaff table but how do I insert them in that same post?
This is my viewset:
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)
This is the json format:
{
"date": "2018-03-01",
"name": "La prueba reciclaje",
"address": "Av espaƱa trujillo",
"users": [
{"id": 40, "first_name": "Raul"},
{"id": 23, "first_name": "ALejandro"}
],
"eventlocation": {
"ubigeo": ["130101"]
}
}
In my opinion, we can custom your def create a bit more.
So we create one Serializer for User, get params user and save it after Event saved.
Maybe like this:
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
// recheck , this loop have input is all users in json
for user in data.get('users'):
user_serializer = UserSerializer(data=user)
if user_serializer.is_valid(raise_exception=True):
user_serializer.save()
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)
Hoop this help
As I said in the commentary, it works wonderfully :D
#transaction.atomic
def create(self, request, *args, **kwargs):
with transaction.atomic():
try:
data = request.data
users = request.data.get('users', None)
serializer = EventSerializer(data=data)
if serializer.is_valid(raise_exception=True):
instance = serializer.save()
if users:
for user in users:
EventStaff.objects.create(recycler_id=user['id'], event_id=instance.id)
return Response({"status": True, "results": "Evento registrado correctamente"},
status=status.HTTP_201_CREATED)
except ValidationError as err:
return Response({"status": False, "error_description": err.detail}, status=status.HTTP_400_BAD_REQUEST)

Django Forms API - Forms Returning Blank

I am not getting any data back through the form. I'm obviously missing something but I don't know what.
I'm following this documentation.
forms.py
class EventRegistrationForm(forms.Form):
tags = forms.TextInput()
class Meta:
model = Event
fields = [
'name', 'description', 'type', 'map_pin', 'location_id', 'location_name', 'address1',
'address2', 'city', 'state', 'zip', 'country', 'admission_fee', 'free_admission', 'online_ticket_url',
'available_parking', 'nearby_transit', 'stream', 'kid_friendly', 'no_underage', 'website_url', 'opportunities'
]
views.py
def save_event(request):
if request.is_ajax() and request.method == "POST":
main_form_data = json.loads(request.POST.get('mainForm'))
print(main_form_data)
main_form = EventRegistrationForm(main_form_data)
if main_form.is_valid():
print("Form is incorrectly returning as valid")
print(main_form.cleaned_data)
output:
{'online_ticket_url': '', 'state': '', 'city': '', 'stream': '', 'name': 'Test Subject', 'description': '', 'type': None, 'no_underage': 'on', 'map_pin': '', 'available_parking': '', 'opportunities': '', 'address': '', 'free_admission': 'on', 'kid_friendly': 'on', 'nearby_transit': '', 'tags': '', 'location_name': '', 'admission_fee': '', 'website_url': '', 'zip': '', 'location_id': '', 'country': ''}
Form is incorrectly returning as valid
{}
Would appreciate some help if anyone can tell where I'm getting tripped up.
EDIT: This is what I get in the shell. I think I am misunderstanding how these are supposed to work:
>>> a = EventRegistrationForm({"name":"Adam"})
>>> a.is_valid()
True
>>> a.cleaned_data
{}
You have defined a lot of your fields in your model, blank=True, that's why your form is returning valid when no value is provided. If you read the documentation, blank=True allows the fields to be blank in ModelForms.
You could override in your __init__() method and set required=True,
class EventRegistrationForm(forms.Form):
tags = forms.TextInput()
class Meta:
model = Event
fields = [
'name', 'description', 'type', 'map_pin', 'location_id', 'location_name', 'address1',
'address2', 'city', 'state', 'zip', 'country', 'admission_fee', 'free_admission', 'online_ticket_url',
'available_parking', 'nearby_transit', 'stream', 'kid_friendly', 'no_underage', 'website_url', 'opportunities'
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].required = True
I solved it. For some reason, this form was set up using a standard form (forms.Form) instead of a model form (forms.ModelForm). Therefore, none of my fields were being registered...
class EventRegistrationForm(forms.ModelForm):
....

Django Rest Framework - accessing nested json object

I'm providing JSON to a view like so -
{
"username": "name",
"first_name": "john",
"last_name": "doe",
"email": "jdoe#hotmail.com",
"profile": {
"company": "abc corp"
}
}
I'm then passing it into the following view to post -
def post(self, request, format=None):
uuid = generate_uuid(request.data.get('username'))
data = {'username': request.data.get('username'),
'first_name': request.data.get('first_name'),
'last_name': request.data.get('last_name'),
'email': request.data.get('email'),
'company': request.data.get('profile').('company'),
'uuid': str(uuid)}
serializer = UserSerializer(data=data)
if serializer.is_valid():
print serializer.data
# serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
I'm having difficulty in understanding how to structure the data to maintain the nested value as you can see I'm also including a field for a uuid which will also be in the nested profile object.
These are then being passed into my serializer here -
class UserSerializer(serializers.ModelSerializer):
profile = UserProfileSerializer()
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email', 'profile')
My view currently as it is gives a syntax error on this line -
'company': request.data.get('profile').('company'),
I know it's wrong just not sure how it should be structured.
Here is the correct version of this line:
'company': request.data.get('profile', dict()).get('company'),
What this does is supply an empty dict as a default argument if the data is missing a 'profile' object, or if it has one, call get('company') on that.