Repository: voxel51/fiftyone
Issue: FiftyOne barfs when a dataset name is `None`

After performing the steps in https://github.com/voxel51/fiftyone/issues/204, I ended up with a non-persistent dataset whose name was `None` in the DB.

In this state, I cannot `import fiftyone`, because `delete_non_persistent_datasets()` barfs when trying to flush the dataset. Stack trace below:

```
In [1]: import fiftyone as fo                                                                                      
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-8c082ccbb59f> in <module>
----> 1 import fiftyone as fo

~/dev/fiftyone/fiftyone/__init__.py in <module>
     31 __path__ = extend_path(__path__, __name__)
     32 
---> 33 from fiftyone.__public__ import *

~/dev/fiftyone/fiftyone/__public__.py in <module>
     12 import fiftyone.core.service as fos
     13 
---> 14 _database_service = fos.DatabaseService()
     15 config = foc.load_config()
     16 

~/dev/fiftyone/fiftyone/core/service.py in __init__(self)
     57         self.child = None
     58         if not self._is_server:
---> 59             self.start()
     60 
     61     def __del__(self):

~/dev/fiftyone/fiftyone/core/service.py in start(self)
    122         import fiftyone.core.dataset as fod
    123 
--> 124         fod.delete_non_persistent_datasets()
    125 
    126     @staticmethod

~/dev/fiftyone/fiftyone/core/dataset.py in delete_non_persistent_datasets()
    142     """Deletes all non-persistent datasets."""
    143     for dataset_name in list_dataset_names():
--> 144         dataset = load_dataset(dataset_name)
    145         if not dataset.persistent:
    146             dataset.delete()

~/dev/fiftyone/fiftyone/core/dataset.py in load_dataset(name)
     94         ValueError: if no dataset exists with the given name
     95     """
---> 96     return Dataset(name, _create=False)
     97 
     98 

~/dev/fiftyone/fiftyone/core/singleton.py in __call__(cls, name, _create, *args, **kwargs)
     49         ):
     50             instance = cls.__new__(cls, name)
---> 51             instance.__init__(name, _create=_create, *args, **kwargs)
     52             cls._instances[name] = instance
     53 

~/dev/fiftyone/fiftyone/core/dataset.py in __init__(self, name, persistent, _create)
    176             )
    177         else:
--> 178             self._meta, self._sample_doc_cls = _load_dataset(name)
    179 
    180     def __len__(self):

~/dev/fiftyone/fiftyone/core/dataset.py in _load_dataset(name)
   1855         raise DoesNotExistError("Dataset '%s' not found" % name)
   1856 
-> 1857     _sample_doc_cls = type(name, (foo.ODMDatasetSample,), {})
   1858 
   1859     num_default_fields = len(_sample_doc_cls.get_field_schema())

TypeError: type.__new__() argument 1 must be str, not None
```

Changed files:
fiftyone/core/dataset.py, fiftyone/core/cli.py, fiftyone/core/odm/dataset.py

diff --git a/fiftyone/core/cli.py b/fiftyone/core/cli.py
index aef11d4a3..727d0de92 100644
--- a/fiftyone/core/cli.py
+++ b/fiftyone/core/cli.py
@@ -287,7 +287,7 @@ class DatasetsListCommand(Command):
         datasets = fod.list_datasets()
 
         if datasets:
-            for dataset in sorted(datasets):
+            for dataset in datasets:
                 print(dataset)
         else:
             print("No datasets found")
diff --git a/fiftyone/core/dataset.py b/fiftyone/core/dataset.py
index 18647ba33..35ff76db6 100644
--- a/fiftyone/core/dataset.py
+++ b/fiftyone/core/dataset.py
@@ -155,15 +155,15 @@ class Dataset(foc.SampleCollection, metaclass=DatasetSingleton):
     Args:
         name (None): the name of the dataset. By default,
             :func:`get_default_dataset_name` is used
-        persistent (False): whether the dataset will persist in the database
-            once the session terminates
+        persistent (False): whether the dataset should persist in the database
+            after the session terminates
     """
 
     # Batch size used when commiting samples to the database
     _BATCH_SIZE = 128
 
     def __init__(self, name=None, persistent=False, _create=True):
-        if name is None:
+        if name is None and _create:
             name = get_default_dataset_name()
 
         if _create:
@@ -223,8 +223,13 @@ class Dataset(foc.SampleCollection, metaclass=DatasetSingleton):
 
     @name.setter
     def name(self, name):
-        self._doc.name = name
-        self._doc.save()
+        _name = self._doc.name
+        try:
+            self._doc.name = name
+            self._doc.save()
+        except:
+            self._doc.name = _name
+            raise
 
     @property
     def persistent(self):
@@ -235,8 +240,13 @@ class Dataset(foc.SampleCollection, metaclass=DatasetSingleton):
 
     @persistent.setter
     def persistent(self, value):
-        self._doc.persistent = value
-        self._doc.save()
+        _value = self._doc.persistent
+        try:
+            self._doc.persistent = value
+            self._doc.save()
+        except:
+            self._doc.persistent = _value
+            raise
 
     @property
     def info(self):
@@ -657,6 +667,9 @@ class Dataset(foc.SampleCollection, metaclass=DatasetSingleton):
         Returns:
             a :class:`Dataset`
         """
+        if name is None:
+            name = get_default_dataset_name()
+
         if dataset_exists(name):
             raise ValueError("Dataset '%s' already exists" % name)
 
diff --git a/fiftyone/core/odm/dataset.py b/fiftyone/core/odm/dataset.py
index 261e881b5..4994ae220 100644
--- a/fiftyone/core/odm/dataset.py
+++ b/fiftyone/core/odm/dataset.py
@@ -96,8 +96,8 @@ class DatasetDocument(Document):
 
     meta = {"collection": "datasets"}
 
-    name = StringField(unique=True)
-    sample_collection_name = StringField(unique=True)
+    name = StringField(unique=True, required=True)
+    sample_collection_name = StringField(unique=True, required=True)
     persistent = BooleanField(default=False)
     info = DictField(default=dict)
     sample_fields = EmbeddedDocumentListField(