How to use prefetch_related with Django's DetailView - django

I want to use prefetch_related with Django's DetailView.
Model:
class Customer(models.Model):
name = models.CharField(
verbose_name='customer name',
max_length=100
)
# Other fields
class Packet(models.Model):
customer = models.ForeignKey(
Customer
)
# Other fields
class Credit(models.Model) :
customer = models.ForeignKey(
Customer
)
# Other fields
View:
class CustomerDetailsView(LoginRequiredMixin, DetailView):
model = Customer
http_method_names = ['get']
template_name = 'detail_templates/customer_details.html'
Templates:
{% for p in object.packet_set %}
{{ do something }}
{% endif %}
{% for p in object.credit_set %}
{{ do something }}
{% endif %}
Tried:
class CustomerDetailsView(LoginRequiredMixin, DetailView):
model = Customer
http_method_names = ['get']
template_name = 'detail_templates/customer_details.html'
def get_queryset(self):
queryset = super(CustomerDetailsView, self).get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg, None)
return queryset.filter(id=pk).prefetch_related('packet_set', 'credit_set')
debug_toolbar shows no improvement.
How do I prefetch_related packet and credit

There is no sense to use prefetch_related() in the DetailView. This view loads the single master object with get() while prefetch_related() is usable for loading related objects of multiple master objects.

Related

Displaying get_context_data in template Django

I am trying to display the get_context_data on the template. I have a method on the model class that I need to call from ProfileView which has two different models. For the Profile View I have Profile Model and for the shippingaddress view I have ShippingAddress Model. And these models are from two different app. I tried the function below and it does not show any error, but when I tried to call it in the Template, It does not show the method.
Views.py
class ProfileView(LoginRequiredMixin, DetailView):
model = Profile
template_name = "account/profile.html"
success_url = "/"
def get_context_data(self, *args, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['shipping'] = ShippingAddress.objects.filter(user=self.request.user)
return context
Template code
{{object.get_full_address}}
Models.py
class ShippingAddress(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
phone_number = PhoneNumberField(null=True, blank=True)
street_address = models.CharField(max_length=300)
province = models.CharField(max_length=300)
city = models.CharField(max_length=50)
country = models.CharField(max_length=50)
zip_code = models.CharField(max_length=10)
def __str__(self):
return str(self.user)
def get_phone_number(self):
return self.phone_number
#property
def get_full_address(self):
return f"{self.street_address}, {self.province}, {self.city}, {self.country}, {self.zip_code}"
object is the context variable that DetailView will add to the context. For your view this would be an instance of Profile. You pass a queryset into the context with the name shipping so you can loop over that:
{% for shipping_address in shipping %}
{{ shipping_address.get_full_address }}
{% endfor %}
Note: You need to loop because one user has multiple Shipping Addresses according to your models.
Note: Also you didn't need to override get_context_data you could simply have written:
{% for shipping_address in request.user.shippingaddress_set %}
{{ shipping_address.get_full_address }}
{% endfor %}
Where shippingaddress_set is the related model name with _set
appended. You can change that by setting related_name on your
foreign key.

Django: how to show related model fields in template

I'm new in Django 3.0 and I'm lost in this easy question, please help me.
I have 2 models:
class Product(models.Model):
fields...
class ProductType(models.Model):
product = models.ForeignKey(Product, related_name='product_types', on_delete=models.CASCADE)
color = models.Charfield(...)
In my template, I would like to show all the related product types and their fields to a specific product:
...
{% for product in products %}
{{ product.??? }}
{% endfor %}
Here is my view:
class ProductsView(ListView):
collection = None
model = Product
paginate_by = 6
template_name = 'shop/product/list.html'
context_object_name = 'products'
def get_queryset(self):
products = Product.objects.filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products
def get_context_data(self):
context = super().get_context_data(**self.kwargs)
context['notification'] = Notification.objects.all()
if 'collection_name' in self.kwargs:
context['collection'] = get_object_or_404(Collection, name=self.kwargs['collection_name'])
context['collection_name'] = self.kwargs['collection_name']
context['collections'] = Collection.objects.all()
return context
Thank you
You access the related ProductTypes through a manager that has as name the value you specify as related_name=… [Django-doc], so in this case:
{% for product in products %}
{% for type in product.product_types.all %}
{{ type.color }}
{% endfor %}
{% endfor %}
To boost efficiency, you can fetch all related ProductTypes for the elements in the queryset with .prefetch_related(…) [Django-doc]:
class ProductsView(ListView):
# …
def get_queryset(self):
products = Product.objects.prefetch_related('product_types').filter(available=True)
collection_name = self.kwargs['collection_name'] if 'collection_name' in self.kwargs else None
if collection_name:
collection = get_object_or_404(Collection, name=collection_name)
products = products.filter(collection=collection)
return products

How to use ModelChoiceField in DRF?

I am trying to convert my form that was written earlier to django rest serializer but it does't work. Could you help me to solve this problem please?
this is my form:
class TripSearchForm(forms.Form):
departure = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
destination = ModelChoiceField(
queryset=Place.objects.places_for_segment(), widget=autocomplete.ModelSelect2(url="autocomplete")
)
How to built proper serializer?
class SearchSerializer(serializers.Serializer):
departure = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='departure')
destination = serializers.RelatedField(queryset=places_models.Place.objects.all(),
label='destination')
Assuming you have model Country
class Country(models.Model):
name = models.CharField(max_length=60, blank=True, default='')
You could write a serializers based on that
class CountryField(serializers.PrimaryKeyRelatedField):
def display_value(self, instance):
return instance.name
class CountrySerializer(serializers.ModelSerializer):
country = CountryField(queryset=Country.objects.all())
class Meta:
model = Country
fields = ('name', )
class DesiredSerializer(serializers.Serializer):
country = ColorSerializer()
Now you should be able to use your desired serialized to render choices from model either as html form for instance or whatever logic fits you better
if you want it as form
#views.py
def get(self, request):
serializer = DesiredSerializer()
return Response({ 'serializer': serializer }, template_name='my_model_choices_form.html')
<!-- my_model_choices_form.html -->
{% load rest_framework %}
<form action="..." method="POST">
{% csrf_token %}
{% render_form serializer %}
</form>
Now if you'll create instance of Country with some name it will be shown in select dropdown, display_value function can be used to customize the option output.
Hope that helps

Django Form based on Form

I am writing my code based on the principle don't repeat yourself. I keep violating that rule because I am new to Django but this one should be straight forward.
The code below is no problem for ModelAForm:
model.py
class ModelA(models.Model):
id = model.AutoField(primary_key=True)
name1 = models.CharField(max_length=100)
name2 = models.CharField(max_length=100)
...
right = models.BooleanField(default=True)
class ModelB(models.Model):
id = model.AutoField(primary_key=True)
mod = model.ForeignKey(ModelA, on_delete=models.CASCADE)
above30 = models.BooleanField(default=True)
forms.py
class ModelAForm(forms.ModelForm):
class Meta:
model = ModelA
exclude = ['id']
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id']
But this way I don't see the other fields of modelA in the ModelBForm. How can I do this?
Thanks!
Well, it should not. Because its FK relation between ModelB and ModelA. So in ModelB form, the ModelA entries should appear as a choice field. If you want to show fields of ModelA then try like this:
First, remove the FK reference field from ModelBForm:
class ModelBForm(forms.ModelForm):
class Meta:
model = ModelB
exclude = ['id', 'mod']
Now Use both those forms in your view:
def some_view(request):
if request.method == 'GET':
return render(request, 'template.html', {'form_a': ModelAForm(), 'form_b': ModelBForm()})
if request.method == 'POST':
form_a = ModelAForm(request.POST)
form_a = ModelBForm(request.POST)
if form_a.is_valid() and form_a.is_valid():
instance_a = form_a.save() # get model a instance
instance_b = form_b.save(commit=False)
instance_b.mod = instance_a # set model a instance as FK
instance_b.save()
Render the Form Like this:
<form action="." method="post">
{% csrf_token %}
{{ form_a.as_p }}
{{ form_b.as_p }}
<input type="submit" value="Submit">
</form>

django: display UpdateView in language defined by the object

I have a django project where I would like to display an UpdateView of a ModelForm in the language which is defined by the model itself. I would like to avoid using i18n patterns in the URL.
The object to be displayed is identified via an UUID provided in the URL of the form
http://name.tld/submit/55dbf53c-9364-4938-873f-6abad2e4ed45
I have:
Model:
class MyModel(models.Model):
unique_id = models.UUIDField(
default=uuid.uuid4,
editable=False,
unique=True,
verbose_name=_("UUID Field")
)
language = models.CharField(
default = "en",
max_length=7,
choices=settings.LANGUAGES,
verbose_name=_("Selected language")
)
ModelForm:
class MyModelForm(forms.ModelForm):
class Meta:
model = MyModel
localized_fields = '__all__'
fields = '__all__'
UpdateView:
class MyModelUpdate(UpdateView):
model = MyModel
form_class = MyModelForm
template_name_suffix = '_form'
def get_object(self, **kwargs):
# get the uuid and find the corresponding object
obj = MyModel.objects.filter(unique_id=self.kwargs.get('uuid')).first()
if not obj or obj.is_answered:
raise Http404("Page not found")
else:
translation.activate(obj.language)
return obj
def get_success_url(self):
return reverse('submit-success')
Template:
{% load i18n %}
{% trans alert %}
<h2>{% trans "My great translated UpdateView" %}</h2>
<form action="" method="post">
{% csrf_token %}
{% form %}
</form>
However, despite providing the translated strings in the PO files (created using django-admin makemessages and django-admin compilemessages the page always shows up in English (my browser language, that is).
What am I missing?