How to prevent save/delete in the Admin

111 views
Skip to first unread message

Mike Dewhirst

unread,
Mar 29, 2015, 4:08:06 AM3/29/15
to django...@googlegroups.com
At the moment "has_change_permission" in the Admin works by returning
403 Forbidden if it gets a False.

That doesn't suit my needs but it is probably not advisable to adjust it.

Users without change permission need to be able to see the data and
perhaps raise an exception (in this case BusinessRuleViolation
subclassed from ValidationError) if they try to save. In other words,
maybe use the existing form clean() mechanism.

However, I recognise that this subverts the Admin by preventing [Save]
and therefore may not be appropriate.

A perhaps better solution would be to disable the Save and Delete
widgets/actions in particular circumstances.

How can I do something like this?

TL;DR

All the business rule decision logic is calculated using the object
instance and request.user.

The application displays a substance record with a few 1:1, 1:n, n:1 and
n:m related records. The substance has a status field which can be made
"public domain".

The business rule is that a user can see and edit all their own
substances but can see nothing owned by anyone else - unless they have
been put in the public domain (and which then must be readonly to the
"public")

Thanks for any ideas

Mike

Avraham Serour

unread,
Mar 29, 2015, 4:09:40 AM3/29/15
to django...@googlegroups.com
maybe overwrite the view in your own modeladmin class?



Mike

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/5517B2B5.7020805%40dewhirst.com.au.
For more options, visit https://groups.google.com/d/optout.

Julo

unread,
Mar 29, 2015, 4:28:46 AM3/29/15
to django...@googlegroups.com
Maybe you can add an interface for the models that are importants and hook to the save/delete signal a interfaced function called CanSave()
And you check the premisson he has, if not allowed rais an exception, and don't save.
Sorry for the bad english.
Saludos,
Julian
--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.

Gabriel - Iulian Dumbrava

unread,
Mar 29, 2015, 6:22:25 AM3/29/15
to django...@googlegroups.com
In case you hide/not display the save/delete button you must also double check in the delete view if the current user has the right permissions to delete the item. It's pretty easy to add a post button in firebug, or even trigger the post using a different method.

Mike Dewhirst

unread,
Mar 29, 2015, 7:04:46 AM3/29/15
to django...@googlegroups.com
On 29/03/2015 7:28 PM, Julo wrote:
> Maybe you can add an interface for the models that are importants and hook to the save/delete signal a interfaced function called CanSave()
> And you check the premisson he has, if not allowed rais an exception, and don't save.

Julo

I like that. I'll dig a bit deper. It would be best to do it in the
model but I'm not sure how I can get request.user from there.

Thank you

Mike

Mike Dewhirst

unread,
Mar 29, 2015, 8:12:30 AM3/29/15
to django...@googlegroups.com
On 29/03/2015 7:09 PM, Avraham Serour wrote:
> maybe overwrite the view in your own modeladmin class?

I need to keep using the Admin and haven't done anything like that
previously. I'll have to do the research.

Thanks

Mike
> send an email to django-users+unsubscribe@__googlegroups.com
> <mailto:django-users%2Bunsu...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/__group/django-users
> <http://groups.google.com/group/django-users>.
> To view this discussion on the web visit
> https://groups.google.com/d/__msgid/django-users/5517B2B5.__7020805%40dewhirst.com.au
> <https://groups.google.com/d/msgid/django-users/5517B2B5.7020805%40dewhirst.com.au>.
> For more options, visit https://groups.google.com/d/__optout
> <https://groups.google.com/d/optout>.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/CAFWa6tJu_uVd5c%3DvWsVEpa84CGWh-K0WS_xQnHrGs33iEMoZHQ%40mail.gmail.com
> <https://groups.google.com/d/msgid/django-users/CAFWa6tJu_uVd5c%3DvWsVEpa84CGWh-K0WS_xQnHrGs33iEMoZHQ%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Mike Dewhirst

unread,
Mar 29, 2015, 8:12:56 AM3/29/15
to django...@googlegroups.com
On 29/03/2015 9:22 PM, Gabriel - Iulian Dumbrava wrote:
> In case you hide/not display the save/delete button you must also double
> check in the delete view if the current user has the right permissions
> to delete the item. It's pretty easy to add a post button in firebug, or
> even trigger the post using a different method.

You are correct. That was a bad idea!

Thanks

Mike
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/5245622d-04a2-435d-9efb-cd61f824953b%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/5245622d-04a2-435d-9efb-cd61f824953b%40googlegroups.com?utm_medium=email&utm_source=footer>.

Ezequiel

unread,
Mar 29, 2015, 11:06:03 AM3/29/15
to django...@googlegroups.com
On Sunday, March 29, 2015 at 5:08:06 AM UTC-3, Mike Dewhirst wrote:
A perhaps better solution would be to disable the Save and Delete
widgets/actions in particular circumstances.

How can I do something like this?


The quick&dirty way I did this was:

In settings.py create a tuple indicating read-only users: 

ADMIN_READONLY_USERS = (5826, 6074, )


In the custom UserProfile model I added: 

@property
def is_admin_readonly(self):
    return self.id in settings. ADMIN_READONLY_USERS


And them override /admin/change_form.html file template and change submit_buttons_bottom block:

{% block submit_buttons_bottom %}
    {% if request.user.is_admin_readonly is False %}
        {% submit_row %}
    {% endif %}
{% endblock %}
 
As you said in your last post you should also include some check in the view to prevent malicious users to POST form in other ways like using firebug.

Ezequiel.

Melvyn Sopacua

unread,
Mar 29, 2015, 12:12:10 PM3/29/15
to django...@googlegroups.com
On Sunday 29 March 2015 22:04:23 Mike Dewhirst wrote:
> On 29/03/2015 7:28 PM, Julo wrote:
> > Maybe you can add an interface for the models that are importants
> > and hook to the save/delete signal a interfaced function called
> > CanSave() And you check the premisson he has, if not allowed rais
> > an exception, and don't save.
> Julo
>
> I like that. I'll dig a bit deper. It would be best to do it in the
> model but I'm not sure how I can get request.user from there.

It's convenient to have all information in one place, but...permission
validation is a view and by extension form action. Data correctness and
integrity validation belongs in the model.

That said, maybe I'm not getting your workflow correctly, but to me it
seems that if request.user is not owner, some fields should be readonly.
And this is what ModelAdmin.get_readonly_fields() is for.

If this doesn't work for you, can you explain why?
--
Melvyn Sopacua

Mike Dewhirst

unread,
Mar 29, 2015, 5:47:51 PM3/29/15
to django...@googlegroups.com
On 30/03/2015 2:06 AM, Ezequiel wrote:
> On Sunday, March 29, 2015 at 5:08:06 AM UTC-3, Mike Dewhirst wrote:
>
> A perhaps better solution would be to disable the Save and Delete
> widgets/actions in particular circumstances.
>
> How can I do something like this?
>
>
>
> The quick&dirty way I did this was:

Ezequiel

Lots of clues there ...

Thanks

Mike

>
> In settings.py create a tuple indicating read-only users:
>
> ADMIN_READONLY_USERS = (5826, 6074, )
>
>
> In the custom UserProfile model I added:
>
> @property
> def is_admin_readonly(self):
> return self.id in settings. ADMIN_READONLY_USERS
>
>
> And them override /admin/change_form.html file template and change
> submit_buttons_bottom block:
>
> {% block submit_buttons_bottom %}
> {% if request.user.is_admin_readonly is False %}
> {% submit_row %}
> {% endif %}
> {% endblock %}
> As you said in your last post you should also include some check in the
> view to prevent malicious users to POST form in other ways like using
> firebug.
>
> Ezequiel.
> http://flickrock.com/mikelpierre
>
> --
> You received this message because you are subscribed to the Google
> Groups "Django users" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to django-users...@googlegroups.com
> <mailto:django-users...@googlegroups.com>.
> To post to this group, send email to django...@googlegroups.com
> <mailto:django...@googlegroups.com>.
> Visit this group at http://groups.google.com/group/django-users.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/django-users/5efe4687-3aef-4f76-988d-789f98f8ae59%40googlegroups.com
> <https://groups.google.com/d/msgid/django-users/5efe4687-3aef-4f76-988d-789f98f8ae59%40googlegroups.com?utm_medium=email&utm_source=footer>.

Mike Dewhirst

unread,
Mar 29, 2015, 7:48:51 PM3/29/15
to django...@googlegroups.com
On 30/03/2015 3:11 AM, Melvyn Sopacua wrote:
> On Sunday 29 March 2015 22:04:23 Mike Dewhirst wrote:
>> On 29/03/2015 7:28 PM, Julo wrote:
>>> Maybe you can add an interface for the models that are importants
>>> and hook to the save/delete signal a interfaced function called
>>> CanSave() And you check the premisson he has, if not allowed rais
>>> an exception, and don't save.
>> Julo
>>
>> I like that. I'll dig a bit deper. It would be best to do it in the
>> model but I'm not sure how I can get request.user from there.
>
> It's convenient to have all information in one place, but...permission
> validation is a view and by extension form action. Data correctness and
> integrity validation belongs in the model.

I agree and that's where I always put it.

>
> That said, maybe I'm not getting your workflow correctly, but to me it
> seems that if request.user is not owner, some fields should be readonly.
> And this is what ModelAdmin.get_readonly_fields() is for.

Working on this as we speak!

>
> If this doesn't work for you, can you explain why?

I think it will work!

More later.

Thank you Melvyn

Mike

>

Mike Dewhirst

unread,
Mar 30, 2015, 3:53:23 AM3/30/15
to django...@googlegroups.com
On 30/03/2015 10:48 AM, Mike Dewhirst wrote:
> On 30/03/2015 3:11 AM, Melvyn Sopacua wrote:
>> On Sunday 29 March 2015 22:04:23 Mike Dewhirst wrote:

snip

>>
>> That said, maybe I'm not getting your workflow correctly, but to me it
>> seems that if request.user is not owner, some fields should be readonly.
>> And this is what ModelAdmin.get_readonly_fields() is for.
>
> Working on this as we speak!
>
>>
>> If this doesn't work for you, can you explain why?
>
> I think it will work!
>
> More later.


It works beautifully!

# in utils.py
def is_public_domain(self, request, obj=None):
"""Return readonly fields. That is the 'modified' fields or all
fields depending on whether the substance is (a) yours or (b) not
yours but the substance is in the public domain. self is the inline
model and obj is the "master" model.
"""
if obj is not None:
if obj.company == get_user_company(request.user):
return self.ro_fields
else:
return self.model._meta.get_all_field_names()

# in substance admin.py
from utils import is_public_domain

class SubstanceAdmin(admin.ModelAdmin):

def get_queryset(self, request):
""" Limit the substances visible to just those owned by the
company of which the user is a member or those in the public
domain.
"""
qs = super(SubstanceAdmin, self).get_queryset(request)
return qs.filter(
Q(access_status=PUBLIC_DOMAIN,)
| Q(company=get_user_company(request),)
)
...
ro_fields = ['modified', 'modified_by',]
get_readonly_fields = is_public_domain
...
class SynonymsInline(admin.StackedInline):
model = Synonym
fk_name = 'substance'
ro_fields = ['modified', 'modified_by',]
get_readonly_fields = is_public_domain
...


Thank you very much Melvyn.

Cheers

Mike

>
> Thank you Melvyn
>
> Mike
>
>>
>


--

Climate Pty Ltd
PO Box 308
Mount Eliza
Vic 3930
Australia +61

T: 03 9787 6598
M: 0411 704 143


Mike Dewhirst

unread,
Mar 30, 2015, 7:10:56 PM3/30/15
to django...@googlegroups.com
I am moved to say thank you to all the devs from day 1 for Django.

It is a marvellous piece of work.

All of it.

Thanks

Mike
Reply all
Reply to author
Forward
0 new messages