0

I have a Region and SubRegion ForeignKey in the Country Model. My SubRegion model also has a Region ForeignKey for the Region Model. I am having a hard time displaying the SubRegion dropdown in the Country Model on the basis of the Region selected.

What would be the best way to achieve it? It would be great if dropdowns were based on the value of their parent.

models.py

class Region(models.Model):
    name = models.CharField(max_length=20)
    is_active = models.BooleanField(verbose_name='is active?', default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

class SubRegion(models.Model):
    region = models.ForeignKey(Region, on_delete=models.CASCADE)
    name = models.CharField(max_length=50)
    is_active = models.BooleanField(verbose_name='is active?', default=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = 'Sub Regions'

class Country(models.Model):
    name = models.CharField(max_length=100)
    region = models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name='Region')
    subregion = models.ForeignKey(SubRegion, on_delete=models.CASCADE, verbose_name='Sub Region')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

admin.py

class CountryAdmin(admin.ModelAdmin):
    list_display = ('name', 'is_active', 'is_default', 'created_at', 'updated_at', )
    list_filter = ('region', 'subregion', 'is_active', 'is_default', 'created_at', 'updated_at', )
    search_fields = ('name', 'official_name', )
    fieldsets = (
        ('Region Information', {'fields': ('region', 'subregion', )}),
        ('Basic Information', {'fields': ('name', 'official_name', )}),
        ('Codes Information', {'fields': ('cca2', 'ccn3', 'cca3', 'cioc','idd', 'status', )}),
        ('Coordinates', {'fields': ('latitude', 'longitude', )}),
        ('Membership & Statuses', {'fields': (('is_independent', 'is_un_member', 'is_default', 'is_active', ), )}),
        ('Country Flag', {'fields': ('png_flag_url', 'svg_flag_url', )}),
        ('Country Map', {'fields': ('google_map_url', 'openstreet_map_url', )}),
        ('Additional Information', {'fields': ('population', 'area', 'start_of_week', 'postal_code_format', 'postal_code_regex', )}),
    )
    inlines = [CountryTopLevelDomainInline, CountryCurrencyInline, CountryTimezoneInline]


admin.site.register(Country, CountryAdmin)

Currently, Whenever I am going to add/edit a country then the complete region and subregion are visible on the select dropdown. What I want like possibilities to load the subregion on the basis of region selection.

2
  • You may need JS for this... depending on the value of Region selected, you can populate the options of SubRegion Commented Apr 22, 2022 at 8:28
  • What I mean is, search for how to solve your problem in Javascript. Javascript would provide a better solution. Commented Apr 23, 2022 at 11:03

1 Answer 1

3

After long research, I was able to solve this issue.

I have created the JS file inside my application.

static\geo_location\js\chained-region.js

var response_cache = {};
function get_subregion_for_region(region_id) {
    (function($){
        var selected_value = $("#id_subregion").val();
        if(response_cache[region_id]){
            $("#id_subregion").html(response_cache[region_id]);
            $("select#id_subregion option").each(function(){
                if ($(this).val() == selected_value)
                    $(this).attr("selected","selected");
            });
        } else {
            $.getJSON("/geo-location/get-subregions-for-region", {region_id:region_id}, function(res, textStatus){
                var options = '<option value="" selected="selected">---------</option>';
                $.each(res.data, function(i, item){
                    options += '<option value="' + item.id + '">' + item.name + '</option>';
                });
                response_cache[region_id] = options;
                $("#id_subregion").html(response_cache[region_id]);
                $("select#id_subregion option").each(function(){
                    if ($(this).val() == selected_value)
                        $(this).attr("selected","selected");
                });
            });
        }
    })(django.jQuery);
}

then, added the views function to handle AJAX requests.

views.py

from django.http import JsonResponse
from django.contrib.auth.decorators import login_required
from .models import SubRegion


@login_required
def subregion_for_region(request):
    if 'region_id' in request.GET and request.GET['region_id'] != '':
        subregions = SubRegion.objects.filter(region=request.GET['region_id'])
        return JsonResponse({'data': [{'id': subregion.id, 'name': subregion.name} for subregion in subregions]})
    else:
        return JsonResponse({'data': []})

Note:- I have added the @login_required decorator to prevent the unnecessary call.

And, in the admin.py I have created the form and added the "onchange" event on the region dropdown.

admin.py

from django import forms
class CountryForm(forms.ModelForm):
    class Meta:
        model = Country
        fields = '__all__'
        widgets = {
            'region': forms.Select(attrs={'onchange': 'get_subregion_for_region(this.value);'})
        }

class CountryAdmin(admin.ModelAdmin):
    ....

    form = CountryForm

    class Media:
        js = (
            'geo_location/js/chained-region.js',
        )

And last added the URL pattern in the application urls.py file. urls.py

from django.urls import path
from . import views

app_name = 'geo_location'
urlpatterns = [
    path('get-subregions-for-region/', views.subregion_for_region, name='subregions-for-region'),
]
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.