AttributeError: 'str' object has no attribute 'get' when sending with batch_id
jordaniza opened this issue · 6 comments
EDIT: Condensed all example code way down
Issue Summary
The Mail
object invokes the get()
method upon sending.
This in turn creates a JSON representation of the mail object, by calling _get_or_none(self.attr)
for all provided attributes.
While the functionality works correctly more most attributes, if one attaches a batch_id
to the Mail
object, the _get_or_none
method throws an AttributeError: 'str' object has no attribute 'get'
What's interesting is that other attributes do not face the same issue (send_at
, for example - I appreciate it's an integer but also has no get()
method), upon closer inspection it appears send_at
has a more sophisticated getter and setter and returns an actual SendAt
object, so I think the problem is that the API is missing a BatchId
object.
Steps to Reproduce
- Initialise the Mail helper
- Add a batch_id attribute
- Call sg.send on the instance
- Enjoy
###Code Snippet
message = Mail(
# init message here
)
message.batch_id = 'iamabatchid'
response = sg.send(message)
Exception/Log
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <sendgrid.helpers.mail.mail.Mail object at 0x0000013D0E464280>, from_obj = 'YWUwNWQ0Y2MtYjk4MS0xMWViLWIxYzctZDZkMjJjNTU5YTcyLWY1Y2EyMGYyYw'
def _get_or_none(self, from_obj):
"""Get the JSON representation of the object, else return None
:param from_obj: Get the JSON representation of the object,
else return None
:type from_obj: obj
"""
> return from_obj.get() if from_obj is not None else None
E AttributeError: 'str' object has no attribute 'get'
venv\lib\site-packages\sendgrid\helpers\mail\mail.py:133: AttributeError
Technical details:
- sendgrid-python version: 6.7.0
- python version: 3.8
Workarounds so far:
- Use the JSON API directly ( 👎 )
- Create a simple wrapper for the batch_id with a get method:
class BatchId:
def __init__(self, id: str):
self.id = id
def get(self) -> str:
return self.id
You can then attach the BatchId
object to the Mail
object.
batch_id = BatchId('id')
message.batch_id = batch_id
EDIT: On reflection, suspect a batch id object is what's needed, based on the other setters for the mail object - happy to look into a suggested code change if needed.
On inspection, turns out there is an existing BatchId
object defined in the API, but it's not been added as a setter.
Would welcome input from the development team as to the correct way to use the following fields:
- Mail.batch_id
- Mail.categories
- Mail.custom_args
Namely, are we to attach the Category, CustomArg and BatchId objects directly to the Mail object, or is there a preferred way to use these fields, that I am missing?
Cheers
Hello @jordaniza,
Thanks for taking the time to express this issue in great detail!
The BatchId
should be set via: message.batch_id = BatchId("HkJ5yLYULb7Rj8GKSx7u025ouWVlMgAi")
per this full example and here is the setter.
Does that work for you?
With best regards,
Elmer
Thanks @thinkingserious,
Yes, good to know that's the right approach.
I clearly missed the Kitchen Sink file. I was looking for examples on how to use the API in the /examples folder, but all of said examples use JSON payloads, instead of the Python Objects.
Thanks for sharing the example folder - I would be happy to prepare an addendum to the README if it would help other users save some time.
Awesome! We would certainly appreciate a PR :) Thanks!!
If you are trying it on a pandas Dataframe, Use to_json()
It converts String column into proper json format, then you can use the get fn.
It works! :)