Search code examples
androiddatabasepostgresqlkotlinandroid-jetpack-compose

How to successfully save data to another database column in postgresql using jetpack compose?


I have gone through each step of adding a new database column in postgresql, however when I press a button to save the information to postgresql database after filling in an outlinedtextfield, nothing happens. The information is not saved. I am trying to figure out why but without success at the moment. Any idea as of why it does not work? Appreciate the feedback as usual.

Frontend: Jetpack compose

Backend: Flask App

Database: Postgresql

OBS! If any additional information is needed let me know and I will update the code!

UPDATED CODE:

Kotlin constants and websocket that is taken from renders webservice:

const val BASE_URL="your_render_webservice_api"
constant usually starting with: "https://flask-render... .com"

the websocket is mostly just used for creating a chat with messages (live chats) hence should not affect the general code of posting data. 

Flask-app code:

  1. Connecting to Render
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = "postgresql: Your_external_render_api_here"
socketio = SocketIO(app)
db = SQLAlchemy(app)
  1. Class RelationShipData:
 class RelationshipData(db.Model):
     __tablename__ = 'relationshipData'
     id = db.Column(db.Integer, primary_key=True, autoincrement=True)
     user_auth_id = db.Column(db.Integer, db.ForeignKey('userdetails.id'), nullable=False)
     relationships = db.Column(db.String(255))

     user = db.relationship('Task', backref=db.backref('relationship_Data', lazy=True)) 

  1. Posting the data:
 # POSTING Relationships DATA TO DATABASE
 @app.route('/relationshipData', methods=['POST'])
 def postRelationshipsData():
     try:  # Added closing parenthesis here
         data = request.get_json()
         newEmail = data['email']
         user = Task.query.filter_by(email=newEmail).first()

         if not user:
             return jsonify({'error': "No User registered with this mail"}), 400

         user_auth_id = user.id
         relationships = data['relationships']
        

         # Check if user details already exist
         userRelationships = RelationshipData.query.filter_by(user_auth_id=user_auth_id).first()

         if userRelationships:
             # Update existing user details
             userRelationships.relationships = relationships            
             message = "Updated user details"
         else:
             # Add new user details
             userRelationships = RelationshipData(
                 user_auth_id=user_auth_id,
                 relationships=relationships,


             )
             db.session.add(userRelationships)
             message = "Added user details"

         db.session.commit()
         return jsonify({'message': message}), 201

     except Exception as e:
         return jsonify({'error': 'Internal Server Error'}), 500  

RelationshipModel:

@Keep
@Entity(tableName = "UserRelationDB", indices = [Index(value = ["id"], unique = true)])
@Serializable
data class UserRelationshipModel(
    @PrimaryKey
    val id: Int? = null,
    val relationship: String? = null,
)

User Relation Model:

@Serializable
class UserRelationModel (
    val users:List<UserRelationshipModel>

)

Interface UserService (Contains all the methods of posting and getting userdata from all the 3 other database columns. This is connected to AppModule!):

    @POST("relationshipData")
    suspend fun postUserRelationshipData(@Body data: UserRelationshipModel): Response<Void>

UserAPI class:

class UserApi @Inject constructor(
    private val userService: UserService,
    private val webSocketManager: WebSocketManager
) { 
    suspend fun postRelationshipsData(data: UserRelationshipModel): Response<Void> {
        return userService.postRelationshipsData(data)
    }
}

User Repo:

class Repo @Inject constructor(private val userApi: UserApi) {

    suspend fun postRelationshipsData(data: UserRelationshipModel): Response<Void> {
        return userApi.postRelationshipsData(data)
    }
}

Interface Dao:

@Dao
interface UserRelationshipDataModelDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUserRelationship(user: UserRelationshipModel)

    @Update
    suspend fun updateUserRelationship(user: UserRelationshipModel)


    @Query("SELECT * FROM UserRelationDB WHERE id = :userId")
    suspend fun getUserById(userId: Int): UserRelationshipModel?

    @Query("SELECT * FROM UserRelationDB LIMIT 1")
    suspend fun getLoggedInUser(): UserRelationshipModel?

    @Query("DELETE FROM UserRelationDB")
    suspend fun deleteAllUsers()
}

UserRelationshipLayoutScreen:

var relationship by remember { mutableStateOf("") }
......

item {
     UserInfo(
              initialUserData = relationship,
              label = "Relationship",
              onValueChange = { relationship = it }
              )
     }
....

Button OnClick:

onClick = {
            scope.launch {
                          val userData = UserRelationshipModel(
                          relationships = relationship
                          )

                                userCredentialsRelationshipViewModel.postUserRelationshipData(context, userData) { result ->

                                    when (result) {
                                        NetworkResult.Loading -> {
                                            showLoadingAnim = true
                                        }

                                        is NetworkResult.Error -> {
                                            showLoadingAnim = false
                                        }

                                        is NetworkResult.Success -> {

                                            showLoadingAnim = false
                                            userRelationshipHiltViewModel.insertUserRelationship(userData)

                                            // Navigate the user forward
                                            navController.navigate("${NavigationItem.UserDetails.route}/${selectedGender}")
                                        }
                                    }

                                }
                                Log.d("relationship model clicked", userData.toString())
                            }
                        },

UserCredentialsRelationshipViewModel:

@HiltViewModel
class UserCredentialsRelationshipViewModel@Inject constructor(private val repo: Repo) : ViewModel() {

    fun postUserRelationshipData(
        context: Context, userData: UserRelationshipModel,
        handleResult: (NetworkResult<String>) -> Unit
    ) {
        viewModelScope.launch {
            try {
                handleResult(NetworkResult.Loading)

                val result = repo.postRelationshipsData(data = userData)

                if (result.isSuccessful) {
                    handleResult(NetworkResult.Success("User relationship added successfully"))
                    Toast.makeText(context, "User relationship added successfully", Toast.LENGTH_SHORT)
                        .show()

                } else {

                    handleResult(NetworkResult.Error("Something went wrong"))

                }

            } catch (e: Exception) {

                handleResult(NetworkResult.Error("Something went wrong"))

                Toast.makeText(context, e.message.toString(), Toast.LENGTH_LONG).show()

                Log.d("postDataError", e.message.toString())

                e.printStackTrace()

            }
        }
    }
}

UserRelationshipHiltViewModel:

@HiltViewModel
class UserRelationshipHiltViewModel @Inject constructor(private val userRelationshipDataModelDao: UserRelationshipDataModelDao)

    : ViewModel() {
    private val _loggedInUser = MutableLiveData<UserRelationshipModel?>()
    val loggedInUser: LiveData<UserRelationshipModel?> get() = _loggedInUser


    fun insertUserRelationship(user: UserRelationshipModel) {
        viewModelScope.launch {
            try {
                userRelationshipDataModelDao.insertUserRelationship(user)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    fun deleteUser() {
        viewModelScope.launch {
            try {
                userRelationshipDataModelDao.deleteAllUsers()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

    // Update an existing user
    fun updateUser(user: UserRelationshipModel) {
        viewModelScope.launch {
            _loggedInUser.postValue(user)
            userRelationshipDataModelDao.updateUserRelationship(user)
        }
    }

    // Get a user by their ID
    fun getUserById(userId: Int) {
        viewModelScope.launch {
            val user = userRelationshipDataModelDao.getUserById(userId)
        }
    }

    fun getLoggedInUser() {
        viewModelScope.launch {
            try {
                val user = userRelationshipDataModelDao.getLoggedInUser()
                _loggedInUser.postValue(user)
            } catch (e: Exception) {
                _loggedInUser.postValue(null) // or handle the exception as needed
            }
        }
    }


}

UserInfoDatabase:

@Database(entities = [UserDataModel::class,UserMatchEntity::class, UserRelationshipModel::class,],version =6,exportSchema = true)
@TypeConverters(Converters::class)
abstract class UserInfoDatabase:RoomDatabase() {
    abstract fun userDataModelDao():UserDataModelDao
    abstract fun userMatch():UserMatchDao

    abstract fun userRelationshipDataModelDao(): UserRelationshipDataModelDao

}

AppModule:

    @Singleton
    @Provides
    fun provideRelationshipDao(userInfoDatabase: UserInfoDatabase): UserRelationshipDataModelDao{
        return userInfoDatabase.userRelationshipDataModelDao()
    }


Solution

  • Based on the provided code, it seems like the issue might be related to the communication between your Jetpack Compose frontend and the Flask backend. Here are a few steps to debug and ensure that the data is being sent and saved correctly:

    1. Check Network Requests: Ensure that the network request is being made correctly from your Jetpack Compose app to the Flask backend. You can use tools like Logcat in Android Studio to log the request and response.

    2. Verify Backend Endpoint: Ensure that the Flask endpoint /relationshipData is correctly set up and accessible. You can test this using tools like Postman or curl.

    3. Check for Errors: Ensure that there are no errors in the backend when processing the request. Check the Flask logs for any errors.

    4. Debugging Steps:

      • Log the request data in the Flask endpoint to ensure that the data is being received correctly.
      • Log the response in the Jetpack Compose app to ensure that the request is successful.

    Here is an updated version of the relevant parts of your code with added logging for debugging:

    Flask Backend

    @app.route('/relationshipData', methods=['POST'])
    def postRelationshipsData():
        try:
            data = request.get_json()
            print(f"Received data: {data}")  # Log the received data
    
            newEmail = data['email']
            user = Task.query.filter_by(email=newEmail).first()
    
            if not user:
                return jsonify({'error': "No User registered with this mail"}), 400
    
            user_auth_id = user.id
            relationships = data['relationships']
    
            userRelationships = RelationshipData.query.filter_by(user_auth_id=user_auth_id).first()
    
            if userRelationships:
                userRelationships.relationships = relationships
                message = "Updated user details"
            else:
                userRelationships = RelationshipData(
                    user_auth_id=user_auth_id,
                    relationships=relationships,
                )
                db.session.add(userRelationships)
                message = "Added user details"
    
            db.session.commit()
            return jsonify({'message': message}), 201
    
        except Exception as e:
            print(f"Error: {e}")  # Log the error
            return jsonify({'error': 'Internal Server Error'}), 500
    

    Jetpack Compose App

    Add logging to the UserCredentialsRelationshipViewModel to ensure the request is being made and the response is handled correctly:

    @HiltViewModel
    class UserCredentialsRelationshipViewModel @Inject constructor(private val repo: Repo) : ViewModel() {
    
        fun postUserRelationshipData(
            context: Context, userData: UserRelationshipModel,
            handleResult: (NetworkResult<String>) -> Unit
        ) {
            viewModelScope.launch {
                try {
                    handleResult(NetworkResult.Loading)
    
                    val result = repo.postRelationshipsData(data = userData)
                    Log.d("postUserRelationshipData", "Response: ${result.code()} - ${result.message()}")  // Log the response
    
                    if (result.isSuccessful) {
                        handleResult(NetworkResult.Success("User relationship added successfully"))
                        Toast.makeText(context, "User relationship added successfully", Toast.LENGTH_SHORT).show()
                    } else {
                        handleResult(NetworkResult.Error("Something went wrong"))
                    }
    
                } catch (e: Exception) {
                    handleResult(NetworkResult.Error("Something went wrong"))
                    Toast.makeText(context, e.message.toString(), Toast.LENGTH_LONG).show()
                    Log.d("postDataError", e.message.toString())
                    e.printStackTrace()
                }
            }
        }
    }
    

    Button OnClick

    Ensure that the UserRelationshipModel is correctly populated and logged:

    onClick = {
        scope.launch {
            val userData = UserRelationshipModel(
                relationships = relationship
            )
            Log.d("ButtonOnClick", "UserData: $userData")  // Log the user data
    
            userCredentialsRelationshipViewModel.postUserRelationshipData(context, userData) { result ->
                when (result) {
                    NetworkResult.Loading -> {
                        showLoadingAnim = true
                    }
                    is NetworkResult.Error -> {
                        showLoadingAnim = false
                    }
                    is NetworkResult.Success -> {
                        showLoadingAnim = false
                        userRelationshipHiltViewModel.insertUserRelationship(userData)
                        navController.navigate("${NavigationItem.UserDetails.route}/${selectedGender}")
                    }
                }
            }
        }
    }
    

    By adding these logs, you can trace the flow of data and identify where the issue might be occurring. Ensure that the data is correctly sent from the frontend, received by the backend, and saved to the database.