I'm working on a small e-commerce. For a model 'Product' I keep track of the stock using a Custom Manager and a method called Products.objects.with_stock() that uses annotations (I'm required to do so, there's no way I can add a stock attribute in Product)
In a ListView of all the products, it's pretty straightforward to add the stock, to access it from a template:
# app/views.py
class ProductListView(ListView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
And then in the template I just call it:
<!-- templates/home.html-->
{% for product in products %}
<p> Stock: {{product.stock}} <p>
{% endfor %}
How do I perform something similar for a DetailView?
Given that a DetailView gets a particular object instance, where or how do I run something similar to what I did in the ListView? Where do I run that query method that affects all objects as a whole, so that then I can access it in the same way from the template?
It couldn't be simpler: just do the same in the DetailView:
# app/views.py
class ProductDetailView(DetailView):
...
def get_stock(self, obj):
return obj.stock
def get_queryset(self):
return super().get_queryset().with_stock()
Related
I have employed django simple history package on the admin site to be able to track and revert to previous versions of the object of the model. I am designing a web form that allows users to change instances of the model object using model form on django and would like to allow the users to view and revert to previous versions. Also to allow them to see what are the changes compared to the current version.
With the code below I am able to get the list of historical records on my template under histoire.
class CompanyDetailView(LoginRequiredMixin,generic.DetailView):
model = Company
def get_context_data(self, **kwargs):
context = super(CompanyDetailView, self).get_context_data(**kwargs)
company_instance = self.object
context['histoire'] = company_instance.history.all()
return context
In my template,
<p>
Previous versions:
{% for item in histoire %}
<li>
{{ item }} submitted by {{ item.history_user }} {{
item.history_object }}
</li>
{% endfor %}
</p>
But ideally I want item.history_object to be a link that users can view the previous object and be able to revert if desired.
I did something similar by adding HistoricForm to my model forms.
class MyModelForm(HistoricForm, ModelForm):
...
HistoricForm takes and extra history_id kwarg.
If history_id is provided HistoricForm swaps the ModelForm instance with the historic_instance (what your instance looked like at the time of history_id). This way your form will show the historic version of your object.
class HistoricForm(object):
def __init__(self, *args, **kwargs):
self.history_id = kwargs.pop('history_id', None)
instance = kwargs.get('instance')
if instance and self.history_id:
kwargs['instance'] = self.get_historic_instance(instance)
super(HistoricForm, self).__init__(*args, **kwargs)
def get_historic_instance(self, instance):
model = self._meta.model
queryset = getattr(model, 'history').model.objects
historic_instance = queryset.get(**{
model._meta.pk.attname: instance.pk,
'history_id': self.history_id,
}).instance
return historic_instance
If history_id is not provided the ModelForm works as usual.
You can revert by showing the historic instance and save (this way you will post your historic data).
Is it possible to provide a template name to a viewset in DRF, such that this new template extends api.html?
new_template.html
{% extends 'api.html' %}
{% block my-new-block %}
TEST
{% endblock %}
I can't see how to do it currently, and setting 'template_name' hasn't had any effect. I need to display content differently depending on the particular endpoint.
EDIT: Tried overwriting the retrieve method and adding 'rest_framework.renderers.TemplateHTMLRenderer', to the DEFAULT_RENDERER_CLASSES in settings.py.
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data, template_name='new_template.html')
However, this seems to have zero effect.
The way to do it is to subclass renderers.BaseRenderer and set your own template. In the view, you can set the renderer_classes attribute to your own custom renderer
So I have two models: Car and Picture. a car may have multiple pictures.
Now I want to use a list view to display all the cars along with one picture for each car, can someone tell me how can I do that?
Below is my code
# models.py
class Car(models.Model):
name = models.CharField(max_length=100)
class Picture(models.Model):
car = models.ForeignKey(Car,related_name='pictures')
picture = models.ImageField()
# views.py
class CarList(ListView):
model = Car
List view has a method get_context_data. You can override this to send extra context into the template.
def get_context_data(self,**kwargs):
context = super(CarList,self).get_context_data(**kwargs)
context['picture'] = Picture.objects.filter(your_condition)
return context
Then, in your template you can access picture object as you wish.
I guess this should solve your problem.
As I wanted to pass forms with queryset, following worked for me.
def get_queryset(self):
return Men.objects.filter(your condition)
def get_context_data(self,**kwargs):
context = super(Viewname,self).get_context_data(**kwargs)
context['price_form']=PriceForm(self.request.GET or None)
return context
Using get_queryset() you can define a base queryset that will be implemented by get_context_data() in super().
You can directly access the related Picture objects to each Car object by using car.pictures.all in your template.
So you could do something like,
{% for car in objects %}
{{ car.name }}
{% if car.pictures.all %}<img src="{{ car.pictures.all.0.picture.url }}" />{%endif %}
{% endfor %}
For more info, read up on Related Objects.
In my app, I want to have an index page which will display two lists of objects, where both lists contain the same type of object (i.e, same Model).
In traditional function based views this is easy: I define two variables, assign them to querysets, and pass them into the context to my template where I can easily access them by name.
I'm still new to CBVs and it seems there is a lot of magic, a lot of things that are handled automatically. I understand how I can override the queryset for a ListView (which defaults to all objects for the given Model), but what I don't get is how to supply multiple querysets, so that my ListView can actually display two lists.
My only thought so far is to override self.object_list to be a tuple of two querysets, but that seems like it would make my template code less clear and I'm not even certain it would work.
If you do not want to support pagination in your multiple lists view, I would suggest overwriting the get_context_data and get methods of your view class
def get_context_data(self, **kwargs):
"""
Get the context for this view.
"""
queryset = kwargs.pop('object_list', self.object_list)
queryset2 = kwargs.pop('object_list', self.object_list2)
context = {
'paginator': None,
'page_obj': None,
'is_paginated': False,
'object_list': queryset,
'object_list3': queryset2
}
context.update(kwargs)
return context
def get(self, request, *args, **kwargs):
self.object_list1 = self.get_queryset1()
self.object_list2 = self.get_queryset2()
context = self.get_context_data()
return self.render_to_response(context)
Sorry for the names (1 and 2) but you should place more descriptive whenever I placed names like "get_queryset1"
What is the difference between the items in both lists? If you're talking about a single list (i.e. queryset) which can be split in two by some of their properties, this should simply be done in the template itself.
For example, imagine you have a list of users, and you want to display them by gender, i.e. men in one list and women in the other. In this case simply return a single queryset like normally with the ListView, and then in the template put something like:
<h4>Male Users</h4>
<li>
{% for user in users %}
{% if not user.is_female %}<ul>{{ user.full_name }}</ul>
{% endfor %}
</li>
<h4>Female Users</h4>
<li>
{% for user in users %}
{% if user.is_female %}<ul>{{ user.full_name }}</ul>
{% endfor %}
</li>
I have a Project model.
This model has Days which are inlines.
How do I display them using a DetailView?
My views.py looks like this:
class ProjectDetailView(DetailView):
queryset = Project.objects.all()
slug_field = 'slug'
template_name = 'projects/detail_project.html'
How do I pull through the Day inlines with this?
I've tried:
def get_context_data(self, **kwargs):
context = super(ProjectDetailView, self).get_context_data(**kwargs)
project = Project.objects.filter(slug=self.slug_field)
context['days'] = Day.objects.filter(project=project)
return context
But this doesn't work. Also it seems pointless that I'm using a Generic view but then doing a get_object_or_404 anyway to pull the Days out.
How do I do this properly?
There's no such thing as an inline model. There are inline forms, which are forms for a model which has a ForeignKey relationship with a parent model - but you don't seem to be talking about forms.
In any case, there's no need to do anything in code. You can refer to the related models directly in the template:
{% for day in object.day_set.all %}
{{ day.whatever }}
{% endfor %}