Firstly thanks for taking the time to read my problem
I tried to add a custom button to my admin changeList view, but it always gives me Page not found (404)
This my modelAdmin code:
class MyModelAdmin(admin.ModelAdmin):
class Media:
js = ('admin/js/additional_js.js',)
model = MyModel
def get_urls(self):
urls = super(MyModelAdmin, self).get_urls()
analyze_url = patterns('',
(r'^(?P<pk>\d+)/analyze/$',
self.admin_site.admin_view(self.analyze_view))
)
return analyze_url + urls
def analyze_view(self, request, pk):
# some code here
HttpResponseRedirect(
reverse('admin:myapp_MyModel_change', args=(pk,))
)
My jQuery code for adding custom link to change list view:
(function($) {
$(document).ready(function($) {
$(".object-tools").append('<li>Analyze</li>');
});
})(django.jQuery);
when I click my custom link it gives me this:
MyModel object with primary key '3/change/analyze' does not exist.
I see that the link does not point to the view
Can someone help me fix this issue.
Thanks
In Django 1.9, the admin change url has changed to /admin/<app>/<model>/<pk>/change/ (release notes).
Therefore, it doesn't make sense to link to href="analyze/" in your html. The relative url is treated as /admin/<app>/<model>/3/change/analyze, not /admin/<app>/<model>/3/analyze as you expect.
You can probably fix the problem by changing the link to:
<a href="../analyze/" ...
Ideally, it would be better to reverse the url rather than having a relative link. I think this would make the code less fragile.
Since you are using Django 1.9, you can update your code to use a list rather than patterns:
from django.conf.urls import url
analyze_url = [
url(r'^(?P<pk>\d+)/analyze/$', self.admin_site.admin_view(self.analyze_view)),
]
Related
I encountered a bit weird problem and not really sure why it happens.
This is my model:
class PostIndexPage(RoutablePage):
max_count = 1
parent_page_types = []
intro = RichTextField(blank=True)
content_panels = Page.content_panels + [
FieldPanel('intro', classname="full")
]
#route(r'^posts/$')
def main(self, request):
from django.shortcuts import render
return render(request, self.get_template(request), self.get_context(request))
I defined route in that model but it seems to have no effect.
http://127.0.0.1:8000/post-index-page/ - the old url is still working the old way
http://127.0.0.1:8000/posts/ - but this one doesn't (404, not found).
Do you have any idea what I'm doing wrong?
URL routes defined by #route on a RoutablePage are relative to the normal path of the page, as determined by its slug and position in the page tree. If you created a routable page with the slug post-index-page, the posts route will be found at /post-index-page/posts/.
If you want a view that remains at a completely fixed URL, you can always define that as a standard Django view.
I have installed django-contact-form, and the default url to the form is 'contact/'. But, I already have my own contact page, let's call it 'contact_us/', I wish to include the form inside 'contact_us/', using the url and view I already have for 'contact_us/'.
The usual way to do that is by using {% extends 'contact_us' %}, but if I do this, and remove my original 'contact_us/' url, it gives me an ReverseError.
The way I think that would be possible is if sent the ContactFormView (from django-contact-form) as context to 'contact_us/', but I think that's not possible because each view has it's own url.
I want to know a way to be able to put the form easily inside of any template. There's probably a simple way to do that and I don't know.
You can call the ContactFormView from the contact_us by:
1. By overriding the url
urls.py
from contact_form.views import ContactFormView
from django.views.generic import TemplateView
urlpatterns = [
# ... other URL patterns for your site ...
url(r'^contact_us/$',
ContactFormView.as_view(),
name='contact_form'),
url(r'^contact_us/sent/$',
TemplateView.as_view(template_name='contact_form/contact_form_sent.html'),
name='contact_form_sent'),
]
2. If you want to customize this view
In views.py
from contact_form.views import ContactFormView
class CustomContactFormView(ContactFormView):
# Customize any function that you want to
template_name = 'custom_template.html'
urls.py
from views import CustomContactFormView
urlpatterns = [
# ... other URL patterns for your site ...
url(r'^contact_us/$',
CustomContactFormView.as_view(),
name='contact_form'),
]
I am using Django 1.8 on Python 3.4.3
I have a Model called FormUpload and I am adding a ModelAdmin for the same in admins.py in the same application "mca".
#admin.register(FormUpload)
class FormUploadAdmin(admin.ModelAdmin):
def upload_form(self, request):
context = dict(
# Include common variables for rendering the admin template.
self.admin_site.each_context(request),
# Anything else we want in the context...
)
return TemplateResponse(request, "admin/formupload.html", context)
def get_urls(self):
urls = super(FormUploadAdmin, self).get_urls()
upload_admin_urls = [
url(r'^upload/$', self.admin_site.admin_view(self.upload_form)),
]
logger.debug(upload_admin_urls)
return upload_admin_urls + urls
This should have been available at /admin/mca/upload/ . However when I go the URL I get a 404 saying the current URL doesn't match anything
Here is the output of the debug
RegexURLPattern None ^upload/$
(Removed few things that will make the display weird for the debug).
Please notice the None. If the other URLs are listed out there is a method name where the None is.
What am I doing wrong here ? I am following the approach suggested in django documentation - https://docs.djangoproject.com/en/1.8/ref/contrib/admin/#modeladmin-methods
Django 1.8 / Python 3.4
I have a website.html that displays entries from my database, each of which is identified by its ID. Now, at the end of each displayed entry I have a link to an "edit" view, looking like this:
<td>edit</td>
The link is working fine and leads to the correct view:
def edit(request, object_id):
implemented in views.py. There some code is executed correctly too, and in the view's last line I have:
return redirect('website.html')
Obviously, after the chosen entry has been edited I want my website.html with the edited entry being displayed in the browser again: 127.0.0.1:8000/website/. However, what happens is that I get a Page not found (404) error, with the information:
Requested URL 127.0.0.1:8000/website/2/website.html
The "2" here is the ID of the entry.
Maybe I have the wrong idea about how redirects work, but I was assuming that instead of calling the respective view's url from url.py it would open the url provided in the redirect() function?!
What happens, though, is that this url gets appended to the view's url!
Here is my urls.py:
from django.conf.urls import include, url
from django.contrib import admin
from www.list.views import website, edit
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^website/$', website, name="website"),
url(r'^website/(?P<object_id>\d+)/$', edit, name="edit"),
]
I'm pretty sure the third url entry is causing the problem but I have absolutely no idea how to change it for the redirect to work. Also, the edit view doesn't have an actual website (template), but I'm supposed to provide a URL for it, so this was the best I could think of.
The way this should work is: Click the "edit" link on website.html, the code from the edit-view is being executed, and, afterwards, the website.html with the change in the database entry gets displayed again.
^^ How to achieve this? Any help is appreciated!
Redirect uses names or absolute URLS. You should either use the name of your URL:
return redirect('website') # since name="website"
or an absolute URL, like:
return redirect('/website/')
you can use the reverse function instead of redirect
from django.core.urlresolvers import reverse
return reverse('website')
I found the mistake and the solution:
At the end of the edit-view it's correct to write "return redirect('website')". However, just as I assumed, the URL of edit in urls.py was wrong.
Instead of
url(r'^website/(?P<object_id>\d+)/$', edit, name="edit"),
it should just be
url(r'^(?P<object_id>\d+)/$', edit, name="edit"),
Thank you nonetheless!
get_absolute_url() method is cool, but in some cases is not needed. django.contrib.auth.models.User has it set by default, this cause my projects to have a broken link in the admin.
How can I prevent that from happening?
In one of my old projects I set a custom template in which I removed the html of the button, it doesn't sound like a good solution that would scale though. Anything better?
If you click on Django 1.7 link, the site will tell you that "Its an insecure version of Django that is no longer supported. Please upgrade to a newer release!"
For Django 1.9, following solution works fine as mentioned in the Django documentation
in myapp/admin.py
from django.contrib.admin import AdminSite
class MyAdminSite(AdminSite):
# Disable View on Site link on admin page
site_url = None
This can be done, per model, as of django 1.7.
# myapp/admin.py
from django.contrib import admin
from myapp.models import MyModel
class MyModelAdmin(admin.ModelAdmin):
view_on_site = False
admin.site.register(MyModel,MyModelAdmin)
Instead of monkey patching you can hide the button on the client side using JavaScript. The HTML of the view on site button looks like this:
<li>View on site</li>
If you just hide the anchor tag you will get part of the round button appearing as that is applied on the li tag. Now unfortunately there is no easy way to use css to select that specific li tag since it doesn't have a class, name or id on it. So we can use jquery which gives you more control on your selectors. Put the following in your static folder. For example in the location static/admin/user_change_form.js
django.jQuery( document ).ready(function($) {
$(".viewsitelink").parent().css('display', 'none')
});
Your admin.py could then look something like this:
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from django.contrib.admin import site
class CustomUserAdmin(UserAdmin):
class Media:
js = ['admin/user_change_form.js']
site.unregister(User)
site.register(User, CustomUserAdmin)
UPDATE
A feature was added in Django 1.7 ModelAdmin.view_on_site which lets you not display the “View on site” link.
I know this is old but I came across it when I had to do the same thing.
A better solution would be to monkey patch it in an accounts/admin.py file when you have accounts in INSTALLED_APPS.
admin.site.unregister(User)
# We don't want a broken View on Site link. Thanks for that, contrib.auth!
del User.get_absolute_url
admin.site.register(User, MyUserAdmin)
Django 2.0 above you can add in default admin
admin.site.site_url = None
Above trick worked for me very well.
As a last resort, I have a monkey_patch app at the bottom of my INSTALLED_APPS which modifies the built in django contrib apps in ways I haven't found better ways to modify such as username length, default admin forms, __unicode__, etc.
Just watch your back when you upgrade django / in general.
from django.contrib.auth.models import User
del User.get_absolute_url
I'm using Django 1.4 and Marwan Alsabbagh's solution worked fine for me. Although, when opening/refreshing User change form there was a short blink. That is because JQuery hides this button only when page is loaded.
To solve this minor issue I used CSS to hide the whole .change-form block. After page is loaded this block's visibility is restored by means of JQuery. So, my code looks like this:
admin.py:
class Media:
js = ['js/admin/user_change_form.js']
css = {'all': ('css/admin/user_change_form.css',)}
...static/css/admin/user_change_form.css
.change-form {
visibility: hidden;
}
...static/js/admin/user_change_form.js
/* Do not show 'View on site' button in User change form */
django.jQuery( document ).ready(function($) {
$(".viewsitelink").parent().css('display', 'none')
/* restore visibility of the page (hidden in css to avoid blinking) */
$(".change-form").css('visibility', 'visible')
});
use
admin.site.site_url = "your url here"
in url.py of ur main app to modify the "visit site" on django page
and
for "view_on_site" removal
use
view_on_site = False in ur class with display_list for
Inside your app config (apps.py) do this:
class MyAppConfig(AppConfig):
def ready(self):
admin.site.site_url = None
Works in Django 4.0 as well.