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.