Repository: akvo/akvo-rsr

Issue:
POSTing to /rest/v1/crs_add/ raises a FieldError
https://sentry.io/organizations/akvo-foundation/issues/1252660667/?project=218288

```python_traceback
FieldError: Cannot resolve keyword 'id' into field. Choices are: channel_code, commitment_date, interest_arrears, interest_received, loan_status_currency, loan_status_value_date, loan_status_year, loan_terms_rate1, loan_terms_rate2, other_flags, principal_arrears, principal_outstanding, project, project_id, repayment_final_date, repayment_first_date, repayment_plan, repayment_type
  File "django/core/handlers/base.py", line 185, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "rest_framework/viewsets.py", line 116, in view
    return self.dispatch(request, *args, **kwargs)
  File "rest_framework/views.py", line 495, in dispatch
    response = self.handle_exception(exc)
  File "rest_framework/views.py", line 455, in handle_exception
    self.raise_uncaught_exception(exc)
  File "rest_framework/views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "akvo/rest/viewsets.py", line 150, in create
    obj = self.queryset.model.objects.get(id=response.data['id'])
  File "django/db/models/manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "django/db/models/query.py", line 371, in get
    clone = self.filter(*args, **kwargs)
  File "django/db/models/query.py", line 787, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "django/db/models/query.py", line 805, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "django/db/models/sql/query.py", line 1250, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "django/db/models/sql/query.py", line 1276, in _add_q
    allow_joins=allow_joins, split_subq=split_subq,
  File "django/db/models/sql/query.py", line 1154, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg)
  File "django/db/models/sql/query.py", line 1034, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
  File "django/db/models/sql/query.py", line 1352, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))
```


Discussion:

Resolving patch:
diff --git a/akvo/rest/viewsets.py b/akvo/rest/viewsets.py
--- a/akvo/rest/viewsets.py
+++ b/akvo/rest/viewsets.py
@@ -172,7 +172,7 @@ def get_queryset(self):
 
     def create(self, request, *args, **kwargs):
         response = super(PublicProjectViewSet, self).create(request, *args, **kwargs)
-        obj = self.queryset.model.objects.get(id=response.data['id'])
+        obj = self.queryset.model.objects.get(pk=response.data['id'])
         project = self.get_project(obj)
         if project is not None:
             project.update_iati_checks()
diff --git a/akvo/utils.py b/akvo/utils.py
--- a/akvo/utils.py
+++ b/akvo/utils.py
@@ -530,7 +530,7 @@ def log_project_changes(user, project, related_obj, data, action):
     if not isinstance(related_obj, Project):
         obj_name = related_obj._meta.model_name
         if action_flag in {ADDITION, DELETION}:
-            project_fields = {'name': obj_name, 'object': related_obj.id}
+            project_fields = {'name': obj_name, 'object': related_obj.pk}
         else:
             project_fields = {
                 'fields': ['{}_{}'.format(obj_name, key) for key in data.keys()]


Test patch:
diff --git a/akvo/rsr/tests/rest/test_crs_add.py b/akvo/rsr/tests/rest/test_crs_add.py
new file mode 100644
--- /dev/null
+++ b/akvo/rsr/tests/rest/test_crs_add.py
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+
+# Akvo RSR is covered by the GNU Affero General Public License.
+
+# See more details in the license.txt file located at the root folder of the Akvo RSR module.
+# For additional details on the GNU license please see < http://www.gnu.org/licenses/agpl.html >.
+
+
+from akvo.rsr.models import CrsAdd
+from akvo.rsr.tests.base import BaseTestCase
+
+
+class CrsAddViewSetTestCase(BaseTestCase):
+
+    def setUp(self):
+        super(CrsAddViewSetTestCase, self).setUp()
+        email = password = 'foo@example.com'
+        self.user = self.create_user(email, password, is_superuser=True)
+        self.c.login(username=email, password=password)
+
+    def test_get(self):
+        # Given
+        project = self.create_project('Test Project')
+        crs_add = CrsAdd.objects.create(project=project, loan_terms_rate1=5)
+        url = '/rest/v1/crs_add/{}?format=json'.format(crs_add.pk)
+
+        # When
+        response = self.c.get(url, follow=True)
+
+        # Then
+        self.assertEqual(response.status_code, 200)
+        self.assertEqual(response.data['project'], project.pk)
+        self.assertEqual(float(response.data['loan_terms_rate1']), crs_add.loan_terms_rate1)
+
+    def test_post(self):
+        # Given
+        project = self.create_project('Test Project')
+        url = '/rest/v1/crs_add/?format=json'
+        data = {'project': project.id}
+
+        # When
+        response = self.c.post(url, data=data)
+
+        # Then
+        self.assertEqual(response.status_code, 201)
+        self.assertEqual(response.data['project'], project.pk)