VeryApt / django-phone-field

Lightweight model and form field for phone numbers in Django
GNU General Public License v3.0
52 stars 13 forks source link

value_from_object returns <phone_field.phone_number.PhoneNumber object at 0x000001BB1BECBAF0> #11

Closed Liamhanninen closed 4 years ago

Liamhanninen commented 4 years ago

I am using model_to_dict() on an instance that has a PhoneNumber field. model_to_dict() uses value_from_object() to get the value. value_from_object() expects the value (actual phone number 123-456-7892). Instead it is getting <phone_field.phone_number.PhoneNumber object at 0x000001BB1BECBAF0>. Django docs don't mention anything about having to overwrite value_from_object so I'm not sure what you have to do. But it's returning a value that I wouldn't expect when using model_to_dict.

Example: 'mailphone2': <phone_field.phone_number.PhoneNumber object at 0x000001BB1BECBAF0>

For your reference model_to_dict:

def model_to_dict(instance, fields=None, exclude=None):
    """
    Return a dict containing the data in ``instance`` suitable for passing as
    a Form's ``initial`` keyword argument.

    ``fields`` is an optional list of field names. If provided, return only the
    named.

    ``exclude`` is an optional list of field names. If provided, exclude the
    named from the returned dict, even if they are listed in the ``fields``
    argument.
    """
    opts = instance._meta
    data = {}
    for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many):
        if not getattr(f, 'editable', False):
            continue
        if fields and f.name not in fields:
            continue
        if exclude and f.name in exclude:
            continue
        data[f.name] = f.value_from_object(instance)
    return data
va-andrew commented 4 years ago

Thanks for commenting!

From what I can tell in the docs, model_to_dict() isn't intended for serialization, but to prep model fields for inputs to a Form. It simply maps a field name to a Python object. The function seems to be used in BaseModelForm, which is a part of admin forms and ModelForms in Django. Our test cases cover these form types and seem to work as expected, which would indicate that model_to_dict() and value_from_object() are functioning as intended.

Liamhanninen commented 4 years ago

Sure thing. Thanks for responding va-andrew. Unfortunately (for me) that makes total sense. It's probably on my end then. I'll comment here if I figure out what I did wrong.

Liamhanninen commented 4 years ago

Nothing yet but does this tell you anything; the phone number is clean right before it passes to model_to_dict. All other fields return normally - this one is the only one that returns like this (<phone_field.phone_number.PhoneNumber object at 0x000001BB1BECBAF0>).

customer = customer_model.objects.get(index=customer_id)
print(customer.mailphone1)
selected_object = model_to_dict(customer)

(608) 222-1234

va-andrew commented 4 years ago

In this example, customer is always a PhoneNumber object. It's disguised, though, since when you call print() it implicitly calls PhoneNumber.__str__() and displays the formatted number.

Your code should accept that selected_object['mailphone1'] is an object of type PhoneNumber. If your application wants to use it as a formatted string, apply str() to it first.

va-andrew commented 4 years ago

err customer.mailphone is always a PhoneNumber object

Liamhanninen commented 4 years ago

Ok thanks. Also makes sense. Here's what I'll have to do for now.

phone_field_list = [
    'mailphone1',
    'mailphone2',
    'mailphone3',
    'phone2',
    'cellphone',
]

customer = customer_model.objects.get(index=customer_id)
selected_object = model_to_dict(customer, exclude = phone_field_list)
print(selected_object)
selected_object['mailphone1'] = str(customer.mailphone1)
selected_object['mailphone2'] = str(customer.mailphone2)
selected_object['mailphone3'] = str(customer.mailphone3)
selected_object['phone2'] = str(customer.phone2)
selected_object['cellphone'] = str(customer.cellphone)
print(selected_object)

Output:

{'index': 7020, 'firstname': 'John'} {'index': 7020, 'firstname': 'John','mailphone1': 'None', 'mailphone2': 'None', 'mailphone3': '(608) 222-1234', 'phone2': 'None', 'cellphone': 'None'}

If I don't do it this way and just feed all fields into model_to_dict then I get this: {'index': 7020, 'firstname': 'John','mailphone1': 'None', 'mailphone2': 'None', 'mailphone3': <phone_field.phone_number.PhoneNumber object at 0x000002A15A9D8B20>, 'phone2': 'None', 'cellphone': 'None'}

va-andrew commented 4 years ago

I think that's the only solution for your use case. I'm closing this issue now, feel free to re-open if anything new crops up.