1

I'm using Angular 6 and Django REST Framework

The view of DRF is

class AmountGivenViewSet(viewsets.ModelViewSet):
    serializer_class = AmountGivenSerializer
    permission_classes = (IsAuthenticated, AdminAuthenticationPermission,)

    def get_queryset(self):
        return AmountGiven.objects.filter(
            contact__user=self.request.user
        )

    def perform_create(self, serializer):
        save_data = {}

        print(self.request.POST)
        # validate user and to save_data dictionary
        contact_pk = self.request.POST.get('contact', None)
        print(contact_pk)
        if not contact_pk:
            raise ValidationError({'contact': ['Contact is required']})

        contact = Contact.objects.filter(
            user=self.request.user,
            pk=contact_pk
        ).first()

        if not contact:
            raise ValidationError({'contact': ['Contact does not exists']})

        # add contact to save_data dictionary
        save_data['contact'] = contact

        # process mode_of_payment is in request
        mode_of_payment_pk = self.request.POST.get('mode_of_payment', None)

        if mode_of_payment_pk:
            mode_of_payment = ModeOfPayment.objects.get(pk=mode_of_payment_pk)
            if not mode_of_payment:
                raise ValidationError({'mode_of_payment': ['Not a valid mode of payment']})

            # add mode_of_payment to save_data dictionary
            save_data['mode_of_payment'] = mode_of_payment

        # pass save_data dictionary to save()
        serializer.save(**save_data)

AmountGivenSerializer in serializers.py

class AmountGivenSerializer(serializers.ModelSerializer):
    class Meta:
        model = AmountGiven
        depth = 1
        fields = (
            'id', 'contact', 'amount', 'interest_rate', 'duration', 'given_date', 'promised_return_date',
            'mode_of_payment', 'transaction_number', 'interest_to_pay', 'total_payable', 'amount_due',
            'total_returned', 'comment', 'modified', 'created'
        )

and in Angular component

import { Component, OnInit } from '@angular/core';
import {FormBuilder, FormControl, FormGroup, Validators} from '@angular/forms';
import {AmountGiven} from '../amount-given.model';
import {AmountGivenService} from '../amount-given.service';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-amount-given-add',
  templateUrl: './amount-given-add.component.html',
  styleUrls: ['./amount-given-add.component.css']
})
export class AmountGivenAddComponent implements OnInit {

  addMoneyForm: FormGroup;
  submitted = false;
  contact_id: string;

  amountGiven: AmountGiven;

  constructor(
    private formBuilder: FormBuilder,
    private amountGivenService: AmountGivenService,
    private route: ActivatedRoute
  ) { }

  ngOnInit(): void {

    this.route.params.subscribe(
      param => {
        this.contact_id = param['contact_id'];
      }
    );

    this.addMoneyForm = this.formBuilder.group({
      amount: new FormControl('', [
        Validators.required
      ]),
      interest_rate: new FormControl(),
      duration: new FormControl(),
      given_date: new FormControl(),
      promised_return_date: new FormControl(),
      transaction_number: new FormControl(),
      mode_of_payment: new FormControl(),
      comment: new FormControl()
    });
  }

  get f() {
    return this.addMoneyForm.controls;
  }

  onSubmit() {
    this.submitted = true;

    // stop here if form is invalid
    if (this.addMoneyForm.invalid) {
      console.log('invalid');
      return;
    }

    const data = this.addMoneyForm.value;
    data.contact = this.contact_id;

    this.amountGivenService.add(data).subscribe(res => {
      console.log('req completed', res);
    });
  }

}

But when form is submited it returns validation error from server for contact field.

{"contact":["Contact is required"]}

The request header has the contact parameter

enter image description here

sending the same request from Postman is working fine. The Javascript XHR code of Postman request is

var data = new FormData();
data.append("amount", "5000");
data.append("contact", "65827a1f-003e-4bb3-8a90-6c4321c533e6");

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "https://koober-production.herokuapp.com/api/amount-given/");
xhr.setRequestHeader("Authorization", "Bearer ATjIuQ6hLzc55wHaXIzHmcKafEzk1B");
xhr.setRequestHeader("Cache-Control", "no-cache");
xhr.setRequestHeader("Postman-Token", "28d9d33a-f0a6-431c-8936-da4f6565ece4");

xhr.send(data);

could not understand this issue is related to Angular or Django because Postman request is working fine with Djano and Angular is sending the request in the parameter.

Edit 2: AmountGivenService

import { Injectable } from '@angular/core';
import {ResourceProviderService} from '../../resource-provider.service';
import {AuthService} from '../../auth/auth.service';
import {Observable, of} from 'rxjs';
import {AmountGiven} from './amount-given.model';
import {AppHttpClient} from '../../app-http-client';

@Injectable({
  providedIn: 'root'
})
export class AmountGivenService {

  private url = 'amount-given/';

  constructor(
    private http: AppHttpClient
  ) { }

  add(data): Observable<AmountGiven> {

    return this.http.Post<AmountGiven>(this.url, data);
  }

}

which is furhter using AppHttpClient

import { Injectable } from '@angular/core';
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
import {Observable, of} from 'rxjs';
import {catchError} from 'rxjs/operators';
import {ResourceProviderService} from './resource-provider.service';

export interface IRequestOptions {
  headers?: HttpHeaders;
  observe?: 'body';
  params?: HttpParams;
  reportProgress?: boolean;
  responseType?: 'json';
  withCredentials?: boolean;
  body?: any;
}

export function appHttpClientCreator(http: HttpClient, resource: ResourceProviderService) {
  return new AppHttpClient(http, resource);
}

@Injectable({
  providedIn: 'root'
})

export class AppHttpClient {

  private api_url = this.resource.url + '/api/';

  constructor(
    public http: HttpClient,
    private resource: ResourceProviderService
  ) {
  }

  public Post<T>(endPoint: string, params: Object, options?: IRequestOptions): Observable<T> {
    return this.http.post<T>(this.api_url + endPoint, params, options)
      .pipe(
        catchError(this.handleError('post', endPoint, null))
      );
  }

  private handleError<T> (operation = 'operation', endpoint = '', result?: T) {
    return (error: any): Observable<T> => {
      console.error(error);
      console.log(error.message);
      console.log(operation);
      console.log(endpoint);

      return of(result as T);
    };
  }
}
3
  • Can we see the code for the AmountGivenService? Commented Aug 15, 2018 at 17:57
  • Hi Anuj, you may need to add the following to your form element (I'm not sure on the Angular way of doing this), but I know Django expects x-www-form-urlencoded, so depending on how Angular handles this you'll need somethign that replicates: <form method="POST" action="url/to/your-DRF-endpoint" enctype="application/x-www-form-urlencoded"> Commented Aug 15, 2018 at 18:02
  • @R.Richards update question with requested code. Commented Aug 15, 2018 at 18:09

2 Answers 2

1

The problem is that you're trying to access:

self.request.POST.get('contact', None)

in DRF you need to do:

self.request.data.get('contact', None)

It works from postman because you're constructing a FormData object which is fundamentally different from the JSON body angular will send and that is expected by a RESTful API like DRF.

from a more general standpoint, in DRF, serializers should perform the request validation using the built in validators, rather than manually validating in the view.

Sign up to request clarification or add additional context in comments.

1 Comment

While connecting android through retrofit to drf request.POST works fine but why it's not working with angular why have we have to use request.data instead ??
0

I'm hesitant to add this as the answer just yet, but it feels like you need to add something along the lines of:

<form method="POST" action="url/to/your-DRF-endpoint" enctype="application/x-www-form-urlencoded">
 ....
</form>

Either within your template or within your Angular POST request code you need to make sure the data is posted as application/x-www-form-urlencoded.

Essentially, Django expects application/x-www-form-urlencoded, and without it your self.request.POST object will be empty and hence self.request.POST.get('contact', None) will be empty and default to None.

1 Comment

Django expects a standard form, DRF expects a JSON body as angular will send, the issue is how he's attempting to access the data in the view.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.