Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How I can get related model for custom column in admin list_display with prefetch_related?

Hi I have two models with m2m link through third model:

class Group(UsefulAbstractModel):
    hotels = models.ManyToManyField(
        Hotel,
        through='HotelDetails',
        related_name='groups', )

class Hotel(UsefulAbstractModel):
    name = models.CharField(
        max_length=255,)

class HotelDetails(models.Model):
    hotel = models.ForeignKey(
            Hotel,
            related_name='hotel_details', )
    group = models.ForeignKey(
            Group,
            related_name='hotel_details', )
    num = models.IntegerField(
        validators=[
            MaxValueValidator(2),
            MinValueValidator(1), ], )

All groups has two hotel links with numbers 1 and 2. I need to display it in admin group interface. I create two custom columns for each hotel:

class GroupAdmin(admin.ModelAdmin):

    def first_hotel(self, instance):
        return instance.hotel_details.filter(num=1).first().hotel

    def second_hotel(self, instance):
        return instance.hotel_details.filter(num=1).first().hotel

But for every instance I have 2 additional query now.
I tried to override queryset method, but in not helped:

def queryset(self, request):
    return super(GroupAdmin,self).queryset(request).prefetch_related('hotels')
like image 953
Ivan Semochkin Avatar asked Nov 28 '25 07:11

Ivan Semochkin


1 Answers

The problem is that you are filtering the prefetched results with filter(num=1). This causes Django to do a new query.

You can use a Prefetch object to fetch the correct queryset. Note you should override the model admin's get_queryset method, not queryset.

def get_queryset(self, request):
    return super(GroupAdmin,self).get_queryset(request).prefetch_related(
        Prefetch('hotel_details', queryset=HotelDetails.objects.filter(num=1).select_related('hotel'), to_attr='hotel_details_num1'),
    )

Then change your model admin methods to use the new queryset, for example:

def first_hotel(self, instance):
    return instance.hotel_details_num1.first().hotel

See the docs for more into about prefetch_related and Prefetch objects.

like image 69
Alasdair Avatar answered Nov 29 '25 19:11

Alasdair



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!