Nozbe / WatermelonDB

🍉 Reactive & asynchronous database for powerful React and React Native apps ⚡️
https://watermelondb.dev
MIT License
10.62k stars 600 forks source link

Writer Method [TypeError: undefined is not a function] #1634

Closed Garabed96 closed 9 months ago

Garabed96 commented 1 year ago

I am facing an issue with using the WatermelonDB writer in my model. I have successfully used the inline writer, but I'm struggling to get the recommended approach of creating a writer in the model to work.

Schema.ts:

tableSchema({
      name: "foods",
      columns: [
        { name: "calories", type: "number" },
        { name: "carbs", type: "number" },
        { name: "fat", type: "number" },
        { name: "protein", type: "number" },
        { name: "description", type: "string" },
      ],
    }),

Model Food.ts

import { Model } from "@nozbe/watermelondb";
import { text, writer } from "@nozbe/watermelondb/decorators";

export default class Food extends Model {
  static table = "foods";

  @text("calories") calories;
  @text("carbs") carbs;
  @text("fat") fat;
  @text("protein") protein;
  @text("description") description;

  @writer async addFood(calories, carbs, fat, protein, description) {
    try {
      // const foodItem = this.collections.get("foods");
      await this.collections.get("foods").create((food) => {
        food.calories = calories;
        food.carbs = carbs;
        food.fat = fat;
        food.protein = protein;
        food.description = description;
      });
    } catch (error) {
      console.error("Error creating Nutrition entry:", error);
      throw error; // Rethrow the error or handle it as per your application's requirements
    }
  }
}

Usage in my frontend:

const diaryButton = async () => {
    const foodItem = database.collections.get("foods");
    try {
      await foodItem.addFood(
        total_cals,
        totalCarbs,
        totalFats,
        totalProteins,
        "This is my description"
      ); // Replace 0 with the correct water value
      console.log("Successfully created food post");
      const all_food = await database.get("foods").query().fetch();
      console.log("food saved in DB!:", all_food);
    } catch (error) {
      console.error("Error while creating food post: \n", error);
    }
  };
Garabed96 commented 1 year ago

I haven't seen a single working example of method based writers, do I need to wrap my frontend in an observable for method writers to work?

ghost commented 1 year ago

I came up with the same problem. Did you solve it?

Sannijiv commented 1 year ago

I would also like to see an example :)

theianjohnson commented 1 year ago

I've got a writer that performs an update to work, but like the example above I can't for the life of me get a write that inserts to work. The lack of documentation is making me rethink watermelon altogether

ash0080 commented 1 year ago

Adding a static method for creation would indeed be a good solution. However, the @writer decorator cannot be used in that context.

But I believe a better practice might be to use an abstract Data type to control CRUD operations on the Model type. This would add an extra layer of abstraction instead of directly using the Model type, which is actually specific to the database and contains redundant data that frontend don't need to know at the API level.

But I agree, the documentation for waterlemondb is written so poorly, it's hard to believe that this fact has persisted for 5 years...

geoff-desj commented 9 months ago

If this can help, i have this message after creating an @writer updateProgress function : "TypeError: work.updateProgress is not a function (it is undefined)", autorefresh is not enough. After reloading the app tapping "r" in terminal now the function is loaded and works.

Garabed96 commented 9 months ago

No I never got anything working except for the inline writers which worked from the get go. I completely stopped using wm and decided to go with an alternative instead. Gave it a really long try but the lack of proper documentation is a huge red flag, the devs could have created a single blog post saving us all cumulatively 100's of hours at the minimum.

theianjohnson commented 9 months ago

I also never got watermelon to work, I’m now using https://www.npmjs.com/package/expo-sqlite-eloquent-orm which doesn’t have syncing, but is a really easy to use wrapper around SQLite

estrayer7 commented 7 months ago

Ran into this same issue, and I could only get writer methods to work when some form of data existed in the table. Using an inline writer worked regardless of having data or not which is very annoying the methods do not work the same way.

Cheizr commented 6 months ago

Experiencing the same with writers... [TypeError: _Turnos.default.addTurn is not a function (it is undefined)]

rvibit commented 6 months ago

I also never got watermelon to work, I’m now using https://www.npmjs.com/package/expo-sqlite-eloquent-orm which doesn’t have syncing, but is a really easy to use wrapper around SQLite

Is it reactive?, like when we insert anything in DB the data on the screen update automatically?

rvibit commented 6 months ago

I was facing the same issue, the issue was that by mistake I wrote same table name in multiple Model classes.

ranuja-apps commented 2 months ago

The issue is because the return type of the database.get(<table_name>) is class of that model , not a actual object of that class counter to the observables which return the model object so the post.<writer_fn> works fine if post comes from the observables. Using as a static variable will resolve the issue but then we need to pass some args like collection in order to do crud operations in the @writer function.

ranuja-apps commented 2 months ago

This is my workaround for using @writer and use it.

const GetDatabaseInstance = <T extends Model>(
  tableName: TableType[keyof TableType],
): T => {
  const collections = WatermenlonDB.collections.get(tableName).modelClass;
  collections.database = WatermenlonDB;
  collections.collection = WatermenlonDB.collections.get(tableName);
  return collections;
};

collection.database : because writer decorator internally needs this and collection because to use collection object in @writer

@writer should be static

@writer static async saveCategoryList(models: CategoryModel[]) {
    this.database.batch(
      models.forEach(model => { })