LukasKawerau
LukasKawerau

Reputation: 1071

limit_choices_to in DjangoAdmin

One of my models contains a ForeignKey-field to a model that has multiple thousand instances.
When I display a record, all of these are loaded into a dropdown, which I a) don't need and b) is slow as frack, especially when displaying multiple records on one page.
Page size shoots up to multiples of 3.5mb because of the size of the dropdown.

I thought about using "limit_choices_to" to contain that, but

country = models.IntegerField(blank=True, null=True)
location = models.ForeignKey(Geonames, limit_choices_to = {'cowcode': country}, related_name='events')

does not work. Is there even a way to do that?

Update:
What do I want to display?
I want to show all places (Geonames) that are in the country of the EventRecord that the code above is taken from. I want to show only these places, not the whole list of all possible places.

Why don't I need all places?
a) Page load times: 3.5 minutes for a page load is a tad too long
b) See above: An Event takes place in a certain country, so I don't need to show locations that are not in that country

Upvotes: 1

Views: 2944

Answers (3)

ydaniv
ydaniv

Reputation: 1289

What you want is to make limit_choices_to aware to your instance, which is not possible. What you should do is set the queryset property of location field in your admin form, something similar to this:

class EventRecordAdminForm(forms.ModelForm):

    class Meta:
        model = EventRecord

    def __init__(self, *args, **kwargs):
        super(EventRecordAdminForm, self).__init__(*args, **kwargs)
        self.fields['location'].queryset = Geonames.objects.filter(cowcode=self.instance.country)

and of course use that form for your admin:

class EventRecordAdmin(admin.ModelAdmin):

    form = EventRecordAdminForm

See here for docs

HTH!

Upvotes: 5

dan-klasson
dan-klasson

Reputation: 14230

Not sure why that is not working for you. But I think a better solution would be to use django-smart-selects. That way you can have the user choose country first. Then the Geoname dropdown is only populated when the user first chooses country.

From the docs:

If you have the following model:

class Location(models.Model)
    continent = models.ForeignKey(Continent)
    country = models.ForeignKey(Country)
    area = models.ForeignKey(Area)
    city = models.CharField(max_length=50)
    street = models.CharField(max_length=100)

And you want that if you select a continent only the countries are available that are located on this continent and the same for areas you can do the following:

from smart_selects.db_fields import ChainedForeignKey 

class Location(models.Model)
    continent = models.ForeignKey(Continent)
    country = ChainedForeignKey(
        Country, 
        chained_field="continent",
        chained_model_field="continent", 
        show_all=False, 
        auto_choose=True
    )
    area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country")
    city = models.CharField(max_length=50)
    street = models.CharField(max_length=100)

This example asumes that the Country Model has a continent = ForeignKey(Continent) field and that the Area model has country = ForeignKey(Country) field.

Upvotes: 0

nim4n
nim4n

Reputation: 1829

if you are using admin interface you can use raw_id_fields in ModelAdmin:

class BookAdmin(admin.ModelAdmin):
    list_display = ('title', 'publisher', 'publication_date')
    list_filter = ('publication_date',)
    date_hierarchy = 'publication_date'
    ordering = ('-publication_date',)
    filter_horizontal = ('authors',)
    raw_id_fields = ('publisher',)

from Django Book:

sometimes you don’t want to incur the overhead of having to select all the related objects to display in the drop-down. For example, if our book database grows to include thousands of publishers, the “Add book” form could take a while to load, because it would have to load every publisher for display in the box.

The way to fix this is to use an option called raw_id_fields. Set this to a tuple of ForeignKey field names, and those fields will be displayed in the admin with a simple text input box () instead of a select.

Upvotes: 2

Related Questions