I have an INSTEAD OF trigger, but PostgreSQL still complains while I insert into view
You have a trigger INSTEAD OF DELETE in your code, but none INSTEAD OF INSERT. Create that, too.
You can make that a trigger INSTEAD OF INSERT OR DELETE, but I'd suggest separate trigger functions for INSERT and DELETE and separate triggers. Simplifies the function code (no more need for IF TG_OP =  etc.).
Also consider this quote from the manual:

A row-level INSTEAD OF trigger should either return NULL to
indicate that it did not modify any data from the view's underlying
base tables, or it should return the view row that was passed in (the
NEW row for INSERT and UPDATE operations, or the OLD row for
DELETE operations). A nonnull return value is used to signal that
the trigger performed the necessary data modifications in the view.
This will cause the count of the number of rows affected by the
command to be incremented. For INSERT and UPDATE operations only,
the trigger may modify the NEW row before returning it. This will
change the data returned by INSERT RETURNING or UPDATE RETURNING,
and is useful when the view will not show exactly the same data that
was provided.