Adapters allow converting data from one structure to another.
The adapters work very similar to Serializers in Django REST framework. They are not tied with Django nor DRF in any way, instead they provide a generic way of transforming an object to another.
The are intended to be used when working with 3rd party APIs and as View-Models.
Suppose you're dealing with an API that returns a profile in the following format:
{
"first_name": "Alexandra",
"last_name": "Johnson",
"dob": "2/27/1985",
"address_street": ["71 Boat Lane"],
"address_zip": "EH45 0ZQ",
"address_city": "Alderton",
"address_country": "GB"
}
But your local models are a little different:
class Address(object):
def __init__(self, **kwargs):
self.line1 = kwargs.get('line1', None)
self.line2 = kwargs.get('line2', None)
self.postal_code = kwargs.get('postal_code', None)
self.city = kwargs.get('city', None)
self.region = kwargs.get('region', None)
self.country = kwargs.get('country', None)
class Profile(object):
def __init__(self, **kwargs):
self.first_name = kwargs.get('first_name', None)
self.last_name = kwargs.get('last_name', None)
self.birthday = kwargs.get('birthday', None)
self.address = kwargs.get('address', None)
How do you create local instances from the result returned by the API? Enter adapters:
import adapters
class AddressAdapter(adapters.Adapter):
class Meta(object):
model = Address
line1 = adapters.CharField(source='address_street.0')
line2 = adapters.CharField(source='address_street.1', default='')
postal_code = adapters.CharField(source='address_zip')
city = adapters.CharField(source='address_city')
region = adapters.CharField(source='address_region', default='')
country = adapters.CharField(source='address_country')
class ProfileAdapter(adapters.Adapter):
class Meta(object):
model = Profile
first_name = adapters.CharField()
first_name = adapters.CharField()
birthday = adapters.DateField(source='dob')
address = AddressAdapter(source='*')
ProfileAdapter().adapt(remote_data)
Declaring an adapter is as simple as inheriting from adapters.Adapter.
The data argument can be omitted and passed to the .adapt() method. See Adapting data below.
The instance argument is optional and it allows converting to an existing instance i.e. instead of creating a new one.
The Meta.model field specifies the type of the end result. Defaults to dict and as such the data is converted to a dictionary.
To convert data from one format to another, call the Adapter.adapt() method. It accepts an optional data argument which refers to the data to be converted.
Each field accepts the following arguments:
sourcerefers to the attribute that will be used to populate the field; the default is to use the same name as the field;
The source argument can use dotted notation to traverse objects e.g. profile.birthday.
The value * can be used to indicate the adapter to pass the entire object to the field.
defaultspecifies the default value of the resulting field; if not set and the field is required, it will raise an errorrequiredindicates whether the field should be required or not; default isTrue
The following field types are available:
The field gets it's value by calling a method defined on the adapter class. It can be used to manipulate the data.
The method_name argument refers to the name of the method. It defaults to get_<field_name>.
Converts the result to a boolean value.
A Unicode field.
Parses the value into a datetime.date object.
Parses the value into a datetime.datetime object.
Parses the value into a Decimal object.
A float field.
An integer field.
Parses the value into a datetime.time object.
Copy the value as is.