|
1 | | -# Adapter |
| 1 | +# Adapters |
| 2 | + |
| 3 | +Adapters allow converting data from one structure to another. |
| 4 | + |
| 5 | +The adapters work very similar to `Serializers` in Django REST framework. They are not tied with Django nor DRF in any way, instead they probide a generic way of transforming an object to another. |
| 6 | + |
| 7 | +The are intended to be used when working with 3rd party APIs and as View-Models. |
| 8 | + |
| 9 | +## Example |
| 10 | + |
| 11 | +Suppose you're dealing with an API that returns a profile in the following format: |
| 12 | + |
| 13 | + { |
| 14 | + "first_name": "Alexandra", |
| 15 | + "last_name": "Johnson", |
| 16 | + "dob": "2/27/1985", |
| 17 | + "address_street": ["71 Boat Lane"], |
| 18 | + "address_zip": "EH45 0ZQ", |
| 19 | + "address_city": "Alderton", |
| 20 | + "address_country": "GB" |
| 21 | + } |
| 22 | + |
| 23 | +But your local models are a little different: |
| 24 | + |
| 25 | + class Address(object): |
| 26 | + def __init__(self, **kwargs): |
| 27 | + self.line1 = kwargs.get('line1', None) |
| 28 | + self.line2 = kwargs.get('line2', None) |
| 29 | + self.postal_code = kwargs.get('postal_code', None) |
| 30 | + self.city = kwargs.get('city', None) |
| 31 | + self.region = kwargs.get('region', None) |
| 32 | + self.country = kwargs.get('country', None) |
| 33 | + |
| 34 | + |
| 35 | + class Profile(object): |
| 36 | + def __init__(self, **kwargs): |
| 37 | + self.first_name = kwargs.get('first_name', None) |
| 38 | + self.last_name = kwargs.get('last_name', None) |
| 39 | + self.birthday = kwargs.get('birthday', None) |
| 40 | + self.address = kwargs.get('address', None) |
| 41 | + |
| 42 | +How do you create local instances from the result returned by the API? Enter adapters: |
| 43 | + |
| 44 | + import adapters |
| 45 | + |
| 46 | + |
| 47 | + class AddressAdapter(adapters.Adapter): |
| 48 | + class Meta(object): |
| 49 | + model = Address |
| 50 | + |
| 51 | + line1 = adapters.CharField(source='address_street.0') |
| 52 | + line2 = adapters.CharField(source='address_street.1', default='') |
| 53 | + postal_code = adapters.CharField(source='address_zip') |
| 54 | + city = adapters.CharField(source='address_city') |
| 55 | + region = adapters.CharField(source='address_region', default='') |
| 56 | + country = adapters.CharField(source='address_country') |
| 57 | + |
| 58 | + |
| 59 | + class ProfileAdapter(adapters.Adapter): |
| 60 | + class Meta(object): |
| 61 | + model = Profile |
| 62 | + |
| 63 | + first_name = adapters.CharField() |
| 64 | + first_name = adapters.CharField() |
| 65 | + birthday = adapters.DateField(source='dob') |
| 66 | + address = AddressAdapter(source='*') |
| 67 | + |
| 68 | + |
| 69 | + ProfileAdapter().adapt(remote_data) |
| 70 | + |
| 71 | + |
| 72 | +## Declaring adapters |
| 73 | + |
| 74 | +Declaring an adapter is as simply as inheriting from `adapters.Adapter` |
| 75 | + |
| 76 | +The `data` argument can be omitted and passed to the `.adapt()` method. See [Adapting data](#adapting-data) below. |
| 77 | + |
| 78 | +The `instance` argument is optional and it allows converting to an existing instance i.e. instead of creating a new one. |
| 79 | + |
| 80 | +### Meta options |
| 81 | + |
| 82 | +The `Meta.model` field specifies the type of the end result. Defaults to `dict` and as such the data is converted to a dictionary. |
| 83 | + |
| 84 | +### Adapting data |
| 85 | + |
| 86 | +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. |
| 87 | + |
| 88 | +## Fields |
| 89 | + |
| 90 | +Each field accepts the following arguments: |
| 91 | + |
| 92 | +* `source` refers to the attribute that will be used to populate the field; the default is to use the same name as the field; |
| 93 | + |
| 94 | +The `source` argument can use dotted notation to traverse objects e.g. `profile.birthday`. |
| 95 | + |
| 96 | +The value `*` can be used to indicate the adapter to pass the entire object to the field. |
| 97 | + |
| 98 | +* `default` specifies the default value of the resulting field; if not set and the field is required, it will raise an error |
| 99 | +* `required` indicates whether the field should be required or not; default is `True` |
| 100 | + |
| 101 | +The following field types are available: |
| 102 | + |
| 103 | +### AdapterMethodField |
| 104 | + |
| 105 | +The field gets it's value by calling a method defined on the adapter class. It can be used to manipulate the data. |
| 106 | + |
| 107 | +The `method_name` argument refers to the name of the method. It defaults to `get_<field_name>`. |
| 108 | + |
| 109 | +### BooleanField |
| 110 | + |
| 111 | +Converts the result to a boolean value. |
| 112 | + |
| 113 | +### CharField |
| 114 | + |
| 115 | +A Unicode field. |
| 116 | + |
| 117 | +### DateField |
| 118 | + |
| 119 | +Parses the value into a `datetime.date` object. |
| 120 | + |
| 121 | +### DateTimeField |
| 122 | + |
| 123 | +Parses the value into a `datetime.datetime` object. |
| 124 | + |
| 125 | +### DecimalField |
| 126 | + |
| 127 | +Parses the value into a `Decimal` object. |
| 128 | + |
| 129 | +### FloatField |
| 130 | + |
| 131 | +A float field. |
| 132 | + |
| 133 | +### IntField |
| 134 | + |
| 135 | +An integer field. |
| 136 | + |
| 137 | +### TimeField |
| 138 | + |
| 139 | +Parses the value into a `datetime.time` object. |
| 140 | + |
| 141 | +### VerbatimField |
| 142 | + |
| 143 | +Copy the value as is. |
0 commit comments