Django admin override save behaviour - django

Using the Django Admin I would like to have a 'confirmation box' when someone presses the save button in the admin interface i.e. you are trying to update "name or age or sex" to "foo or 23 or m".

You could overwrite the get_form method in your model admin to add another check or the save() method to create a warning. You could also add an intermediate page (like the delete view does)...
i.e.
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(MyModelAdmin, self).get_urls()
my_urls = patterns('',
(r'^my_view/$', self.my_view)
)
return my_urls + urls
def my_view(self, request):
# custom view which should return an HttpResponse
pass
Read more: http://www.ibm.com/developerworks/web/library/os-django-admin/index.html?ca=drs
If you want a JavaScript method then I imagine you could just overwrite the admin view for that very easily and add a simple confirmation when save is click i.e.
Dave
As the OP is very slim, with no code examples I cannot really help beyond this general answer.

Related

Django Class-based Views - ListView if User Is Authenticated, FormView if User is Not, Same URL

When a visitor is logged out and visits my homepage, I want them to see a registration form. When the user logs in, they will be redirected back to the homepage, but it will show a listview-like view.
Is the best way to achieve this to subclass both ListView and FormView and override each method necessary to get the behavior I want? Or is there a better way? Checking if the user is authenticated in each method doesn't seem like the Django way to do this. Hoping there's a smart design pattern approach for doing this that I don't know about.
class HomepageView(ListView, FormView):
def get_template_names(self):
if self.request.user.is_authenticated:
return ['homepage/loggedin_index.html']
else:
return ['homepage/loggedout_index.html']
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated:
...
else:
...
def ... and so on
My must-have requirement is that the URL for both logged-in and logged-out users must resolve to the root URL since it's a homepage.
from django.urls import path
from .views import HomepageView
app_name = 'homepage'
urlpatterns = [
path('', HomepageView.as_view(), name='index'),
]
There is also a CreateListView that do what you want. And then you can change its form to the one you want.

Use ModelAdmin form in any view

I've spent quite a bit of time setting up the ModelAdmin for my models, and I want to be able to basically reuse it elsewhere on my site, i.e., not just in the admin pages.
Specifically, I want to further subclass the ModelAdmin subclass I created and override some of the methods and attributes to be non-admin specific, then use that second subclass to create a form from a model in one of my ordinary (non-admin) views. But I'm not sure how to get the form out of the ModelAdmin subclass or how to bind it to a specific model.
So for instance, my admin.py looks something like this:
from django.contrib import admin
class MyAdmin(admin.ModelAdmin):
fields = ['my_custom_field']
readonly_fields = ['my_custom_field']
def my_custom_field(self, instance):
return "This is my admin page."
And this works real nice. Now in I want to subclass this as:
class MyNonAdmin(MyAdmin):
def my_custom_field(self, instance):
return "This is NOT my admin page!"
Now I want to use the MyNonAdmin in a view function to generate a form bound to a particular model, and use that form in a template. I'm just not sure how to do that.
Update
I found how to render the form, but it doesn't include any of the readonly_fields. Rendering the form looks like this:
def view_func(request, id):j
res = get_object_or_404(ModelClass, pk=int(id))
admin = MyNonAdmin(ModelClass, None)
form = admin.get_form(request, res)
return render(request, 'my_template.html', dict(
form=form,
))
But as I said, it only renders the form itself, not the other readonly_fields which is what I really care about.

Django Admin: Change URL links in change_list page

I would like to change the URL links in the change_list page from default to something else.
I have gone through the code of Admin and have to say, I need help.
Can anyone help me out???
Example:
In the above pic, I want to change the link under "Abhilash Nanda" to some other link. This for all the rows I may have. I would like to go from this change list page to another change list page where I can again list the rows from a related table to the clicked link.
You can edit the admin URL by extending the admin model. Like this.
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
urls = super(MyModelAdmin, self).get_urls()
my_urls = patterns('',
(r'^my_view/$', self.my_view)
)
return my_urls + urls
def my_view(self, request):
# custom view which should return an HttpResponse
pass
You can here the full documentation here.

Override Django Admin URLs for Specific Model?

First a little background:
I have an Event model that has various event_types. I want to break one of those event types, 'Film', into it's own admin. I have the basic functionality in place: a proxy model inheriting from Event, named Film, a custom manager for that proxy model that filters it to only 'film' event types, and it's own ModelAdmin.
The problem is with the reverse. I now need to filter out films from the main Event admin. I don't want to alter the Event model or its default manager, because the impact would be too widespread. So, I tried creating another proxy model, EventAdminProxy, with the sole purpose of providing a filtered list of events in the admin. I then register this model, instead of Event, with the existing ModelAdmin.
This obviously works, but it has the unfortunate side-effect of altering the URLs in the admin. Instead of the changelist being at "/admin/event/event/", it's now at "/admin/event/eventadminproxy/".
What I'm trying to do is keep this setup, but also keep the old URL. I've tried overloading the ModelAdmin's get_urls method, but from what I can tell, you can't control the full URL there, only what comes after the "/app_label/model_class/" part.
I thought about overriding it in the main urls.py, but can't figure out an acceptable view to tie into. The actual views are only available on the instantiated ModelAdmin object, not the class itself.
Any ideas of how override the URL being used in the admin?
Looking at the Django source, the admin URLs are built in two places, in the ModelAdmin instances, and in the AdminSite instances.
The part you want to change is built in the AdminSite instance (django.contrib.admin.sites.AdminSite), you can subclass that and override the get_urls method. If you look at the second half of the method you'll see this:
# Add in each model's views.
for model, model_admin in self._registry.iteritems():
urlpatterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, model._meta.module_name),
include(model_admin.urls))
)
There it is adding the model's ._meta.module_name which is just the model's name lowercased (django.db.models.options.Options.contribute_to_class).
An easy way out is to override the Site's get_urls method and add a dict or special case for the Proxy model so it uses a different url instead of model._meta.module_name, something along the lines:
class MyAdminSite(AdminSite):
module_name_dict = {
EventAdminProxy: 'myfunkymodulename'
}
def get_urls(self):
base_patterns = super(MyAdminSite, self).get_urls()
my_patterns = patterns('',)
for model, model_admin in self._registry.iteritems():
if model in self.module_name_dict:
module_name = self.module_name_dict[model]
my_patterns += patterns('',
url(r'^%s/%s/' % (model._meta.app_label, module_name),
include(model_admin.urls))
)
return my_patterns + base_patterns
You could override the queryset-method of your EventModelAdmin and filter the queryset so that Film-Events get excluded.
Something similar to this:
class EventAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(EventAdmin, self).queryset(request)
return qs.exclude(event_type='film')
You could also subclass ChangeList and override the url_for_result() method to customise change urls, (learned from another answer), e.g.:
from django.contrib.admin.views.main import ChangeList
class FooChangeList(ChangeList):
def url_for_result(self, obj):
return '/foos/foo/{obj.pk}/'
class FooAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
return FooChangeList
Adapted example for the question:
from django.contrib.admin.views.main import ChangeList
from django.urls import reverse
class FilmAdmin(admin.ModelAdmin):
def get_changelist(self, request, **kwargs):
class FilmChangeList(ChangeList):
def url_for_result(self, obj):
return reverse('admin:events_event_change', args=(obj.pk, ))
return FilmChangeList

Implementing a generic protected preview in Django

I'm trying to add some kind of generic preview functionality to the Django admin. Opposed to Django's builtin preview-on-site functionality this preview should only be visible to logged in users with specific permissions.
All my content models have the same base class which adds a status like published and unpublished. Obviously unpublished content doesn't appear on the website, but editors should still be able to preview an unpublished site.
I read about class based views in the upcoming Django 1.3 release which might be well suited to implement it in a generic way. With Django 1.2 i can't seem to come up with a solution without touching any single view and adding specific permission checks. Has anyone done something like that before?
I believe the Django Admin already provides a "show on site" option to the admin pages of any models which provides a get_absolute_url() method. Using decorators, it should be possible to do this in a generic way across models
class MyArticleModel(Article): #extends your abstract Article model
title = .....
slug = ......
body = ......
#models.permalink
def get_absolute_url(self): # this puts a 'view on site' link in the model admin page
return ('views.article_view', [self.slug])
#------ custom article decorator -------------------
from django.http import Http404
from django.shortcuts import get_object_or_404
def article(view, model, key='slug'):
""" Decorator that takes a model class and returns an instance
based on whether the model is viewable by the current user. """
def worker_function(request, **kwargs):
selector = {key:kwargs[key]}
instance = get_object_or_404(model, **selector)
del kwargs[key] #remove id/slug from view params
if instance.published or request.user.is_staff() or instance.author is request.user:
return view(request, article=instance, **kwargs)
else:
raise Http404
return worker_function
#------- urls -----------------
url(r'^article/(?(slug)[\w\-]{10-30})$', article_view, name='article-view'),
url(r'^article/print/(?(id)\d+)$',
article(view=generic.direct_to_template,
model=MyArticleModel, key='id'),
name='article-print-view'
)
#------ views ----------------
from django.shortcuts import render_to_response
#article(MyArticleModel)
def article(request, article):
#do processing!
return render_to_response('article_template.html', {'article':instance},
xontext_instance=RequestContext(request) )
Hope this is informative (and hopefully correct ;)