Disable Django model admin buttons (save, delete, etc.) - python

I would like to disable all buttons that they are present into the submit-row (save, save and edits, delete, etc...) after the click on one of they.
I started to try to override the change_form for admin model. Something
like this:
class MyAdmin(admin.ModelAdmin):
change_form_template = 'admin/reports/models/change_form.html'
into the admin/reports/models/change_form.html file I added this code:
{% extends "admin/change_form.html" %}
{% load i18n admin_urls %}
<div class="submit-row">
{% block submit-row %}
{% if show_save %}<input type="submit" value="{% trans 'Save' %}" class="default" name="_save">{% endif %}
{% if show_delete_link %}
{% url opts|admin_urlname:'delete' original.pk|admin_urlquote as delete_url %}
<p class="deletelink-box">{% trans "Delete" %}</p>
{% endif %}
{% if show_save_as_new %}<input class="myclass" type="submit" value="{% trans 'Save as new' %}" name="_saveasnew">{% endif %}
{% if show_save_and_add_another %}<input class="myclass" type="submit" value="{% trans 'Save and add another' %}" name="_addanother">{% endif %}
{% if show_save_and_continue %}<input class="myclass" type="submit" value="{% if can_change %}{% trans 'Save and continue editing' %}{% else %}{% trans 'Save and view' %}{% endif %}" name="_continue">{% endif %}
{% if show_close %}{% trans 'Close' %}{% endif %}
{% endblock %}
</div>
But, I don't see any changes (class="myclass" there aren't).
My next changes will be the js code to disable all buttons at on click... but now I'm blocked on this first problem.
I use python 3 and Django 2

If you are trying to add classes to the inputs just so you can use JavaScript to disable them upon click, that's not necessary. You should be able to select the buttons pretty easily without adding classes to them. For example, using jQuery included in the Django admin:
django.jQuery(".submit-row :submit").attr("disabled", "disabled")
If you truly need to override the rendering of the button HTML, you should look at overriding the blocks submit_buttons_bottom and submit_buttons_top:
{% extends "admin/change_form.html" %}
{% if save_on_top %}your submit buttons here{% booking_submit_row %}{% endblock %}{% endif %}
{% block submit_buttons_bottom %}your submit buttons here{% endblock %}
If you want to avoid repeating yourself with this approach, consider using a custom submit-line.html and building a custom templatetag to output your submit buttons in the above template, like so:
from django.contrib.admin.templatetags.admin_modify import submit_row
from django.template import Library
register = Library()
#register.inclusion_tag("admin/<your app>/<your model>/submit_line.html", takes_context=True)
def custom_submit_row(context):
ctx = submit_row(context)
original = context["original"] if "original" in context else None
# adjust as you need based on your context
return ctx
Now in your custom change_form.html you can use:
{% extends "admin/change_form.html" %}
{% load custom_admin_modify %}
{% if save_on_top %}{% custom_submit_row %}{% booking_submit_row %}{% endblock %}{% endif %}
{% block submit_buttons_bottom %}{% custom_submit_row %}{% endblock %}
Hope that helps - good luck!

I could remove "SAVE" button, "Save and continue editing" button, "Save and add another" button and "Delete" button from "Change person" page with this code below for "admin.py". But, when I removed "SAVE" button, "Close" button appeared then I don't know how to remove "Close" button:
# "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):
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:

Related

Custom button not performing action in Django admin

Am trying to add another custom button, and at the same time I want it to trigger certain processes in Django-admin with templates, below is how I have implemented the template :
{% extends 'admin/custominlines/change_form.html' %}
{% load i18n %}
{% block submit_buttons_bottom %}
{{ block.super }}
{% if request.GET.edit %}
<div class="submit-row">
<input type="submit" value="Custom button" name="_transition-states">
</div>
{% endif %}
{% endblock %}
The button does appear however, when I click it , it doesn't show the print I have inserted in the response_change() function in the admin.py, what am I missing here :
def response_change(self, request, obj):
if '_transition-states' in request.POST:
print("am working")
return super().response_change(request, obj)
When I click the button it just routes back to the previous page.

User login and Django OTP

I want the user to login using three fields, username, password and OTP ONLY.
Here is my .html file
{% extends "base.html" %}
{% load static %}
{% load bootstrap4 %}
{% block head %}
<title>HomePage</title>
{% endblock %}
{% block content %}
<div class="container">
<div style='width:500px;margin:0 auto;' class="panel panel-default">
<div class="panel-body">
<h1>Login</h1>
<form method="POST" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Login</button>
{% endbuttons %}
</form>
</div>
</div>
</div>
{% endblock %}
Here is the browser view of the .html file
I want to remove Otp Device and Otp Challenge as they are unnecessary for my case.
Here is my models.py file
from django.contrib.auth.models import AbstractUser
class ProjectUser(AbstractUser):
# add additional fields in here
def __str__(self):
return self.email
Here is my urls.py file
from django_otp.forms import OTPAuthenticationForm
from django.contrib.auth.views import LoginView
urlpatterns = [
path('user_login/', LoginView.as_view(template_name="accounts_app/user_login.html",
authentication_form=OTPAuthenticationForm), name='user_login'), ]
Here is the reference I used
Solution 1:
The straight forward way is to Subclass OTPAuthenticationForm to replace the form fields used for otp_device and otp_challenge by using the HiddenInput widget. In your app folder, create a new python file called forms.py and add the following
from django_otp.forms import OTPAuthenticationForm
from django import forms
class SimpleOTPAuthenticationForm(OTPAuthenticationForm):
otp_device = forms.CharField(required=False, widget=forms.HiddenInput)
otp_challenge = forms.CharField(required=False, widget=forms.HiddenInput)
In your urls.py file inside your app, add the import and replace the LoginView with the following
from .forms import SimpleOTPAuthenticationForm
path('user_login/', LoginView.as_view(template_name="accounts_app/user_login.html",
authentication_form=SimpleOTPAuthenticationForm), name='user_login'),
Solution 2:
Alternative option but requires more styling to fit the bootstrap form warning and other features. Start with using the following
Only display the fields you want in your template: Since otp_device and otp_challenge are not required, you can just leave them out. Use {% bootstrap_field form.<field> %} for each of the fields you want to display instead of {% bootstrap_form form %}. See here for all the options to customise the rendering of each field. Errors for each field can be displayed with {{ form.errors.<field> }} but you have to style them yourself.
In your case
{% bootstrap_field form.username %}
{% bootstrap_field form.password %}
{% bootstrap_field form.otp_token %}

Django - wagtail Inline panel CSS edits

I am using wagtail on django and creating a page where I will create questions and allow students to answer it by typing in the input box. I am using inline field where the wagtail gives me the option to choose various input option like(singleline, multiline, radio button etc). For my questions, I am choosing a singleline option. The problem I am facing is with the text box, the text box is too small, is there a way to increase the size(width) of the text box.
model.py
class QuestionPage(AbstractForm):
intro = RichTextField(blank=True)
thank_you_text = RichTextField(blank=True)
points_for_this_activity = models.IntegerField(blank=True, default=0)
content_panels = AbstractEmailForm.content_panels + [
FieldPanel('intro', classname="full"),
InlinePanel('form_fields', label="Create your question"),
FieldPanel('points_for_this_activity', classname="title"),
FieldPanel('thank_you_text', classname="full"),
]
question.html
<div class ="container">
<h1>{{ page.title }}</h1>
{% if user.is_authenticated and user.is_active or request.is_preview %}
{% if form %}
<div>{{ page.intro|richtext }}</div>
<form action="{% pageurl page %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input class="btn-md btn-primary" type="submit">
</form>
{% else %}
<div>You have already answered this. Go to the next question.</div>
{% endif %}
{% else %}
<div>To fill in the form, you should log in.</div>
{% endif %}
{% endblock %}
</div>

Django - Have a class based view and Crispy form parrallell to each other on same page

Using Django, I am making a site that displays tickets. On the login page, I want it to where there is a preview area of existing tickets to the left of the login form. I am able to make both pages seperately, but I can not figure out how to make them co-exist side by side. I am also using jinja2 templating. I've been trying at this for 2 days, and I have read the tutorials over and over. Here is how I have it:
url.py:
urlpatterns = patterns('',
url(r'', include('ticket_app.urls')),
url(r'^accounts/login/$', login, {'authentication_form': forms.LoginForm}, name='login'),
)
views.py Note: Request is the database table object:
class PreviewList(ListView):
model = Request
form.py:
class LoginForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(LoginForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-8'
self.helper.form_tag = False
self.helper.layout = Layout(
Field('username', placeholder="Username", css_class='input-xlarge'),
Field('password', placeholder="Password", css_class='input-xlarge'),
FormActions(
Submit('login', 'Login', css_class="btn-primary"),
)
base.html(short version):
<h1>{% block page_title %}Preview of tickets{% endblock page_title %}</h1>
{% block preview %}
{% endblock preview %}
{% block login %}
{% endblock login %}
preview.html:
{% extends 'base.html'%}
{% block preview %}
<div class="span6">
<ul class="list-group">
{% if object_list %}
{% for item in object_list %}
<li class="list-group-item">{{item.date_due}} - {{item.desctription}}
<span class="badge">
{% if item.user_assigned %}
<span class="badge"style="color:green"> assigned </span>
{% else %}<span class="badge" style="color:red">unassigned</span>
{% endif %}
</li>
{% endfor %}
{% else %}
<p>Yay! No ticket requests found!</p>
{% endif %}
</div>
{% endblock preview %}
login.html:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block login %}
<div class="container" style="padding-bottom: 70px;">
<div class='row'>
<div class='col-md-6 col-md-offset-3'>
<div class="well">
<legend>Sign in to Site</legend>
<form method="post" action="{% url 'django.contrib.auth.views.login' %}" class="form-horizontal">
{% crispy form %}
<input type="hidden" name="next" value="{{ next }}"/>
</form>
</div>
</div>
</div>
</div>
{% endblock login %}
There are multiple ways to accomplish this. For example, you could create your own login view that 'extends' the one from django.contrib.auth:
from django.contrib.auth.views import login
def login_with_preview(request, extra_context=None, *args, **kwargs):
if extra_context is None:
extra_context = {}
extra_context['object_list'] = Request.objects.all()
return login(request, authentication_form=LoginForm, extra_context=extra_context, *args, **kwargs)
And then use login_with_preview in your url conf and merge your two templates.
Another possibility is to create a custom template tag. This is especially nice if you want to show this preview list in different places:
#register.inclusion_tag("yourapp/preview_list.html")
def ticket_preview_list():
return {'object_list': Request.objects.all()}
preview_list.html should only contain the template code that, right now, is in your preview template block. You need to make sure that template doesn't extend base.html; that would mess things up. The usual template tag setup steps apply.
You could create one view that renders the login form and the preview data

Extending parent blocks from included template, looking for a workaround

I have a site with some ajax pages. If user types in a browser /login/, he should get a full rendered template, extended from a base template. But if user clicks a login button, $('#content').ajax('/login/'); called, so i don't need to render a full template.
I.e. i have this (login_ajax.html):
{% load i18n %}
{% block title %}
{% trans "Login" %}
{% endblock %}
{% block content %}
{% include "social.html" %}
{% endblock %}
In login.html:
{% extends "base.html" %}
{% block ajax_content %}
{% include "login_ajax.html" %}
{% endblock %}
Simple login view:
def login(request):
c = Context({'user': request.user})
if request.is_ajax():
return render_to_response('login_ajax.html', c, context_instance=RequestContext(request))
return render_to_response('login.html', c, context_instance=RequestContext(request))
This problem refers to documentation of include tag:
The include tag should be considered as an implementation of “render
this subtemplate and include the HTML”, not as “parse this subtemplate
and include its contents as if it were part of the parent”. This means
that there is no shared state between included templates – each
include is a completely independent rendering process.
But i don't want to place title name in a view, or place it twice in login.html and login_ajax.html also.
I think you need to move {% block title %} back out into login.html then make two ajax calls. One to override {% block ajax_content %} and one to override {% block title %}. You could use the same pattern for overriding {% block title %} as you've used for overriding {% block ajax_content %}, but you'd probably manage without actually creating a new title.html template.
I can't see any other way round your problem.
Ok, i've found a simple solution. In fact, the problem is in question: "to extend from base, or not to extend".
In fact, i don't care a template from what login.html should be extended. So, for ajax request, the parent template will be empty.html, and for default request, it will be base.html. So, i'll point a parent template in the view:
def login(request):
c = Context({'user': request.user, 'extends': 'empty.html'})
if request.is_ajax():
return render_to_response('login.html', c, context_instance=RequestContext(request))
c['extends'] = 'base.html'
return render_to_response('login.html', c, context_instance=RequestContext(request))
The empty.html just contain a placeholder for a block:
{% block content %}{% endblock %}
And here is login.html:
{% extends extends %}
{% load i18n %}
{% if extends != 'empty.html' %}
{% block title %}{% trans "Login" %}{% endblock %}
{% else %}
<div style="display: none;" class="ajax-title">{% trans "Login" %}</div>
{% endif %}
{% block content %}
{% include "social.html" %}
{% endblock %}
Also, i suppose, there is a way to turn login.html into a snippet, that could be included using with. i.e. {% include 'snippet.html' with extends='base.html' %}

Categories

Resources