Hiding a menu item improves the interface, but access is decided on the server. Consider an internal workspace with managers, operators and read-only users.
Start with actions, not role names
Write down the actions first: view a record, edit it, approve a change, export a report. Then decide which roles can perform each action and on which records. A role name by itself leaves too much room for interpretation.
Make the server decision explicit
For every sensitive request, check both the user and the requested object. Authentication tells the application who is making the request; authorization determines whether this action is allowed. In Django, staff status and model permissions solve different parts of that problem.
Let the interface explain access
React can hide unavailable actions and explain why a button is disabled. That is useful feedback, not a substitute for the API check. Keep action names consistent so a frontend permission maps to a clear server rule.
Test the uncomfortable paths
Check anonymous, ordinary and staff accounts separately. Try the direct URL, an altered record identifier, and a request sent without opening the page. Also test what happens after a user loses a role or signs out.
Keep the policy reviewable
A small permission matrix is easier to review than rules scattered through screens. For each new action, update the matrix, server check and interface behavior together. The example is a design exercise; real object-level rules depend on the product.