Personalized Cascading Selection Menu within the Django Administration Interface

I am struggling with implementing a dependent drop-down list within my Django admin page. I have a project foreign key in my Phase model and I would like the user to be able to select a project from the first drop-down, and then have the phases of that project displayed in the second drop-down.

What is the best approach to achieve this functionality? It would be beneficial if the items in the drop-downs could filter based on the value selected in the parent drop-down.

https://i.sstatic.net/9dc0K.png

class Project(models.Model):
    name                    = models.CharField(max_length=100, unique=True)
    short_name              = models.CharField(max_length=4, unique=True)
    slug                    = models.SlugField(max_length=100, allow_unicode=True, null=True, editable=False)
    location                = models.OneToOneField(Location, on_delete=models.SET_NULL, null=True, blank=False, verbose_name='موقعیت')
    start_date              = models.DateField(default=timezone.now, null=True, blank=True)      
    end_date                = models.DateField(default=timezone.now, null=True, blank=True)
    duration                = models.IntegerField(default=0, editable=False)

class Phase(models.Model):
    title                    = models.CharField(max_length=20)

class ProjectPhase(models.Model):
    project                 = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='phase')
    phase                   = models.ForeignKey(Phase, on_delete=models.CASCADE, related_name='project')
    start_date              = models.DateField(default=timezone.now)      
    end_date                = models.DateField(default=timezone.now)
    duration                = models.IntegerField(default=0, editable=True)

Answer №1

1. Importing a JavaScript Media File in ModelAdmin for Generaldata:

class YourModelAdmin(admin.ModelAdmin):    
    form = YourModelForm
    #list_display = ['your fields',]
    class Media:
        js = ("yourapp/selectajax.js",)

admin.site.register(YourModel, YourModelAdmin)

2. Creating a New JS File and Saving it in yourproject/yourapp/static/yourapp/ Directory or Another Suitable Location.

jQuery(function($){
    $(document).ready(function(){
        $("#id_project_select").change(function(){
            // console.log(obj.currentTarget.value);
            $.ajax({
                url:"/get_phases/",
                type:"POST",
                data:{project: $(this).val(),},
                success: function(result) {
                    console.log(result);
                    cols = document.getElementById("id_phase_select");
                    cols.options.length = 0;
                    for(var k in result){
                        cols.options.add(new Option(k, result[k]));
                    }
                },
                error: function(e){
                    console.error(JSON.stringify(e));
                },
            });
        });
    }); 
});

3. Creating a View to Handle AJAX Requests

@login_required
def get_phases(request):
    project = request.POST.get('project')
    phases = {}
    try:
        if project:
            prophases = Project.objects.get(pk=int(project)).phase
            phases = {pp.phase.title:pp.pk for pp in prophases}
    except:
        pass
    return JsonResponse(data=phases, safe=False)

4. Adding 'get_phases/' to urlpatterns.

Please note that you may need to customize some of the code based on your requirements.

Answer №2

The response provided by Blackdoor offers a viable solution that we have recently implemented. However, there are a few issues with it:

  1. It only triggers when the main select is changed, but I require the dependent select to also be filtered upon page load.
  2. It does not retain the selected item in the dependent select.

To address these concerns, I suggest making the following modifications to step 2 of the solution:service and sub_service are used instead of project / phase:

jQuery(function($){
    $(document).ready(function(){
        var clone = document.getElementById("id_sub_service").cloneNode(true);
        $("#id_service").change(function(){
            update_sub_services($(this).val(), clone)
        });
        update_sub_services($("#id_service").val(), clone)
    });

    function update_sub_services(service, clone) {
        $.ajax({
            url:"/chained_dropdowns/get_sub_services/",
            type:"GET",
            data:{service: service,},
            success: function(result) {
                var cols = document.getElementById("id_sub_service");
                cols.innerHTML = clone.innerHTML
                Array.from(cols.options).forEach(function(option_element) { 
                    var existing = false;
                    for (var k in result) {
                        if (option_element.value == k) {
                            existing = true
                        }
                    }
                    if (existing == false) {
                        $("#id_sub_service option[value='"+option_element.value+"']").remove();
                    }
                })
            },
            error: function(e){
                console.error(JSON.stringify(e));
            },
        });
    }
});

This revised approach removes unnecessary options from the dependent select rather than clearing all items and then refilling it. This method ensures that the selected property and any other custom properties are retained.

As I am not proficient in JS or jQuery, my modifications are in native JS. Any enhancements are welcome :)

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Changing the dynamic boundaries does not produce any results in the 'react-leaflet' library

Encountered an issue while trying to utilize the react-leaflet's bounds property. In my scenario, the application has predefined initial bounds in the state (referred to as fallback bounds) which are passed during the first component render. The archi ...

The issue I am experiencing within my PHP code is that the redirection to the gratitude page is not functioning correctly when utilizing

As a newcomer to PHP, I have been struggling with an example code from a website that is not redirecting to the thank you page as expected. Moreover, the email functionality is not working properly either. The code was sourced from: Upon downloading the f ...

The forceShutdown function in the node.js grpc server does not properly terminate the server

I encountered an issue with restarting a grpc node js server after shutting it down When attempting to restart the grpc server after forceShutdown, I received the following error: Error: Server is already running This snippet shows the problematic code: ...

Generating a highchart visualizing a pie chart using data from a local JSON file (OBJECT)

This is the JSON data from my JSON file {"diskspace":100,"diskspace.free":50,"time":8,"time.played":2,"controllers":25,"controllers.used":3, "controllers.new":10, "controllers.broken":12} I want to represent this JSON data in various forms of pie cha ...

Troubleshooting: Why the TableTools basic usage example is not functioning as

After replicating the code from http://datatables.net/release-datatables/extensions/TableTools/examples/simple.html in my Visual Studio project, I organized the files by saving two css files as style1.css and style2.css, along with three js files named sc ...

Differences in HTML animations can be seen when comparing Google Chrome to Microsoft Edge. Looking for a workaround for autoplay to ensure

My intro video animation is facing recording difficulties due to the autoplay policy in Google Chrome. It seems nearly impossible to capture it accurately. If only autoplay could function for an offline HTML file, my issue would be resolved. Unfortunately ...

Obtain the name of the object method from inside the method itself

Imagine having an object like this: var app = {} inside which there is a method: app = { initialize: function () { } } Is it possible to retrieve the name of the method 'initialize' from within the initialize() function without explicit ...

Sorting price and date with Vue in Laravel - A beginner's guide

Attempting to sort by price and by date. See the code below. I decided not to use the controller for this sorting. Is it feasible to implement it here? It's somewhat functional, but there are some issues. When selecting price, it doesn't seem to ...

Is it possible to trigger a re-render of a child component from its parent component in React without altering its props?

The issue at hand My parent component (X) is responsible for managing numerous states and child components. Within these children, there is an animated component (Y) - an avatar with various facial expressions that change in sync with its dialogue. Curr ...

Is there a way to retrieve the initial value from the second element within the JSON data?

Exploring JSON Data Collections In my JSON data, I have two collections: FailedCount and SucceededCount. { "FailedCount": [{ "FailedCount_DATE_CURRENT_CHECK": "2016-11-30 10:40:09.0", "FailedCount_DATE__CURRENT__CHECK": "10:40:09", "FailedCo ...

What are effective methods for tracing the origin of a JS_Parse_Error?

I am encountering a JS_Parse_Error when running my Express app. Despite commenting out all the new code I added, the error persists. Is there a method to pinpoint the exact line of Javascript causing this issue? Error at new JS_Parse_Error (/home/ch ...

Executing a function with a click, then undoing it with a second click

My goal is to trigger an animation that involves text sliding off the screen only when the burger icon is clicked, rather than loading immediately upon refreshing the page. The desired behavior includes activating the function on the initial click and then ...

Validate User Input Using jQuery to Check if Empty or Deleted

Users can enter text into an input field, and an image will be displayed if the input is valid or invalid. I want to remove or change the image when the input field is empty. Despite writing the following code, it doesn't seem to work: $('#inpu ...

In JavaScript, the function yields a proxy rather than an object

Let's say I have an array: const arr = ['one', 'two', 'three'] Now, imagine I have a function that is designed to take an array of strings and return an array of objects: const func = (arr) => arr.map(item => ({str ...

Iterative for Loop Vice Versa

I need to generate different combinations of numbers based on the input provided. For example, if the input is 3, there would be 8 combinations as shown below: 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1 For input 4, there would be 16 combinations. C ...

Creating a customizable triangle design using the power of three.js

Recently, I came across the three.js library and I'm eager to incorporate it into my projects. One idea I have is to create a 2D triangle shape with hand holds on the vertices which allow me to adjust the shape in real-time. Can anyone provide guidanc ...

The error message "todo.map is not a function" indicates that

Recently, I developed a to-do list application using the MERN stack. However, whenever I try to add a to-do, I encounter an error related to todo.map. The issue seems to be with importing some functions from a context for fetching data. The process shoul ...

Performing AJAX request without relying on jQuery for a bookmarklet

I'm facing a challenge with my bookmarklet that saves a webpage's URL along with the current user's ID from my application. The issue is that it only works on pages with jQuery available, and I need to perform an AJAX call without relying on ...

"Is there a way to retrieve "Lorem ipsum" information from a web service in JSON format

Does anyone know of any sample web services that serve JSON data? I'm looking to practice consuming JSON for testing and learning purposes. I would even be interested in downloading JSON files with images and other content to study offline. Perhaps th ...

What is the extent to which a scope variable will be accessible within the link function of Angular?

var $directive = angular.module('myApp', []); $directive.directive('myDirective', function(){ return { restrict: 'E', template: '<h4>{{title}}</h4>' compile: function(element, attrs){ ...