Django Admin template override - python

I am following the tutorial on django website to create my first django app.
Now I am stuck trying to override Django Admin template.
My project directory is this:
First I tried creating a new admin template in the surveys app. It works.
Then, I tried with the override function. For this I created at surveys/admin.py the following code:
from django.contrib.admin import AdminSite
from django.utils.translation import ugettext_lazy
class SurveysAdminSite(AdminSite):
site_header = ugettext_lazy('Test administration')
surveys_admin_site = SurveysAdminSite()
And add to computationalMarketing/urls.py the following:
from .admin import surveys_admin_site
urlpatterns = [
path('admin/', surveys_admin_site.urls, name='admin'),
]
It doesn't work, so I search, and tried something different. Add this same previous code to surveys/urls.py. Neither works. Then I rollback the changes to save the code to computationalMarketing/urls.py, but this time I changed the code from surveys/admin.py to computationalMarketing/admin.py (in fact I created the file because it doesn't exists.
It works and now I see the site header that I want, but I get You don't have permission to edit anything. I have seen that is something related with superuser creation, but until now I was able to admin my surveys app without problem, so I believe in some solution related with override properly the admin.py at surveys app
Does anybody knows why this does not works as expected?

You are overriding the complete admin site. You may want to override only specific templates and keep using the default admin site.
A first approach to solve your problem is to add models to your admin site:
surveys/admin.py:
from django.contrib.admin import AdminSite
from django.utils.translation import ugettext_lazy
from surveys.models import OneModel, OtherModel
class SurveysAdminSite(AdminSite):
site_header = ugettext_lazy('Test administration')
surveys_admin_site = SurveysAdminSite()
surveys_admin_site.register(OneModel)
surveys_admin_site.register(OtherModel)
And maybe use ModelAdmin objects to add advanced behavior.
Hope this helps.

Related

How we can create generic API in django?

I'm totally new to Django.
I'm creating Generic API. List and Create are working well but ID is not visible in DELETE and UPDATE.
This is my views.py file
from rest_framework import generics
from rest_framework import mixins
This is my urls.py code
from .views import GenericApiView
path('inspections-templates', GenericApiView.as_view()),
But as I check documents the ID is not available for UPDATE & DELETE.
Can you please someone guide me on how can I update and delete the record.
Thanks in advance
Actually you have to pass that id in your URL.Try this in urls.py:
path('inspections-templates/<int:id>/', GenericApiView.as_view()),
Because in UPDATE and DELETE user id is required to perform PUT and DELETE.

Where to best execute database operations using Django framework?

Thanks in advance for any help. I am new to django specifically as well as web development in general. I have been trying to teach myself and develop a website using the Django framework and while everything is working so far, I am not sure if I am really doing things in the best possible way.
Typically, within my django app, I will have certain points where I want to modify the contents of my database model in some way. A typical use case is where I have button on my site that says "Add a post":
models.py:
from django.db import models
# data model import
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
post = models.CharField(max_length=1024)
urls.py:
from django.urls import include, path
from . import views
urlpatterns = [
path('', views.post, name='new_post'),
path('post_exec', views.post_exec, name='post_exec'),
]
views.py:
from django.contrib import messages
from django.shortcuts import render, redirect
# import data models
from django.contrib.auth.models import User
from .models import Post
def post(request):
# make sure the user has privileges
user = User.objects.get(id = request.user.id)
if not user.is_superuser:
return redirect('home')
return render(request, 'post.html')
def post_exec(request):
# make sure the user has priveledges
user = User.objects.get(id = request.user.id)
if not user.is_superuser:
return redirect('home')
# create the post
new_post = Post.objects.create()
new_post.user = user
new_post.post = request.POST['post']
new_post.save()
# this could redirect to a confirmation page or something too
return redirect('home')
You can see what I am doing here is that everytime I need a change to the database due to user input, I am executing the code to do the requested database change in a django view and then redirecting to another page following completion of the database change. While this works, it feels kind of clunky (you end up needing pages that you never actually use), it seems pretty sketchy in terms of security (although I am checking for credentials on each page), and I imagine there is a better way to do this.
Can any experience Django developers offer some advice for how I should be best structuring my project for these specific types of common database operations?
The Django's views is a good place to organize the project's CRUD system so users can manage their data. You can use the class-based views to group the GET, POST etc requests. Also there are better ways of using the authorization system with the login_required() decorator, the LoginRequiredMixin class and other solutions that you can rich here

Template for custom type in mezzanine

I've been learning Mezzanine and currently experimenting with themes. So far, this is what I have:
-projectA <- mezzanine app
-theemeA/ <- custom theme
-admin.py
-models.py
-__init__.py
-templates/
-pages/
-about-us.html
-aboutus.html
-index.html
-base.html
-about-us.html
TheemeA is included in the project and I can see the customized index and base.html. Now I want to create a series of pages that have a common look that will go in the About US section on the site. I want this new content type to extend the base.html.
I created models.py in TheemeA; pulled from here and slightly altered for testing. I know I will need to rework some of this.
from django.db import models
from django.utils.translation import ugettext_lazy as _
from mezzanine.pages.models import Page, RichText
class AboutUs(Page, RichText):
"""
About Us Base Content Type
"""
add_toc = models.BooleanField(_("aboutus"), default=False,
help_text=_("Include a list of child links"))
class Meta:
verbose_name = _("About Us")
verbose_name_plural = _("About Us")
I've also included this new model in the admin.py for TheemeA and it shows up as a content type in the UI with a text edit field. Now I want to create a custom template that extends the model so I can customize the appearance. However, I am not able to get mezzanine to pull up my template. I've been looking at this and it's not helping.
I've tried multiple variations of about-us.html and aboutus.html in that folder and in folder called pages, but can't get it to work. Can someone please provide a bit of guidance?
Your model looks fine
admin.py should be similar to:
from .models import AboutUs,
from mezzanine.pages.admin import PageAdmin
admin.site.register(AboutUs, PageAdmin)
Create a page instance in the admin UI.
Usually a space in the title becomes - eg 'About Us' becomes 'about-us.html.
And it needs to be within the pages directory.

django admin registering dynamic model from action

I have strange problem. In admin.py I can say:
admin.site.register(MyModel)
and this is obviously fine. Now I want this model to be loaded automatically as an result of user action:
def user_action_from_admin_panel(......):
.....
admin.site.register(MyModel)
MyModel class gets shows up in the admin as plain text without links.
Any ideas to solve this?
May be you need this
from django.core.urlresolvers import clear_url_caches
from django.utils.importlib import import_module
def user_action_from_admin_panel(......):
.....
admin.site.register(MyModel)
reload(import_module(settings.ROOT_URLCONF))
clear_url_caches()
models created dynamically will not show up in the admin unless their app_labels match up with packages listed in INSTALLED_APPS
This is again by design, and should not be considered a bug.
Make sure you are adding app_label while creating a model
model = create_model('DynamicModel', app_label='existing_app')
Also reload your url conf so that new model gets links
# after creating model
from django.utils.importlib import import_module
reload(import_module(settings.ROOT_URLCONF))
Source: https://code.djangoproject.com/wiki/DynamicModels#Admininterface
I have black links if I don't permissions to add/change.
Try re-define your admin class:
class MyModelAdmin(admin.ModelAdmin):
def has_add_permission(self, request):
return True
def has_change_permission(self, request):
return True
...
admin.site.register(MyModel, MyModelAdmin)
The reason possibly is because Django couldn't find any URL match with that model for admin section. Hence, that model line in admin area will be set at disabled and no additional add or edit links.
For some cases, your code for registering models are triggered after the building of admin URLs (django.contrib.admin.site.AdminSite.get_urls()).A workaround solution is to update the whole admin urlpatterns of the global URLs, or using a Django apps named django-quickadmin, it will automatically load all custom models into admin without making any additional code.

Django: Remove "view on site" button in the admin User change form

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.

Categories