Repository: getmoto/moto

Issue:
Dynamodb has_item causes scan problems
Calling `has_item` for an item that doesn't exist, and then calling `scan()` causes `AttributeError: 'dict' object has no attribute 'attrs'`

Example of behaviour that causes this:

``` python
from moto import mock_dynamodb
import boto


with mock_dynamodb():
    conn = boto.connect_dynamodb()
    table = conn.create_table(
        name='mytable',
        schema=conn.create_schema(
            hash_key_name='name',
            hash_key_proto_value=str,
        ),
        read_units=1,
        write_units=1
    )
    print(list(table.scan()))
    table.has_item('something')
    print(list(table.scan()))
```

`has_item` calls down to `Table.get_item` in `dynamodb/models.py`. This accesses `self.items`, which is a `defaultdict` whose default value is an empty dict. This results in an empty dict being set in `self.items`. The `scan()` call in `dynamodb/responses.py` does not handle dicts (or anything other than an Item)

Replacing the error line in `dynamodb/responses.py` with the following code appears to work:

``` python
"Items": [item.attrs for item in items if item],
```

I'm not very familiar with this library (only started using it today) but happy to submit a PR if that seems like the right thing to do.



Discussion:
Yes, that sounds like the right solution. A PR would be great!


Resolving patch:
diff --git a/moto/dynamodb/responses.py b/moto/dynamodb/responses.py
--- a/moto/dynamodb/responses.py
+++ b/moto/dynamodb/responses.py
@@ -278,7 +278,7 @@ def scan(self):
 
         result = {
             "Count": len(items),
-            "Items": [item.attrs for item in items],
+            "Items": [item.attrs for item in items if item],
             "ConsumedCapacityUnits": 1,
             "ScannedCount": scanned_count
         }


Test patch:
diff --git a/tests/test_dynamodb/test_dynamodb_table_with_range_key.py b/tests/test_dynamodb/test_dynamodb_table_with_range_key.py
--- a/tests/test_dynamodb/test_dynamodb_table_with_range_key.py
+++ b/tests/test_dynamodb/test_dynamodb_table_with_range_key.py
@@ -419,6 +419,17 @@ def test_scan_with_undeclared_table():
     ).should.throw(DynamoDBResponseError)
 
 
+@mock_dynamodb
+def test_scan_after_has_item():
+    conn = boto.connect_dynamodb()
+    table = create_table(conn)
+    list(table.scan()).should.equal([])
+
+    table.has_item(hash_key='the-key', range_key='123')
+
+    list(table.scan()).should.equal([])
+
+
 @mock_dynamodb
 def test_write_batch():
     conn = boto.connect_dynamodb()
diff --git a/tests/test_dynamodb/test_dynamodb_table_without_range_key.py b/tests/test_dynamodb/test_dynamodb_table_without_range_key.py
--- a/tests/test_dynamodb/test_dynamodb_table_without_range_key.py
+++ b/tests/test_dynamodb/test_dynamodb_table_without_range_key.py
@@ -335,6 +335,17 @@ def test_scan_with_undeclared_table():
     ).should.throw(DynamoDBResponseError)
 
 
+@mock_dynamodb
+def test_scan_after_has_item():
+    conn = boto.connect_dynamodb()
+    table = create_table(conn)
+    list(table.scan()).should.equal([])
+
+    table.has_item('the-key')
+
+    list(table.scan()).should.equal([])
+
+
 @mock_dynamodb
 def test_write_batch():
     conn = boto.connect_dynamodb()