Repository: deis/deis

Issue:
500 error on deploy after reg error
I think after re-registering with an existing cookie (see issue #78), you can get duplicate flavors created:

```
==> /var/log/deis/server.log <==
2013-08-09 10:17:39 [15266] [DEBUG] Closing connection. 
2013-08-09 10:17:39 [15269] [DEBUG] POST /api/formations/shadow-zoologer/layers
Internal Server Error: /api/formations/shadow-zoologer/layers
Traceback (most recent call last):
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/core/handlers/base.py", line 115, in get_response
    response = callback(request, *callback_args, **callback_kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/viewsets.py", line 78, in view
    return self.dispatch(request, *args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 77, in wrapped_view
    return view_func(*args, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 327, in dispatch
    response = self.handle_exception(exc)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/views.py", line 324, in dispatch
    response = handler(request, *args, **kwargs)
  File "/opt/deis/controller/api/views.py", line 260, in create
    return OwnerViewSet.create(self, request, **kwargs)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/mixins.py", line 50, in create
    if serializer.is_valid():
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 479, in is_valid
    return not self.errors
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 471, in errors
    ret = self.from_native(data, files)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 867, in from_native
    instance = super(ModelSerializer, self).from_native(data, files)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 317, in from_native
    attrs = self.restore_fields(data, files)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/serializers.py", line 243, in restore_fields
    field.field_from_native(data, files, field_name, reverted_data)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/relations.py", line 183, in field_from_native
    into[(self.source or field_name)] = self.from_native(value)
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/rest_framework/relations.py", line 299, in from_native
    return self.queryset.get(**{self.slug_field: data})
  File "/opt/deis/controller/venv/local/lib/python2.7/site-packages/django/db/models/query.py", line 393, in get
    (self.model._meta.object_name, num, kwargs))
MultipleObjectsReturned: get() returned more than one Flavor -- it returned 2! Lookup parameters were {u'id': u'ec2-us-west-2'}
```



Discussion:
The Thuzi guys ran into this while testing too. Let's get it fixed for the next release.


Resolving patch:
diff --git a/api/serializers.py b/api/serializers.py
--- a/api/serializers.py
+++ b/api/serializers.py
@@ -12,6 +12,17 @@
 from api import utils
 
 
+class OwnerSlugRelatedField(serializers.SlugRelatedField):
+    """Filter queries by owner as well as slug_field."""
+
+    def from_native(self, data):
+        """Fetch model object from its 'native' representation.
+        TODO: request.user is not going to work in a team environment...
+        """
+        self.queryset = self.queryset.filter(owner=self.context['request'].user)
+        return serializers.SlugRelatedField.from_native(self, data)
+
+
 class UserSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.User` model."""
 
@@ -57,7 +68,7 @@ class FlavorSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Flavor` model."""
 
     owner = serializers.Field(source='owner.username')
-    provider = serializers.SlugRelatedField(slug_field='id')
+    provider = OwnerSlugRelatedField(slug_field='id')
 
     class Meta:
         """Metadata options for a :class:`FlavorSerializer`."""
@@ -69,7 +80,7 @@ class ConfigSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Config` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
     values = serializers.ModelField(
         model_field=models.Config()._meta.get_field('values'), required=False)
 
@@ -83,7 +94,7 @@ class BuildSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Build` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
 
     class Meta:
         """Metadata options for a :class:`BuildSerializer`."""
@@ -95,7 +106,7 @@ class ReleaseSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Release` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
     config = serializers.SlugRelatedField(slug_field='uuid')
     build = serializers.SlugRelatedField(slug_field='uuid', required=False)
 
@@ -121,8 +132,8 @@ class LayerSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Layer` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
-    flavor = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
+    flavor = OwnerSlugRelatedField(slug_field='id')
 
     class Meta:
         """Metadata options for a :class:`LayerSerializer`."""
@@ -143,8 +154,8 @@ class NodeSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Node` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
-    layer = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
+    layer = OwnerSlugRelatedField(slug_field='id')
 
     class Meta:
         """Metadata options for a :class:`NodeSerializer`."""
@@ -156,8 +167,8 @@ class ContainerSerializer(serializers.ModelSerializer):
     """Serialize a :class:`~api.models.Container` model."""
 
     owner = serializers.Field(source='owner.username')
-    formation = serializers.SlugRelatedField(slug_field='id')
-    node = serializers.SlugRelatedField(slug_field='id')
+    formation = OwnerSlugRelatedField(slug_field='id')
+    node = OwnerSlugRelatedField(slug_field='id')
 
     class Meta:
         """Metadata options for a :class:`ContainerSerializer`."""
diff --git a/api/views.py b/api/views.py
--- a/api/views.py
+++ b/api/views.py
@@ -142,7 +142,7 @@ def create(self, request, **kwargs):
 
 
 class FormationViewSet(OwnerViewSet):
-    """RESTful views for :class:`~api.models.Formtion`."""
+    """RESTful views for :class:`~api.models.Formation`."""
 
     model = models.Formation
     serializer_class = serializers.FormationSerializer


Test patch: