Overriding Django Admin's "add" button for related fields - python

I've been trying to override the functioning of +(add) button for related fields in Django admin to open a new tab instead of a popup. I looked at the RelatedObjectLookup.js to see how it works but still stuck at implementing the same functioning by opening a new tab. Is there any way to implement such a thing or to render the form 'inline'?

To open related fields +Add button in a new tab, you have to set target="_blank" attribute for all those links.
Override admin/change_form.html from your admin.
class BookAdmin(admin.ModelAdmin):
add_form_template = 'book/admin/change_form.html'
In the html, set the required attribute and remove
{% extends 'admin/change_form.html' %}
{% load static %}
{% block admin_change_form_document_ready %}
{{ block.super }}
<script type="text/javascript">
(function($) {
$(document).ready(function() {
classes = document.getElementsByClassName('add-related');
for (i=0; i<classes.length; i++) {
// set target to blank
classes[i].setAttribute('target', '_blank');
// remove the class to prevent django listeners overriding click on link
classes[i].classList.remove("related-widget-wrapper-link");
};
});
})(django.jQuery);
</script>
{% endblock %}
Now when you click on related fields, it will open in a new tab.
An alternate option is to use inline admin as mentioned here in docs.

Related

How to create a "save and add another" button and show on Wagtail admin model page

Was wondering if it is possible to add a custom button within a Wagtail model page that will allow me to save (create) the current data fields and move on to another page. The "save and add another" button was already available in django and I want to have something like that on the Wagtail model page.
Thanks.
Wagtail has a few ways to customise content that is shown in the various Wagtail modeladmin views.
Code Example
Step 1 - create a custom CreateView
This will override the get_context_data method to provide the create_url, we could do this simpler (the context already has access to the instance) but it is nice to be explicit.
Override the get_success_url method to allow for a URL param of next to be set, if that exists the next URL will be this instead of the default behaviour.
products/wagtail_hooks.py
from wagtail.contrib.modeladmin.views import CreateView
# ... other imports
class CustomCreateView(CreateView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['create_url'] = self.create_url
return context
def get_success_url(self):
next = self.request.GET.get('next')
if next:
return next
return super().get_success_url()
class ProductsModelAdmin(ModelAdmin):
create_view_class = CustomCreateView
Step 2 - Override the template to add a new button
Wagtail modeladmin uses a template path override approach, you will need to create a new template that aligns with your desired override (for example we may just want to override the product model create view only.
templates/modeladmin/products/products/create.html -> this says to override the create template for the products model within the products app.
Add the code below and check it is working, sometimes the templates path can be a bit tricky so ensure you can see the new button AND that the button has the data-next attribute that should be your create URL.
templates/modeladmin/products/products/create.html
{% extends "modeladmin/create.html" %}
{% load i18n wagtailadmin_tags %}
{% block form_actions %}
{{ block.super }}
<div class="dropdown dropup dropdown-button match-width">
<button type="submit" class="button button-secondary action-save button-longrunning" data-next="{{ create_url }}" data-clicked-text="{% trans 'Saving…' %}">
{% icon name="spinner" %}<em>{% trans 'Save & add another' %}</em>
</button>
</div>
{% endblock %}
Step 3 - Add JS to modify the form action URL on click
The next step requires a bit of JavaScript, we want to attach a different behaviour to the 'save and add another' button.
The JS used here does not require jQuery and should work in IE11
Once the DOM is loaded (which means JS can do things), find all the buttons with the data-next attribute.
Add a listener to the 'click' of each of those buttons which will dynamically update the form action attribute with the extra URL part.
This extra URL part gets read by the get_success_url method in the custom create class but only on a successful save, otherwise you will stay on the page and be able to fix errors.
{% block extra_js %}
{{ block.super}}
<script>
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll('[data-next]').forEach(function(button) {
button.addEventListener('click', function(event) {
var form = document.querySelector('.content-wrapper form');
form.action = form.action + '?next=' + button.dataset.next;
});
});
})
</script>
{% endblock %}

Selecting all checkboxes in Django

I'm trying to find an elegant solution without recourse to using JQuery or JS. Is there anyway that one can perform a select all on fields that are options in a model?
I'm not so keen to use: django checkbox select all
I've seen it hinted at: https://groups.google.com/forum/#!topic/django-users/dzdiZ9TLR5g
But never been able to find anything that would easily allow me to provide a select all directly from Django. Does anyone know if this is possible to switch on? Or is JS the only way to perform this?
I note this answer earlier: select all rows in django_tables2
But is there a way to avoid this approach because I may not know what why fields are - hence, if I have more than one field on each page - i.e. multiple names.
Here is my easy solution with multiple Forms and multiple Fields :
{% for form in formset %}
<div>
{% for field in form %}
{{ field }}
{% for check in field|slice:":1" %}
<input type="checkbox" onClick="toggle(this,'{{ check.name }}')"/>
Select All
{% endfor %}
{% endfor %}
</div>
{% endfor %}
Each checkbox of one Field has the same name - so js can work with it.
Note that all the fields of this example form are checkboxes.
JS Code :
<script type="text/javascript" >
function toggle(source,name) {
checkboxes = document.getElementsByName(name);
for (var i = 0,
n = checkboxes.length; i < n; i++) {
checkboxes[i].checked = source.checked;
}
}
</script>
I extended this solution for django :
How to implement "select all" check box in HTML?
Any solution you write within Django would involve overriding widget renders to output html that included javascript/jquery anyway. I don't think there is any getting round it.
Edit: to answer your comment, the way I would personally do it is create a SlaveCheckboxWidget that could do something as simple inherit from the standard checkbox widget but change the css class name to "slave-checkbox" or similar, then have a MasterCheckboxWidget that includes a bit of jquery to select all (".slave-checkbox") and toggle them.
More on customising django widgets here

how to add javascript to the change_form.html for block content in django

I want to add a javascript to the model's change_form.html template inherited from the django admin's template. I would like to show/display some of the model's attributes based on the type of login. Eg. User can be a customer or staff group member. Can anyone guide? How to use it in the {% block content %} tag of the change_form.html
You don't need content block, there is head block, you could place your js there. But that js thing that prevents some users from modifying some fields could be hacked easily.
{% block extrahead %}
{{ block.super }}
<script type="text/javascript">
$(function() {
{% if user.get_profile.is_customer %}
$('#id_of_field_block').hide();
{% endif %}
});
</script>
{% endblock extrahead %}
Also you could change change_form.html template and override content block, getting original file content as source and change fieldset template fieldset.html (or you could override only fieldset.html, I'm not sure). This template iterates over fields and there you could add some checking.

django-tinymce: Using different options for different instances

I have a model with an HTMLField which can be edited with a TinyMCE control in the admin. However, I would like to be able to give different options to TinyMCE depending on which instance of the model is being edited. How can I do this?
(For example, if the user is editing the SimplePage instance whose slug is technologies, I want TinyMCE to use the default CSS file, but if its editing the SimplePage whose slug is ticker, I want to use a different CSS file.)
I guess you have a Media class in your ModelAdmin with additional JavaScript and CSS for the admin (like here). Your JavaScript doesn't know the slug of the current object, let's change that.
First create one of the following directory structures in your templates directory: "admin/your-app" for an app or "admin/your-app/your-model" for a specific model only (see the Django documentation).
Then create a file "change_form.html" in that directory and put something similar to this in there:
{% extends "admin/change_form.html" %}
{% block extrahead %}
<script type="text/javascript" charset="utf-8">
var MYAPP_objectSlug = "{{ original.slug|escapejs }}";
</script>
{{ block.super }}
{% endblock %}
This will extend the usual "change_form.html" of the admin and extend the extrahead block to set a JavaScript variable with your object slug (original is your object).
Now adapt the JavaScript file that does the tinyMCE.init to use a different CSS file based
on the JavaScript variable MYAPP_objectSlug.
if (MYAPP_objectSlug == "ticker"){
var MYAPP_cssFile = "../css/special.css"; // change to your path
} else {
var MYAPP_cssFile = "../css/default.css"; // change to your path
}
tinyMCE.init({
...
content_css : MYAPP_cssFile,
...
});

Can anyone describe how I implement the ckeditor in django.contrib.flatpages?

Can anyone describe how I implement the ckeditor in django.contrib.flatpages?
Few steps to get this done. First, make sure ckeditor.js is being served up in some way from django. Info on this can be found at http://docs.djangoproject.com/en/1.2/howto/static-files/#howto-static-files. For this example, I will be serving it from 127.0.0.1:8000/js/ckeditor/ckeditor.js.
You'll need to override the standard flatpage change form template. In your templates directory, create a file in the following subdirectory: <your templates dir>/admin/flatpages/flatpage/change_form.html
Create the following text inside:
{% extends "admin/change_form.html" %}
{% block extrahead %}
{{ block.super }}
<script type="text/javascript" src="/js/ckeditor/ckeditor.js"></script>
<script type="text/javascript" charset="utf-8">
var $ = jQuery = django.jQuery.noConflict(); // Use djangos jquery as our jQuery
</script>
<script type="text/javascript" src="/js/ckeditor/adapters/jquery.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready( function(){
$( 'textarea' ).ckeditor({
"skin":"kama",
"width" : 850,
// "toolbar" : "Basic", // uncomment this line to use a basic toolbar
});
});
</script>
{# Adding some custom style to perty thing up a bit. #}
<style type="text/css">
div>.cke_skin_kama{
width: 100%;
padding: 0!important;
clear: both;
}
</style>
{% endblock %}
The first few lines contain django's default text for the extrahead block. The rest of the script imports the ckeditor javascripts and uses django's already-imported jQuery with the ckeditor jQuery adapter. Finally, we end up forcing some style upon the page, as by default, things look a bit messy.
From here, you can quickly change the toolbar by implementing different options in the ckeditor call. Going to a simple toolbar is likely something you'll need if non-technical people are going to edit these flatpages. You can just uncomment that line in the above code to implement that.
Found good solution:
This is a little tricky. I've got admin.autodiscover() in my urls.py,
so it's automatically going to create an admin for flatpages as
defined in django.contrib.flatpages. I certainly don't want to go
hacking apart something that came with Django, nor do I want to give
up the convenience of autodiscover.
http://www.elidickinson.com/story/django-flatpages-and-ckeditor/2011-11
For a solution without template hacking check this page:
http://johansdevblog.blogspot.it/2009/10/adding-ckeditor-to-django-admin.html
I report here the example if the link goes down.
This is a simple model.
from django.db import models
class SampleModel(models.Model):
title = models.CharField(max_length=50)
text = models.TextField()
def __unicode__(self):
return self.title
This is how to add ckeditor support to a specific type of field, in this case a TextArea.
from sampleapp.models import SampleModel
from django.contrib import admin
from django import forms
from django.db import models
class SampleModelAdmin(admin.ModelAdmin):
formfield_overrides = { models.TextField: {'widget': forms.Textarea(attrs={'class':'ckeditor'})}, }
class Media:
js = ('ckeditor/ckeditor.js',) # The , at the end of this list IS important.
admin.site.register(SampleModel,SampleModelAdmin)
At this point the ckeditor.js will check for all the textarea with the class attribute set to "ckeditor".

Categories