djangodjango-modelsdjango-admin

Complicated "limit_choices_to" function in Django


I have Django database with 2 models: DeviceModel and Device. Let's say, for example, DeviceModel object is "LCD panel" and Device object is "LCD panel №547". So these two tables have ManyToOne relationship.

class DeviceModel(models.Model):
    name = models.CharField(max_length=255)

class Device(models.Model):
    device_model = models.ForeignKey(DeviceModel)
    serial_number = models.CharField(max_length=255)

Now I need to add some relations between DeviceModel objects. For example "LCD Panel" can be in "Tablet" object or in "Monitor" object. Also another object can be individual, so it doesn't link with other objects.

I decided to do this with ManyToMany relationship, opposed to using serialization with JSON or something like that (btw, which approach is better in what situation??).

I filled all relationships between device models and know I need to add relationship functional to Device table.

For that purpose I added "master_dev" foreignkey field pointing to 'self'. It works exactly as I need, but I want to restrict output in django admin panel. It should display only devices, that are connected through device_links. Current code:

class DeviceModel(models.Model):
    name = models.CharField(max_length=255)
    device_links = models.ManyToManyField('self')

class Device(models.Model):
    device_model = models.ForeignKey(DeviceModel)
    serial_number = models.CharField(max_length=255)
    master_dev = models.ForeignKey('self', blank=True, null=True)

So, how can I limit output of master_dev field in admin panel? There is a function "limit_choices_to", but I can't get it to work...


Solution

  • While there is no direct answer to my question about "limit_choices_to" function, I post solution that achieves desired output:

    from django import forms
    from django.contrib import admin
    from .models import DeviceModel, Device
    
    class DeviceForm(forms.ModelForm):
    
        def __init__(self, *args, **kwargs):
            super(DeviceForm, self).__init__(*args, **kwargs)
            try:
                linked_device_models = self.instance.device_model.device_links.all()
                linked_devices = Device.objects.filter(device_model__in=linked_device_models)
    
                required_ids = set(linked_devices.values_list("id", flat=True))
    
                self.fields['master_dev'].queryset = Device.objects.filter(id__in=required_ids).order_by("device_model__name", "serial_number")         
            except:
                # can't restrict masters output if we don't know device yet
                # admin should edit master_dev field only after creation
    
                self.fields['master_dev'].queryset = Device.objects.none()
    
    
        class Meta:
            model = Device
            fields = ["device_model", "serial_number", "master_dev"]
    
    
    class DeviceAdmin(admin.ModelAdmin):
        form = DeviceForm
    
        list_display = ('id', 'device_model', 'serial_number')
        list_display_links = ('id', 'device_model')
        search_fields = ('device_model__name', 'serial_number')
        list_per_page = 50
        list_filter = ('device_model',)