Главная > Development > Простой REST для Django

Простой REST для Django

Для одного прототипа понадобилось сделать быстренько REST сервис для стандартного набора действий типа Create, Read, Update, Delete (CRUD).

Писать специальный код для этого не хотелось, поэтому мировой разум подсказал решение в виде django-rest-interface.


Сама по себе библиотечка проста и изящна. Единственно НО – у нее нет классического setup.py, поэтому через easy_install установить не получится.

Использовать сервис надо будет из Flex приложения, что тоже имеет особенности, а именно:

  • Flex плохо дружит с XML тэгами с символом «-» в названии. А корневой элемент xml сериализатора для REST такой символ содержит
  • Flex использует теги для обозначения полей, название тега – это название поля. Сериализатор для полей создает тег field с атрибутом name.

Для устранения этих проблем было сделано следующее:
Создаем свой сериализатор
Сериализатор был сделать на основе стандартного сериализатора xml из поставки Python:

"""
Flex serializer. Based on XML serializer.
"""
 
from django.conf import settings
from django.core.serializers import base
from django.db import models
from django.utils.xmlutils import SimplerXMLGenerator
from django.utils.encoding import smart_unicode
from xml.dom import pulldom
 
class Serializer(base.Serializer):
	"""
	Serializes a QuerySet to XML.
	"""
 
	def indent(self, level):
		if self.options.get('indent', None) is not None:
			self.xml.ignorableWhitespace('\n' + ' ' * self.options.get('indent', None) * level)
 
	def start_serialization(self):
		"""
		Start serialization -- open the XML document and the root element.
		"""
		self.xml = SimplerXMLGenerator(self.stream, self.options.get("encoding", settings.DEFAULT_CHARSET))
		self.xml.startDocument()
		self.xml.startElement("objects", {"version" : "1.0"})
 
	def end_serialization(self):
		"""
		End serialization -- end the document.
		"""
		self.indent(0)
		self.xml.endElement("objects")
		self.xml.endDocument()
 
	def start_object(self, obj):
		"""
		Called as each object is handled.
		"""
		if not hasattr(obj, "_meta"):
			raise base.SerializationError("Non-model object (%s) encountered during serialization" % type(obj))
 
		self.indent(1)
		self.xml.startElement(obj.__class__.__name__, {})
		self.indent(2)
		self.xml.startElement("pk", {})
		self.xml.characters(smart_unicode(obj._get_pk_val()))
		self.xml.endElement("pk")
		self.xml.startElement("model", {})
		self.xml.characters(smart_unicode(obj._meta))
		self.xml.endElement("model")
 
	def end_object(self, obj):
		"""
		Called after handling all fields for an object.
		"""
		self.indent(1)
		self.xml.endElement(obj.__class__.__name__)
 
	def handle_field(self, obj, field):
		"""
		Called to handle each field on an object (except for ForeignKeys and
		ManyToManyFields)
		"""
		self.indent(2)
		self.xml.startElement(field.name, {
			"type" : field.get_internal_type()
		})
 
		# Get a "string version" of the object's data (this is handled by the
		# serializer base class).
		if getattr(obj, field.name) is not None:
			value = self.get_string_value(obj, field)
			self.xml.characters(smart_unicode(value))
		else:
			self.xml.addQuickElement("None")
 
		self.xml.endElement(field.name)
 
	def handle_fk_field(self, obj, field):
		"""
		Called to handle a ForeignKey (we need to treat them slightly
		differently from regular fields).
		"""
		self._start_relational_field(field)
		related = getattr(obj, field.name)
		if related is not None:
			if field.rel.field_name == related._meta.pk.name:
				# Related to remote object via primary key
				related = related._get_pk_val()
			else:
				# Related to remote object via other field
				related = getattr(related, field.rel.field_name)
			self.xml.characters(smart_unicode(related))
		else:
			self.xml.addQuickElement("None")
		self.xml.endElement(field.name)
 
	def handle_m2m_field(self, obj, field):
		"""
		Called to handle a ManyToManyField. Related objects are only
		serialized as references to the object's PK (i.e. the related *data*
		is not dumped, just the relation).
		"""
		if field.creates_table:
			self._start_relational_field(field)
			for relobj in getattr(obj, field.name).iterator():
				self.xml.addQuickElement("object", attrs={"pk" : smart_unicode(relobj._get_pk_val())})
			self.xml.endElement(field.name)
 
	def _start_relational_field(self, field):
		"""
		Helper to output the  element for relational fields
		"""
		self.indent(2)
		self.xml.startElement(field.name, {
			"rel"  : field.rel.__class__.__name__,
			"to"   : smart_unicode(field.rel.to._meta),
		})

2. Создаем сериализатор для django-rest

?View Code PYTHON
class FlexResponder(SerializeResponder):
	def __init__(self, paginate_by=None, allow_empty=False):
		SerializeResponder.__init__(self, 'flex', 'application/xml',
					paginate_by=paginate_by, allow_empty=allow_empty)
 
	def error(self, request, status_code, error_dict=None):
		"""
		Return XML error response that includes a human readable error
		message, application-specific errors and a machine readable
		status code.
		"""
		from django.conf import settings
		if not error_dict:
			error_dict = ErrorDict()
		response = HttpResponse(mimetype = self.mimetype)
		response.status_code = status_code
		xml = SimplerXMLGenerator(response, settings.DEFAULT_CHARSET)
		xml.startDocument()
		xml.startElement("django-error", {})
		xml.addQuickElement(name="error-message", contents='%d %s' % (status_code, STATUS_CODE_TEXT[status_code]))
		xml.addQuickElement(name="status-code", contents=str(status_code))
		if error_dict:
			xml.startElement("model-errors", {})
			for (model_field, errors) in error_dict.items():
				for error in errors:
					xml.addQuickElement(name=model_field, contents=error)
			xml.endElement("model-errors")
		xml.endElement("django-error")
		xml.endDocument()
		return response

3. Подключаем сериализатор в Python через settings.py:

?View Code PYTHON
SERIALIZATION_MODULES = {
	'flex': 'myserver.flex_serializer'
}

4. Подключаем сериализатор к коллекции:

?View Code PYTHON
test_resource = Collection(
	queryset = models.Test.objects.all(),
	responder = FlexResponder()
)

alik Development ,

  1. Пока что нет комментариев.
  1. Пока что нет уведомлений.