carltongibson / neapolitan

Quick CRUD views for Django
https://noumenal.es/neapolitan/
MIT License
485 stars 34 forks source link

Per-actions Permissions. #5

Open carltongibson opened 1 year ago

carltongibson commented 1 year ago

Scenario:

Needs a hook into ModelView.get_urls() since parsing the end-list to wrap the view callbacks isn't fun. But the most vanilla approach is to wrap the view, rather than add a DRF-style check_permissions() hook on the view class itself.

carltongibson commented 1 year ago

Decorators take various kwargs.

        staff_member_required(..., login_url="/", redirect_field_name=None),

🔨: Using partial when defining a map?

tissieres commented 10 months ago

Hello Carlton, This is a functionality that I need for my current project. I was planning to implement something that relies on the built-in model permissions. Do you prefer something more generic using decorators? Maybe I could propose a PR, but I will need your guidance on how to do that first :wink:

carltongibson commented 10 months ago

Hi @tissieres.

I'm still working on the details of the desired API here.

Normally I'd be very grateful for input but I do want to handle this one myself.

A first pass would be to implement a check_permissions and call that at the appropriate place in the view.

We're still at the early stage here so there's no wrong answers yet.

I know that's not very specific but I hope it helps.

tissieres commented 10 months ago

No problem. I will test some approaches on my side and if I find something really elegant, I'll come back to you. Thanks for neapolitan, fun project!

carltongibson commented 10 months ago

Thanks! Let me know what you come up with!

tissieres commented 10 months ago

That was simple finally:

    @classonlymethod
    def get_urls(cls):
        verbose_name = cls.model._meta.model_name
        app_label = cls.model._meta.app_label
        urlpatterns = [
            path(
                f"{verbose_name}/",
                permission_required(f"{app_label}.view_{verbose_name}")(
                    cls.as_view(role=Role.LIST)
                ),
                name=f"{verbose_name}-list",
            ),
            path(
                f"{verbose_name}/new/",
                permission_required(f"{app_label}.add_{verbose_name}")(
                    cls.as_view(role=Role.CREATE)
                ),
                name=f"{verbose_name}-create",
            ),
...

I also added a check of the permissions in the object_list template tag to restrict the actions and in the object_list.html template to remove the "add button" depending on them.

carltongibson commented 10 months ago

Yes, wrapping the view callbacks in get_urls() is a very good way to go!