- Move get_instance_data to the InstanceWriterMixin

- Add comments to the InstanceReaderMixin and InstanceWriterMixin classes
This commit is contained in:
Mjumbe Wawatu Poe 2012-08-25 19:35:06 -04:00
parent 2e7325910d
commit 8f4590e834

View File

@ -454,9 +454,6 @@ class ModelMixin(object):
If a *ModelMixin is going to retrive an instance (or queryset) using args and kwargs If a *ModelMixin is going to retrive an instance (or queryset) using args and kwargs
passed by as URL arguments, it should provied arguments to objects.get and objects.filter passed by as URL arguments, it should provied arguments to objects.get and objects.filter
methods wrapped in by `build_query` methods wrapped in by `build_query`
If a *ModelMixin is going to create/update an instance get_instance_data
handles the instance data creation/preaparation.
""" """
queryset = None queryset = None
@ -477,7 +474,104 @@ class ModelMixin(object):
return kwargs return kwargs
def get_instance_data(self, model, content, **kwargs): def get_queryset(self):
"""
Return the queryset for this view.
"""
return getattr(self.resource, 'queryset',
self.resource.model.objects.all())
def get_ordering(self):
"""
Return the ordering for this view.
"""
return getattr(self.resource, 'ordering', None)
class InstanceReaderMixin (object):
"""
Assume a single instance for the view. Caches the instance object on self.
"""
def get_instance(self):
"""
Return the instance for this view. Raises a DoesNotExist error if
instance does not exist.
"""
if not hasattr(self, 'model_instance'):
query_kwargs = self.get_query_kwargs(
self.request, *self.args, **self.kwargs)
self.model_instance = self.get_queryset().get(**query_kwargs)
return self.model_instance
def get_instance_or_404(self):
"""
Return the instance for this view, or raise a 404 response if the
instance is not found.
"""
model = self.resource.model
try:
return self.get_instance()
except model.DoesNotExist:
raise ErrorResponse(status.HTTP_404_NOT_FOUND)
class InstanceWriterMixin (object):
"""
Abstracts out the logic for applying many-to-many data to a single instance.
"""
def _separate_m2m_data_from_content(self, model, content):
"""
Split the view's CONTENT into atomic data and many-to-many data.
Retrns a pair of (non_m2m_data, m2m_data)
Arguments:
- model: model class (django.db.models.Model subclass) to work with
- content: a dictionary with instance data
"""
# Copy the dict to keep it intact
content = dict(content)
m2m_data = {}
for field in model._meta.many_to_many:
if field.name in content:
m2m_data[field.name] = (
field.m2m_reverse_field_name(), content[field.name]
)
del content[field.name]
non_m2m_data = content
return non_m2m_data, m2m_data
def _set_m2m_data(self, instance, m2m_data):
"""
Apply the many-to-many data to the given instance.
Arguments:
- instance: model instance to work with
- m2m_data: a mapping from fieldname to list of identifiers of related
objects
"""
for fieldname in m2m_data:
manager = getattr(instance, fieldname)
# If we are updating an existing model, we want to clear out
# existing relationships.
if hasattr(manager, 'clear'):
manager.clear()
if hasattr(manager, 'add'):
manager.add(*m2m_data[fieldname][1])
else:
data = {}
data[manager.source_field_name] = instance
for related_item in m2m_data[fieldname][1]:
data[m2m_data[fieldname][0]] = related_item
manager.through(**data).save()
def _get_instance_data(self, model, content, **kwargs):
""" """
Returns the dict with the data for model instance creation/update. Returns the dict with the data for model instance creation/update.
@ -504,118 +598,35 @@ class ModelMixin(object):
return all_kw_args return all_kw_args
def get_queryset(self): def create_instance(self):
""" """
Return the queryset for this view. Create the instance for this view.
""" """
return getattr(self.resource, 'queryset',
self.resource.model.objects.all())
def get_ordering(self):
"""
Return the ordering for this view.
"""
return getattr(self.resource, 'ordering', None)
class InstanceReaderMixin (object):
"""
Assume a single instance for the view. Caches the instance object on self.
"""
def get_instance(self):
if not hasattr(self, 'model_instance'):
query_kwargs = self.get_query_kwargs(
self.request, *self.args, **self.kwargs)
self.model_instance = self.get_queryset().get(**query_kwargs)
return self.model_instance
def get_instance_or_404(self):
model = self.resource.model model = self.resource.model
try: content, m2m_data = self._separate_m2m_data_from_content(model,
return self.get_instance() self.CONTENT)
except model.DoesNotExist: instance_data = self._get_instance_data(model, content,
raise ErrorResponse(status.HTTP_404_NOT_FOUND) *self.args, **self.kwargs)
class InstanceWriterMixin (object):
def _separate_m2m_data_from_content(self, model):
model = self.resource.model
# Copy the dict to keep self.CONTENT intact
content = dict(self.CONTENT)
m2m_data = {}
for field in model._meta.many_to_many:
if field.name in content:
m2m_data[field.name] = (
field.m2m_reverse_field_name(), content[field.name]
)
del content[field.name]
return content, m2m_data
def _set_m2m_data(self, instance, m2m_data):
for fieldname in m2m_data:
manager = getattr(instance, fieldname)
# If we are updating an existing model, we want to clear out
# existing relationships.
if hasattr(manager, 'clear'):
manager.clear()
if hasattr(manager, 'add'):
manager.add(*m2m_data[fieldname][1])
else:
data = {}
data[manager.source_field_name] = instance
for related_item in m2m_data[fieldname][1]:
data[m2m_data[fieldname][0]] = related_item
manager.through(**data).save()
class ReadModelMixin(ModelMixin, InstanceReaderMixin):
"""
Behavior to read a `model` instance on GET requests
"""
def get(self, request, *args, **kwargs):
instance = self.get_instance_or_404()
return instance
class CreateModelMixin(ModelMixin, InstanceWriterMixin):
"""
Behavior to create a `model` instance on POST requests
"""
def post(self, request, *args, **kwargs):
model = self.resource.model
content, m2m_data = self._separate_m2m_data_from_content(model)
instance_data = self.get_instance_data(model, content, *args, **kwargs)
instance = model(**instance_data) instance = model(**instance_data)
instance.save() instance.save()
self._set_m2m_data(instance, m2m_data) self._set_m2m_data(instance, m2m_data)
return instance
headers = {} def create_or_update_instance(self):
if hasattr(self.resource, 'url'): """
headers['Location'] = self.resource(self).url(instance) Update the instance for this view, or create it if it does not yet
return Response(status.HTTP_201_CREATED, instance, headers) exist. Assumes the view is also an InstanceReaderMixin
"""
class UpdateModelMixin(ModelMixin, InstanceReaderMixin, InstanceWriterMixin):
"""
Behavior to update a `model` instance on PUT requests
"""
def put(self, request, *args, **kwargs):
model = self.resource.model model = self.resource.model
instance_is_new = False instance_is_new = False
content, m2m_data = self._separate_m2m_data_from_content(model,
content, m2m_data = self._separate_m2m_data_from_content(model) self.CONTENT)
instance_data = self.get_instance_data(model, content, *args, **kwargs) instance_data = self._get_instance_data(model, content,
*self.args, **self.kwargs)
# TODO: update on the url of a non-existing resource url doesn't work # TODO: update on the url of a non-existing resource url doesn't work
# correctly at the moment - will end up with a new url # correctly at the moment - will end up with a new url
@ -632,17 +643,59 @@ class UpdateModelMixin(ModelMixin, InstanceReaderMixin, InstanceWriterMixin):
instance.save() instance.save()
self._set_m2m_data(instance, m2m_data) self._set_m2m_data(instance, m2m_data)
return (Response(status.HTTP_201_CREATED, instance) if instance_is_new return instance, instance_is_new
else instance)
def delete_instance(self):
"""
Delete the instance for this view. Assumes the view is also an
InstanceReaderMixin.
"""
instance = self.get_instance_or_404()
instance.delete()
class DeleteModelMixin(ModelMixin, InstanceReaderMixin): class ReadModelMixin(ModelMixin, InstanceReaderMixin):
"""
Behavior to read a `model` instance on GET requests
"""
def get(self, request, *args, **kwargs):
instance = self.get_instance_or_404()
return instance
class CreateModelMixin(ModelMixin, InstanceWriterMixin):
"""
Behavior to create a `model` instance on POST requests
"""
def post(self, request, *args, **kwargs):
instance = self.create_instance()
headers = {}
if hasattr(self.resource, 'url'):
headers['Location'] = self.resource(self).url(instance)
return Response(status.HTTP_201_CREATED, instance, headers)
class UpdateModelMixin(ModelMixin, InstanceReaderMixin, InstanceWriterMixin):
"""
Behavior to update a `model` instance on PUT requests
"""
def put(self, request, *args, **kwargs):
instance, instance_is_new = self.create_or_update_instance()
if instance_is_new:
return Response(status.HTTP_201_CREATED, instance)
else:
return instance
class DeleteModelMixin(ModelMixin, InstanceReaderMixin, InstanceWriterMixin):
""" """
Behavior to delete a `model` instance on DELETE requests Behavior to delete a `model` instance on DELETE requests
""" """
def delete(self, request, *args, **kwargs): def delete(self, request, *args, **kwargs):
instance = self.get_instance_or_404() self.delete_instance(instance)
instance.delete()
return return