简体   繁体   中英

django model select_related or prefetch_related child model

Newbie here, I have two models which are below

class ReceipeMaster(models.Model):
    receipe_type = models.CharField(max_length=50, choices=TYPE_OPTIONS, default=TYPE_OPTIONS[0])
    item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='receipe')
    units = models.IntegerField(default=1)
    status = models.BooleanField(default=True)

class ReceipeDetail(models.Model):
    master = models.ForeignKey(ReceipeMaster, on_delete=models.CASCADE, related_name='items')
    item_type = models.ForeignKey(Item_type, null=True, on_delete=models.PROTECT)
    item = models.ForeignKey(Item, on_delete=models.PROTECT)
    quantity = models.IntegerField()

I have a detailed view DetailView

class ReceipeDetailView(PermissionRequiredMixin, DetailView):
    permission_required = 'item_management.view_receipemaster'
    model = ReceipeMaster
    template_name = 'receipes/show.html'
    context_object_name = 'receipe'

I would like to prefetch_related or select_related on the ReceipeMaster model and as well as the ReceipeDetail model. Simply prefetch on both models.

Regards

To customize how a queryset is fetched in a generic view one needs to override the get_queryset method of the view. Also if one wants to use select_related on a prefetched object one should use Prefetch objects [Django docs] and specify their queryset . Since this is a DetailView and hence deals with one object you will need to override get_object instead:

from django.db.models import Prefetch
from django.http import Http404
from django.utils.translation import gettext as _

class ReceipeDetailView(PermissionRequiredMixin, DetailView):
    permission_required = 'item_management.view_receipemaster'
    model = ReceipeMaster
    template_name = 'receipes/show.html'
    context_object_name = 'receipe'
    
    def get_object(self, queryset=None):
        # Use a custom queryset if provided; this is required for subclasses
        # like DateDetailView
        if queryset is None:
            queryset = self.get_queryset()

        # Next, try looking up by primary key.
        pk = self.kwargs.get(self.pk_url_kwarg)
        slug = self.kwargs.get(self.slug_url_kwarg)
        if pk is not None:
            queryset = queryset.filter(pk=pk)

        # Next, try looking up by slug.
        if slug is not None and (pk is None or self.query_pk_and_slug):
            slug_field = self.get_slug_field()
            queryset = queryset.filter(**{slug_field: slug})

        # If none of those are defined, it's an error.
        if pk is None and slug is None:
            raise AttributeError(
                "Generic detail view %s must be called with either an object "
                "pk or a slug in the URLconf." % self.__class__.__name__
            )
        queryset = queryset.select_related('item').prefetch_related(
            Prefetch(
                'items',
                 queryset=ReceipeDetail.objects.select_related('item_type', 'item')
            )
        )
        try:
            # Get the single item from the filtered queryset
            obj = queryset.get()
        except queryset.model.DoesNotExist:
            raise Http404(_("No %(verbose_name)s found matching the query") %
                          {'verbose_name': queryset.model._meta.verbose_name})
        return obj

You could override the get_queryset method entirely, without calling super() :

def get_queryset(self):
    queryset = ReceipeMaster.objects.select_related('item').prefetch_related(
        Prefetch(
            'items',
            queryset=ReceipeDetail.objects.select_related('item_type', 'item'),
        )
    )
    return queryset

The get_object method will then filter the queryset in order to get the object. This won't be a problem in this case since the order of filter() and select_related() chaining isn't important ( docs ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM