Django TabularInline override admin temaplate - python

I have a class that inherits from TabularInline. I have overridden the template, how do I pass data to this template to output it?
class commercial_proposal_productadmin(SortableTabularInline):
model = commercial_proposal_product
extra = 0
template = "admin/tabular.html"
i tried to use the change_view function, but the world value is not displayed in the template
def change_view(self, request, object_id, form_url='', extra_context=None):
extra_context = {'world':'hello'}
return super().change_view(request, object_id, form_url, extra_context=extra_context)

The template code is required. But in order to display something in the template, it is necessary to output data from the context with a special tag of the Django template engine:
{{ world }}
https://docs.djangoproject.com/en/4.1/ref/templates/language/

Related

Overriding get_context_data() is not working in child view

I am trying to override get_context_data() in a child class-based view to send more contextual data to the template, but it doesn't work. As a sample I am sending a test variable, but it is not rendered in the template.
class ProductList(LoginRequiredMixin, View):
template_name = 'product/product_scroll.html'
def get(self, request, *args, **kwargs):
#...
return render(request, self.template_name, data)
class Feed(ProductList):
template_name = "product/feed.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['test'] = 'sometestinfo'
return context
But in the template:
<p> Prueba: {{test}} </p>
is empty.
It's not working because you override get. Therefore the whole built in functionality of the view - including calling get_context_data - is bypassed. You almost never need to define the get or post methods.

Cannot hide "Save and add another" button in Django Admin

I would like to hide all the "Save" buttons in Django's Admin's Change Form, for a specific model, when certain conditions are met. Therefore, I override the changeform_view method in the relevant ModelAdmin, like so:
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
obj = collection_management_MammalianLine.objects.get(pk=object_id)
if obj:
if not (request.user.is_superuser or request.user.groups.filter(name='Lab manager').exists() or request.user == obj.created_by):
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
extra_context['show_save_and_add_another'] = False
else:
pass
else:
pass
return super(MammalianLinePage, self).changeform_view(request, object_id, extra_context=extra_context)
With this code, I can successfully hide the "Save" and "Save and continue" buttons, but not the "Save and add another" one. I can see that submit_line.html contains the following three lines:
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save" />{% endif %}
{% if show_save_and_add_another %}<input type="submit" value="{% trans 'Save and add another' %}" name="_addanother" />{% endif %}
{% if show_save_and_continue %}<input type="submit" value="{% trans 'Save and continue editing' %}" name="_continue" />{% endif %}
My question is: why can I hide the "Save" and "Save and continue" buttons, but not the "Save and add another" one? Even though the relevant templatetag (show_save_and_continue) is in the template.
You may override render_change_form method in ModelAdmin subclass.
In this method obj is available as argument and you can check certain conditions.
class OrderAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
context.update({
'show_save': False,
'show_save_and_continue': False,
'show_save_and_add_another': False,
'show_delete': False
})
return super().render_change_form(request, context, add, change, form_url, obj)
EDIT:
As of Django 3.1, the approach from the original question should simply work:
...
extra_context['show_save_and_add_another'] = False
...
See this Django commit for details.
OLD ANSWER:
summary
Some additional options:
Set save_as=True on your ModelAdmin. As described in the docs, this will replace the "Save and add another" button with a "Save as new" button. This may not be ideal for all cases, but it is the simplest solution as far as I know.
Apply a monkey patch for the has_add_permission method on your ModelAdmin, so it returns False during the call to super().change_view (or changeform_view).
Override the TemplateResponse.content (docs) to simply hide (or remove) the submit-row element, which contains the buttons, from the HTML.
details option 1
The simplest option is to set save_as=True on the ModelAdmin. This will replace the "Save and add another" button with a "Save as new" button. As a result, assuming the other save buttons have been disabled, any changes made to the current object can only be saved as a new object. The current object will remain unchanged.
Basic implementation:
class MyModelAdmin(ModelAdmin):
save_as = True
# your other code here, such as the extended changeform_view
details option 2
The submit_line.html template shows which context variables are used to show/hide the save and delete buttons. Most of those context variables can be set via the extra_context in changeform_view (or change_view). However, as the OP showed, we cannot simply override show_save_and_add_another in that manner.
As pointed out in #Oluwafemi-Sule's answer,
show_save_and_add_another is set in admin_modify.py, which creates the context for submit_line.html.
Upon closer inspection of the source, it is tempting to override the has_add_permission context variable, because that determines the value of show_save_and_add_another. However, simply adding has_add_permission=False to extra_context does not work in Django < 2.1, because the change will be undone by the ModelAdmin.render_change_form method.
Fortunately, rather than overriding the template or patching the template tags, we can simply trick Django into thinking that has_add_permission is False, using a monkey patch in change_view:
def change_view(self, request, object_id=None, form_url='', extra_context=None):
# use extra_context to disable the other save (and/or delete) buttons
extra_context = dict(show_save=False, show_save_and_continue=False, show_delete=False)
# get a reference to the original has_add_permission method
has_add_permission = self.has_add_permission
# monkey patch: temporarily override has_add_permission so it returns False
self.has_add_permission = lambda __: False
# get the TemplateResponse from super (python 3)
template_response = super().change_view(request, object_id, form_url, extra_context)
# restore the original has_add_permission (otherwise we cannot add anymore)
self.has_add_permission = has_add_permission
# return the result
return template_response
This also works if you replace change_view by changeform_view, as used by the OP (source).
Note: obviously, this option can only be used if has_add_permission=False does not interfere with other things in your change view.
details option 3
Based on this example from the docs, it is also possible simply to hide (or even remove) the submit-row from the rendered HTML for the change_view:
def change_view(self, request, object_id=None, form_url='',
extra_context=None):
# get the default template response
template_response = super().change_view(request, object_id, form_url,
extra_context)
# here we simply hide the div that contains the save and delete buttons
template_response.content = template_response.rendered_content.replace(
'<div class="submit-row">',
'<div class="submit-row" style="display: none">')
return template_response
As noted, we can also remove the submit-row section altogether, but that is a bit more work.
This is simpler than option 2, but more fragile.
If no one is against, then I publish my little monkey patch.
This can be inserted anywhere and called up from the settings file (preferably at the very end).
# Hide "save_and_add_another" button
from django.contrib.admin.templatetags import admin_modify
submit_row = admin_modify.submit_row
def submit_row_custom(context):
ctx = submit_row(context)
ctx['show_save_and_add_another'] = False
return ctx
admin_modify.submit_row = submit_row_custom
The other keys are checked for in the passed context except show_save_and_continue. Django always sets this directly.
'show_save_and_add_another': (
context['has_add_permission'] and not is_popup and
(not save_as or context['add'])
),
You can patch the submit_row template tag function to first check the context dictionary for show_save_and_add_another.
#register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
"""
Display the row of buttons for delete and save.
"""
change = context['change']
is_popup = context['is_popup']
save_as = context['save_as']
show_save = context.get('show_save', True)
show_save_and_continue = context.get('show_save_and_continue', True)
show_save_and_add_another = context.get('show_save_and_add_another', False)
ctx = Context(context)
ctx.update({
'show_delete_link': (
not is_popup and context['has_delete_permission'] and
change and context.get('show_delete', True)
),
'show_save_as_new': not is_popup and change and save_as,
'show_save_and_add_another': (
context.get('show_save_and_add_another', None) or
(context['has_add_permission'] and not is_popup and
(not save_as or context['add']))
),
'show_save_and_continue': not is_popup and context['has_change_permission'] and show_save_and_continue,
'show_save': show_save,
})
return ctx
Edit
Steps to patch the "admin/submit_line.html" inclusion tag
Create a templatetags folder at the same level of models.py and views.py
Create __init__.py in the templatetags folder
Copy django/contrib/admin/templatetags/admin_modify.py to templatetags/admin_modify.py.
Overwrite submit_row function definition with the one above.
Note that this is applicable for Django 2.0 and below.
For recent Django versions, find a context mix that allows this expression to be False.e.g.
has_add_permission and not is_popup and
(not save_as or add) and can_save
See values for the names used in the above expression.
I have an alternative way to hide the "Save and add another" button.
In form.py
class TestForm(forms.ModelForm):
class Media:
js = ('admin/yourjsfile.js',)
In yourjsfile.js
(function($) {
'use strict';
$(document).ready(function() {
// hide the "Save and add another" button
$("input[name='_addanother']").attr('type','hidden');
});
})(django.jQuery);
I had same issue for hiding that button.
I've tested it in Django 1.11 and it did work, but I think there are better ways to achieve it.
I propose (changed option 3 of djvg's answer) removing this html input element with regex as with this example:
class MyModelAdmin(admin.ModelAdmin):
def add_view(self, request, form_url='', extra_context=None):
template_response = super(MyModelAdmin, self).add_view(
request, form_url=form_url, extra_context=extra_context)
# POST request won't have html response
if request.method == 'GET':
# removing Save and add another button: with regex
template_response.content = re.sub("<input.*?_addanother.*?(/>|>)", "", template_response.rendered_content)
return template_response
_addanother is this html element's id
To remove "Save and add another" button, return "False" in "has_add_permission()" as shown below and I don't know why we cannot remove "Save and add another" button by setting "False" to "extra_context['show_save_and_another']" in "changeform_view()":
# "admin.py"
from django.contrib import admin
from .models import MyModel
#admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None): # Here
return False

Django DeleteView without confirmation template

I am using Django DeleteView in a template and I've created a url & view.
Is it possible to skip the process of loading the _confirm_delete template and just post the delete immediately.
DeleteView responds to POST and GET requests, GET request display confirmation template, while POST deletes instance.
You can send POST request, without confirmation with form like this:
<form method="POST" action="{% url "your_delete_url_name" %}">
{% csrf_token %}<input type="submit" value="DELETE">
</form>
If you do not want to have a link instead form button, use some javascript to make invisible form, that will be submitted on link click.
It is not good practice to use GET request for updating or deleting, but if you really insist you can shortcut get method in your class view to post, ie:
def get(self, *args, **kwargs):
return self.post(*args, **kwargs)
Or you can redefine get() method in your DeleteView:
class YourDeleteView(DeleteView):
model = YourModel
success_url = '<success_url>'
def get(self, request, *args, **kwargs):
return self.post(request, *args, **kwargs)
But be careful with that, ensure that this doesn't affect other functionality.
Yes, just change the next parameter. In your return response, make sure that the dictionary that you pass in is has something like this : { 'next': '/<your_path_here>}/' }, make sure you commit the changes before adding the next parameter. You might want to change your view's get and post functions.
Or you could only allow HTTP request method delete by routing the request directly to the delete method of your class.
from django.views.generic import DeleteView
from django.http import HttpResponseForbidden
class PostDeleteView(DeleteView):
model = Post
http_method_names = ['delete']
def dispatch(self, request, *args, **kwargs):
# safety checks go here ex: is user allowed to delete?
if request.user.username != kwargs['username']:
return HttpResponseForbidden()
else:
handler = getattr(self, 'delete')
return handler(request, *args, **kwargs)
def get_success_url(self):
username = self.kwargs.get('username')
success_url = str(reverse_lazy('post:user_home', kwargs={'username': username}))
return success_url
Let's say your URL looks like this:
path('posts/delete/<int:pk>/', PostDeleteView.as_view(), name='post_delete'),
For clarity why this works, you have to analyze the post and delete methods.
def post(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
"""
Call the delete() method on the fetched object and then redirect to the
success URL.
"""
self.object = self.get_object()
success_url = self.get_success_url()
self.object.delete()
return HttpResponseRedirect(success_url)
post just calls delete and delete gets the object and success URL, deletes the object, then redirects to the success URL you provided. pk_url_kwarg = 'pk' is why I showed the <int:pk> part in the URL.
You can override the get() method to behave exactly like the delete() method:
def get(self, request, *args, **kwargs):
return self.delete(request, *args, **kwargs)
See CCBV here: https://ccbv.co.uk/projects/Django/4.1/django.views.generic.edit/DeleteView/
All you have to do is override the get_success_url method of your delete view. Then it will directly delete the object from the DB. Eg:
class YourView(DeleteView):
model = YourModel
def get_success_url(self):
return reverse('your_redirect_view')

How can i change the Django admin template on the fly

I have the Django Admin model like this
class STUDENTAdmin(ModelAdmin):
change_list_template = "students/student_change_list.html"
Now i want to chnage that dynamically based on some request parameter
something like
if request.GET['foo']:
change_list_template = "students/student_change_list_other.html"
how can i do that
I think you need to override the changelist_view and act on the TemplateResponse() returned from it or change the variable holding that name just before that call.
The original function is like this
def changelist_view(self, request, extra_context=None):
# a lot of stuff happen here
return TemplateResponse(request, self.change_list_template or [
'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()),
'admin/%s/change_list.html' % app_label,
'admin/change_list.html'
], context, current_app=self.admin_site.name)
so I think that a code like
def changelist(self, request, extra_context=None):
if request.GET['foo']:
self.change_list_template = "students/student_change_list_other.html"
return super(STUDENTAdmin, self).changelist_view(request, extra_context)

Django Admin- disable Editing and remove "Save" buttons for a specific model

I have a Django Model which I wish to be only readonly. No adds and edits allowed.
I have marked all fields readonly and overridden has_add_permission in ModelAdmin as:
class SomeModelAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
Is there a similar has_edit_permission? Which can be disabled to remove "Save" and "Save and continue" buttons? And replace by a simple "Close and Return" button.
Django Documentation Only mentions only about read only fields not about overriding edit permissions.
For Django 1.11:
def has_add_permission(self, request, obj=None):
return False
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save_and_continue'] = False
extra_context['show_save'] = False
return super(YourModelAdmin, self).changeform_view(request, object_id, extra_context=extra_context)
The easiest method would be disabling respective permissions in ModelAdmin class. For example, I have a Cart model that I want an admin to only view (list and details) and all I did was to add the following functions to CartAdmin class to disable delete, change and add
class CartAdmin(admin.ModelAdmin):
list_display = ['listing']
def has_add_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
The three methods has_add_permission, has_change_permission and has_delete_permission are the ones that disable add button, add form, edit form and delete buttons in the admin
Here is a sample output when viewing a record details in the admin that has the above permissions disabled
As you can see the diagram above, you only have close button and the details are not displayed in a form
I had same problem. I fixed it in admin.py
from django.contrib.admin.templatetags.admin_modify import register, submit_row as original_submit_row
#register.inclusion_tag('admin/submit_line.html', takes_context=True)
def submit_row(context):
''' submit buttons context change '''
ctx = original_submit_row(context)
ctx.update({
'show_save_and_add_another': context.get('show_save_and_add_another',
ctx['show_save_and_add_another']),
'show_save_and_continue': context.get('show_save_and_continue',
ctx['show_save_and_continue']),
'show_save': context.get('show_save',
ctx['show_save']),
'show_delete_link': context.get('show_delete_link', ctx['show_delete_link'])
})
return ctx
In MyModelAdmin class, add following function
#classmethod
def has_add_permission(cls, request):
''' remove add and save and add another button '''
return False
def change_view(self, request, object_id, extra_context=None):
''' customize add/edit form '''
extra_context = extra_context or {}
extra_context['show_save_and_continue'] = False
extra_context['show_save'] = False
return super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)
Override the templates/admin/submit_line.html template and make the buttons whatever you want. You can do this for only the specific model by putting it in templates/admin/[app_label]/[model]/submit_line.html.
To conditionally show the default submit line or your custom submit line, override ModelAdmin.change_view, and add a boolean to extra_context:
class MyModelAdmin(admin.ModelAdmin):
...
def change_view(self, request, object_id, extra_context=None):
if not request.user.is_superuser:
extra_context = extra_context or {}
extra_context['readonly'] = True
return super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)
Updated answer using Django 1.8 (Python 3 syntax).
There are three things to do:
1) extend the admin change form template, adding an if to conditionally suppress the submit buttons
2) override admin.ModelAdmin.change_view() and set a context var for the template if to read
3) prohibit unwanted POST requests (from DOM hacking, curl/Postman)
MyProject/my_app/templates/admin/my_app/change_form.html
{% extends "admin/change_form.html" %}
{% load admin_modify %}
{% block submit_buttons_top %}{% if my_editable %}{% submit_row %}{% endif %}{% endblock %}
{% block submit_buttons_bottom %}{% if my_editable %}{% submit_row %}{% endif %}{% endblock %}
MyProject/my_app/admin.py (MyModelAdmin)
def change_view(self, request, object_id, form_url='', extra_context=None):
obj = MyModel.objects.get(pk=object_id)
editable = obj.get_status() == 'Active'
if not editable and request.method == 'POST':
return HttpResponseForbidden("Cannot change an inactive MyModel")
more_context = {
# set a context var telling our customized template to suppress the Save button group
'my_editable': editable,
}
more_context.update(extra_context or {})
return super().change_view(request, object_id, form_url, more_context)
I had the same problem - the easiest way to do this, is to include some custom JS.
In you admin.py file include
class Media:
js = ('/static/js/admin.js',)
Then in your admin.js file, include the following JS.
(function($) {
$(document).ready(function($) {
$(".submit-row").hide()
});
})(django.jQuery);
The row is gone - it should work in all versions of Django too.
This has been possible for a while. The names are has_add_permission, has_change_permission and has_delete_permission. See the django admin documentation for reference. Here is also an example:
#admin.register(Object)
class Admin(admin.ModelAdmin):
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
Aug, 2022 Update:
You can remove "SAVE" button, "Save and continue editing" button, "Save and add another" button and "Delete" button from a specific admin.
For example, this is "Person" model in "store" app below:
# "store/models.py"
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
def __str__(self):
return self.first_name + " " + self.last_name
class Meta:
verbose_name = "Person"
verbose_name_plural = "Person"
Then, this is "Person" admin in "store" app below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
pass
Then, this is how "Add person" page looks like:
Then, this is how "Change person" page looks like:
Then, this is how "Select person to change" page looks like:
Then, this is how "Person" admin on "Store administration" page looks like:
First, to remove "SAVE" button, set "False" to "extra_context['show_save']" in "changeform_view()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False # Here
return super().changeform_view(request, object_id, form_url, extra_context)
Then, "SAVE" button is removed from "Add person" page and "Change person" page. *Actually, "SAVE" button is replaced with "Close" buttom as shown below:
Next, to remove "Save and continue editing" button, set "False" to "extra_context['show_save_and_continue']" in "changeform_view()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False # Here
return super().changeform_view(request, object_id, form_url, extra_context)
Then, "Save and continue editing" button is removed from "Add person" page and "Change person" page as shown below:
Next, to remove "Save and add another" button, return "False" in "has_add_permission()" as shown below. *After this, "Add person" page can no longer be accessed:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None): # Here
return False
Then, "Save and add another" button is removed from "Change person" page as shown below:
Then, "ADD PERSON" button is also removed from "Select person to change" page as shown below:
Then, "➕ADD" button is also removed from "Person" admin on "Store administration" page as shown below:
Next, to remove "Delete" button, set "False" to "extra_context['show_delete']" in "changeform_view()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
extra_context['show_delete'] = False # Here
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None):
return False
Then, "Delete" button is removed from "Change person" page as shown below:
Actually, you can also remove "Delete" button by returning "False" in "has_delete_permission()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
# extra_context['show_delete'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None): # Here
return False
Then, "Delete" button is removed from "Change person" page as shown below:
Then, "Action" select dropdown box is also removed from "Select person to change" page as shown below:
In addition, you can make the fields on "Change person" page unchangeable by returning "False" in "has_change_permission()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
# extra_context['show_delete'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None): # Here
return False
Then, the fields on "Change person" page are made unchangeable as shown below:
Then, "✏️Change" button is replaced with "👁️View" for "Person" admin on "Store administration" page as shown below:
In addition, you can remove "Person" admin from "Store administration" page by returning "False" in "has_view_permission()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
extra_context = extra_context or {}
extra_context['show_save'] = False
extra_context['show_save_and_continue'] = False
# extra_context['show_delete'] = False
return super().changeform_view(request, object_id, form_url, extra_context)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
def has_view_permission(self, request, obj=None): # Here
return False
Then, "Person" admin is removed from "Store administration" page as shown below:
Finally, you can replace "changeform_view()" with "render_change_form()" which can also remove "SAVE" button, "Save and continue editing" button and "Delete" button with "context.update()" as shown below:
# "store/admin.py"
from django.contrib import admin
from .models import Person
#admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
# Here
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
context.update({
'show_save': False, # Here
'show_save_and_continue': False, # Here
# 'show_delete': False, # Here
})
return super().render_change_form(request, context, add, change, form_url, obj)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
def has_change_permission(self, request, obj=None):
return False
def has_view_permission(self, request, obj=None):
return False
You could try this package Django Admin View Permission. This package adds a view permission for the specified models and handles the other stuff automatically.
Based on the excellent answer from #mat_gessel, here's my solution:
The main differences are UX'y:
use messages and redirect (with reversing-admin-urls), rather than HttpResponseForbidden to prevent a save
define get_readonly_fields conditionally to prevent un-saveable inputs being displayed
Also:
override change_form.html app-wide, because read_only is such a useful, non-invasive enhancement
define has_delete_permission (may not be required by the OP)
test request.method != 'GET' to prevent PATCH and friends (not altogether sure if this is required, tbh)
my_app/admin.py
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.contrib import admin
from django.contrib import messages
class MyModelAdmin(admin.ModelAdmin):
# let's assume two fields...
fields = (field1, field2)
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
if object_id:
extra_context = extra_context or {}
extra_context['read_only'] = True
if request.method != 'GET':
messages.error(request, "Cannot edit a MyModel object")
return redirect(
reverse('admin:myapp_mymodel_change', args=[object_id])
)
return super(MyModelAdmin, self).changeform_view(request, object_id, extra_context=extra_context)
def has_delete_permission(self, request, obj=None):
return False
def get_readonly_fields(self, request, obj=None):
if obj:
# display all fields as text, rather than inputs
return (field1, field2)
else:
return []
admin/change_form.html
{% extends "admin/change_form.html" %}
{% load admin_modify %}
{# remove the save buttons if read_only is truthy #}
{% block submit_buttons_top %}{% if not read_only %}{% submit_row %}{% endif %}{% endblock %}
{% block submit_buttons_bottom %}{% if not read_only %}{% submit_row %}{% endif %}{% endblock %}
(Tested on Django 1.9: heads up: some imports have moved since then, eg reverse)

Categories