gtalarico / pyairtable

Python Api Client for Airtable
https://pyairtable.readthedocs.io
MIT License
765 stars 138 forks source link

Model.save() specific fields #378

Closed gildastone closed 1 week ago

gildastone commented 1 month ago

Is there a way to save specific fields of a Model and not the entire record while using the Model.save method?

Indeed, I have a record that has a pretty heavy payload (number of fields). I'm fetching the record, doing some operations/updates on it on specific fields and then save again.

I would like to only save/update those specific fields instead of posting the whole record (see pyairtable.orm.model.Model.save()):

I couldn't find a way with the current ORM. Is there a solution?

If not, we could update the pyairtable.orm.model.Model.save method to (adding the fields_to_save: List = [] argument):

    def save(self, fields_to_save: List = []) -> bool:
        if self._deleted:
            raise RuntimeError(f"{self.id} was deleted")
        table = self.meta.table
        fields = self.to_record(only_writable=True)["fields"]

        if fields_to_save:
            fields = {key: value for key, value in fields.items() if key in fields_to_save}

        if not self.id:
            record = table.create(fields, typecast=self.meta.typecast)
            did_create = True
        else:
            record = table.update(self.id, fields, typecast=self.meta.typecast)
            did_create = False

        self.id = record["id"]
        self.created_time = datetime_from_iso_str(record["createdTime"])
        return did_create
mesozoic commented 1 month ago

Totally agree – this would be a very useful behavior (and should probably be the default behavior, rather than something implementers would have to explicitly request with a flag or a separate method call).

One way I can think of approaching this is to keep track of which values have been modified in memory since the object was created. Then it would be possible to only send those fields to the table.update method. There are a few good pieces of prior art for SQLAlchemy, Django, and Rails.

Pull requests welcome! 😁

BAPCon commented 4 weeks ago

@mesozoic Working on this now, pull request probably today.

gildastone commented 4 weeks ago

@BAPCon great to hear you're working on it. Let me know if you need some help. @mesozoic great suggestion to leverage existing art from those projects.

BAPCon commented 4 weeks ago

@gildastone I addressed the original issue and the move to being able to specify which fields to update, I believe my pull request accomplishes what the user stated. In general @gildastone I would suggest also using Model.batch_save when possible.

I've done some testing with my changes to compare specifying fields to update vs all fields at the same time. The runtime savings vary from 0% - 40% when specifying the fields, not sure exactly why such a large variation but the savings seem inconsistent. It seems dependent on whether you are using Model.batch_save or Model.save and the total number of records.

If anyone wants to review my changes or test the execution times yourself, I think that may be a good idea before implementing the larger concept of a field's state being dirty that @mesozoic mentioned.

gildastone commented 4 weeks ago

@BAPCon Understood. Thanks. Just added my review to https://github.com/gtalarico/pyairtable/pull/379.