2016-09-23 25 views
6

Ứng dụng hỗ trợ Django của tôi với API DRF hoạt động tốt, nhưng tôi đã bắt đầu chạy vào các vấn đề hiệu suất khi cơ sở dữ liệu được điền dữ liệu thực tế. Tôi đã làm một số hồ sơ với Django Debug Thanh công cụ và thấy rằng nhiều điểm cuối của tôi vấn đề hàng chục đến hàng trăm truy vấn trong quá trình trả lại dữ liệu của họ.Django REST Framework: Thiết lập tìm nạp trước cho serializers lồng nhau

Tôi mong đợi điều này, vì trước đó tôi chưa tối ưu hóa bất kỳ điều gì liên quan đến truy vấn cơ sở dữ liệu. Bây giờ tôi đang thiết lập tìm nạp trước, tuy nhiên, tôi đang gặp khó khăn khi sử dụng việc sử dụng dữ liệu serializer được tìm nạp đúng cách khi serializer đó được lồng trong một serializer khác. Tôi đã sử dụng số awesome post này làm hướng dẫn về cách suy nghĩ về các cách khác nhau để tìm nạp trước.

Hiện tại, bộ nối tiếp ReadingGroup của tôi không tìm nạp trước khi tôi nhấn điểm cuối /api/readinggroups/. Vấn đề của tôi là điểm cuối /api/userbookstats/, trả về tất cả các đối tượng UserBookStats. Trình nối tiếp có liên quan, UserBookStatsSerializer, có một số lồng nhau ReadingGroupSerializer.

Các mô hình, serializers và viewsets như sau:

models.py

class ReadingGroup(models.model): 
    owner = models.ForeignKeyField(settings.AUTH_USER_MODEL) 
    users = models.ManyToManyField(settings.AUTH_USER_MODEL) 
    book_type = models.ForeignKeyField(BookType) 
    .... 
    <other group related fields> 

    def __str__(self): 
    return '%s group: %s' % (self.name, self.book_type) 

class UserBookStats(models.Model): 
    reading_group = models.ForeignKey(ReadingGroup) 
    user = models.ForeignKey(settings.AUTH_USER_MODEL) 
    alias = models.CharField() 

    total_books_read = models.IntegerField(default=0) 
    num_books_owned = models.IntegerField(default=0) 
    fastest_read_time = models.IntegerField(default=0) 
    average_read_time = models.IntegerField(default=0) 

serializers.py

class ReadingGroupSerializer(serializers.ModelSerializer): 
    users = UserSerializer(many = True,read_only=True) 
    owner = UserSerializer(read_only=True) 

    class Meta: 
     model = ReadingGroup 
     fields = ('url', 'id','owner', 'users') 

    @staticmethod 
    def setup_eager_loading(queryset): 
     #select_related for 'to-one' relationships 
     queryset = queryset.select_related('owner') 

     #prefetch_related for 'to-many' relationships 
     queryset = queryset.prefetch_related('users') 

     return queryset 

class UserBookStatsSerializer(serializers.HyperlinkedModelSerializer): 
    reading_group = ReadingGroupSerializer() 
    user = UserSerializer() 
    awards = AwardSerializer(source='award_set', many=True) 

    class Meta: 
     model = UserBookStats 
     fields = ('url', 'id', 'alias', 'total_books_read', 'num_books_owned', 
       'average_read_time', 'fastest_read_time', 'awards') 

    @staticmethod 
    def setup_eager_loading(queryset): 
     #select_related for 'to-one' relationships 
     queryset = queryset.select_related('user') 

     #prefetch_related for 'to-many' relationships 
     queryset = queryset.prefetch_related('awards_set') 

     #setup prefetching for nested serializers 
     groups = Prefetch('reading_group', queryset ReadingGroup.objects.prefetch_related('userbookstats_set'))   
     queryset = queryset.prefetch_related(groups) 

     return queryset 

views.py

class ReadingGroupViewset(views.ModelViewset): 

    def get_queryset(self): 
    qs = ReadingGroup.objects.all() 
    qs = self.get_serializer_class().setup_eager_loading(qs) 
    return qs 

class UserBookStatsViewset(views.ModelViewset): 

    def get_queryset(self): 
    qs = UserBookStats.objects.all() 
    qs = self.get_serializer_class().setup_eager_loading(qs) 
    return qs 

tôi đã tối ưu hóa tìm nạp trước cho ReadingGroup endpoint (Tôi thực sự đăng về cách loại bỏ các truy vấn trùng lặp cho rằng thiết bị đầu cuối here), và bây giờ tôi đang làm việc trên các thiết bị đầu cuối UserBookStats.

Vấn đề tôi gặp phải là, với setup_eager_loading hiện tại trong UserBookStatsSerializer, dường như không sử dụng tính năng tìm nạp trước được thiết lập theo phương thức tải mong muốn trong ReadingGroupSerializer. Tôi vẫn còn hơi mơ hồ về cú pháp cho đối tượng Prefetch - Tôi đã được truyền cảm hứng bởi this câu trả lời tuyệt vời để thử cách tiếp cận đó.

Rõ ràng phương thức get_queryset của UserBookStatsViewset không gọi setup_eager_loading cho các đối tượng ReadingGroup, nhưng tôi chắc chắn có cách để thực hiện việc tìm nạp trước đó.

+1

Bạn đang hỏi về giải pháp thanh lịch hay giải pháp nào? 'queryset = queryset.prefetch_related ('reading_group', 'reading_group__users', 'reading_group__owner')' chỉ hoạt động tốt. – serg

+0

Bất kỳ giải pháp nào cũng tuyệt vời - và wow, điều đó thật tuyệt vời. Điều đó hoạt động hoàn hảo, vì vậy nếu bạn gửi nó như là một câu trả lời, tôi sẽ chấp nhận. Nó thay thế đối tượng 'Prefetch', đó là những gì tôi đã hy vọng sẽ sử dụng cho một giải pháp thanh lịch có thể phân tách được việc tìm nạp trước, nhưng nó hoạt động rất tốt. Cảm ơn! – dkhaupt

Trả lời

3

prefetch_related() hỗ trợ tìm nạp trước các mối quan hệ bên trong bằng cách sử dụng cú pháp gạch kép:

queryset = queryset.prefetch_related('reading_group', 'reading_group__users', 'reading_group__owner') 

Tôi không nghĩ rằng Django REST cung cấp bất kỳ giải pháp thanh lịch ra khỏi hộp cho lấy tất cả các lĩnh vực cần thiết tự động.

Các vấn đề liên quan