Usage¶
Usage for simple cases is very similar as with Tastypie. You should read their tutorial first.
The main difference is when you are defining API resource files. There you must
use MongoEngineResource
instead of ModelResource
.
Simple Example¶
from tastypie import authorization
from tastypie_mongoengine import resources
from test_app import documents
class PersonResource(resources.MongoEngineResource):
class Meta:
queryset = documents.Person.objects.all()
allowed_methods = ('get', 'post', 'put', 'delete')
authorization = authorization.Authorization()
Defining fields¶
Most document fields are automatically mapped to corresponding Tastypie fields but some are not. Of course, you can also manually define (override) those automatically mapped fields if, for example, you want to define some read-only.
Warning
When manually defining resource fields be careful to properly map
MongoEngine attributes to Tastypie attributes. For example, required
and null
attributes are inversed in meaning, but both are by default
False
.
Warning
When manually defining resource fields be careful not to forget to set
attribute
to document’s field name. It is not set automatically and
if it is not set it is assumed that you will be processing this field
manually (in for example, resource’s hydrate
method).
Some fields cannot be mapped automatically so you have to define them manually. Like related and embedded fields, but special fields like SequenceField as well:
sequence_field = tastypie_fields.IntegerField(attribute='sequence_field')
EmbeddedDocument¶
When you are using EmbeddedDocument
in your MongoEngine documents, you must define object_class
in Meta class of your resource declaration instead of queryset:
class EmbeddedPersonResource(resources.MongoEngineResource):
class Meta:
object_class = documents.EmbeddedPerson
...
When you are using normal MongoEngine Document
you can use queryset
or object_class
.
Polymorphism¶
MongoEngine supports document inheritance and you can normally add such inherited documents to your RESTful API. But sometimes it is useful to have only one API endpoint for family of documents so that they are all listed together but that you can still create different variations of the document. To enable this, you have to define mapping between types and resources. For example, if documents are defined as:
class Person(mongoengine.Document):
meta = {
'allow_inheritance': True,
}
name = mongoengine.StringField(max_length=200, required=True)
optional = mongoengine.StringField(max_length=200, required=False)
class StrangePerson(Person):
strange = mongoengine.StringField(max_length=100, required=True)
You might define your resources as:
class StrangePersonResource(resources.MongoEngineResource):
class Meta:
queryset = documents.StrangePerson.objects.all()
class PersonResource(resources.MongoEngineResource):
class Meta:
queryset = documents.Person.objects.all()
allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
authorization = authorization.Authorization()
polymorphic = {
'person': 'self',
'strangeperson': StrangePersonResource,
}
Use self
keyword to refer back to the current (main) resource.
Authorization and other similar parameters are still taken from the main
resource.
Then, when you want to use some other type than default, you should pass it as
an additional parameter type
to Content-Type
in your payload request:
Content-Type: application/json; type=strangeperson
Alternatively, you can pass a query string parameter.
All this works also for embedded documents in list.
Polymorphic resource_uri¶
By default, polymorphic resources are exposed through the API with a common
resource_uri
.
In the previous case, PersonResource
and StrangePersonResource
are both
exposed through the /<api_version>/person/
resource URI.
But in some cases, you may want to expose your resources through the polymorphic
resource uri.
To use this behaviour, you should set the prefer_polymorphic_resource_uri
meta variable to True
.
You might define your resources as:
class IndividualResource(resources.MongoEngineResource):
class Meta:
queryset = documents.Individual.objects.all()
allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
authorization = tastypie_authorization.Authorization()
paginator_class = paginator.Paginator
class CompanyResource(resources.MongoEngineResource):
class Meta:
queryset = documents.Company.objects.all()
allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
authorization = tastypie_authorization.Authorization()
paginator_class = paginator.Paginator
class ContactResource(resources.MongoEngineResource):
class Meta:
queryset = documents.Contact.objects.all()
allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
authorization = tastypie_authorization.Authorization()
prefer_polymorphic_resource_uri = True
polymorphic = {
'individual': IndividualResource,
'company': CompanyResource,
}
You might now reference both resources:
class ContactGroupResource(resources.MongoEngineResource):
contacts = fields.ReferencedListField(of='test_project.test_app.api.resources.ContactResource', attribute='contacts', null=True)
class Meta:
queryset = documents.ContactGroup.objects.all()
allowed_methods = ('get', 'post', 'put', 'patch', 'delete')
authorization = tastypie_authorization.Authorization()
And for each contact listed, the:
IndividualResource
would be dehydrated to/<api_version>/individual/<id>/
CompanyResource
to/<api_version>/company/<id>/
Warning
The ContactResource
could not be registered but be careful to register
all the resources present in the polymorphic
dict otherwise the
dehydrated resource_uri
will point to the parent resource.