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:
source
refers 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.
default
specifies the default value of the resulting field; if not set and the field is required, it will raise an errorrequired
indicates 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.