Search code examples
python-3.xlistviewdjango-templatesdjango-viewsdetailview

I want to represent DetailView and ListView in one Template. What can I do?


I want to express the detailview as the picture. I want to code the box part in the template of the picture. enter image description here

It is as follows now.

views.py

@login_required
def product_detail(request, id, product_slug=None):
    product = get_object_or_404(Product, id=id, slug=product_slug)
    return render(request, 'shop/detail.html', {'product': product})

I think it should be modified to class. I would like to explain it by representing DetailView and ListView together in detail_template. I modified only views.py as shown below.

class ProductDetailView(DetailView, ListView):

    model = Product
    template_name = 'shop/detail.html'
    context_object_name = 'latest_question_list'
    @login_required
    def get_queryset(self, id, product_slug=None): 
        product = get_object_or_404(Product, id=id, slug=product_slug)
        return render(self, 'shop/detail.html', {'product': product})

This error occurs. AttributeError: 'ProductDetailView' object has no attribute 'user'

urls.py

urlpatterns = [
    .........
    path('<int:id>/<product_slug>/', product_detail, name='product_detail'),
    .........
]

detail.html

{% extends 'base.html' %}
{% block title %}Product Detail{% endblock %}
{% block content %}

<div class="col">
    <div class="alert alert-info" role="alert">Detail</div>

    <div class="container">
        <div class="row">
            <div class="col-4">
                <img src="{{product.image.url}}" width="100%">
            </div>
            <div class="col">
                <h1 class="display-6">{{product.cname}}</h1>
                      <p class="card-text">{{product.pname}}</p>


                <h5><span class="badge badge-secondary">Description</span>{{product.description|linebreaks }}</h5>
                {% if product.author.username == user.username %}
                <a href="{% url 'shop:product_update' pk=product.id product_slug=product.slug %}" class="btn btn-outline-primary btn-xs mr-1 mt-1 float-left">Update</a>
                <a href="{% url 'shop:product_delete' pk=product.id product_slug=product.slug %}" class="btn btn-outline-danger btn-xs mr-1 mt-1 float-left">Delete</a>
                {% endif %}
                {% if product.author.username != user.username %}
                <a href="#" class="btn btn-secondary btn-xs mr-1 mt-1 float-left">Inquiry</a>
                {% endif %}
                <a href="/" class="btn btn-info btn-xs mt-1 float-left">Continue shopping</a>

            </div>
        </div>


    </div>
    <p></p>

<div class="col">
    <div class="alert alert-info" role="alert">Products added by registrants</div>


    <div class="container">
        {% for product in products %}
        <div class="row">

            {% if product.user.username == user.username %}
            <div class="col-4">
                <img src="{{product.image.url}}" width="auto" height="250">
            </div>
            <div class="col">
                <h1 class="display-6">{{product.pname}}</h1>
                <h5><span class="badge badge-secondary">Description</span>{{product.description|linebreaks}}</h5>
            </div>
            {% endif %}
        </div>
        {% endfor %}
    </div>

{% endblock %}

Please help me how to fix it. I would also appreciate it if you would recommend any textbooks that I can refer to.


Solution

  • I have a similar situation. I wanted to display create form, detail and list on the same page:

    urls:

    example_urlpatterns = [
        path('', views.ExampleCreateView.as_view(), name='_list'),
        path('new/', views.ExampleCreateView.as_view(), name='_create'),
        path('<int:pk>/', views.ExampleCreateView.as_view(), name='_detail'),
        path('<int:pk>/del', views.ExampleDeleteView.as_view(), name='_del'),
    ]
    urlpatterns = [
        # ...
        path('example/', include(example_urlpatterns)),
        # ...
    ]
    

    As you can see, I have two views, ExampleCreateView (also providing detail and list) and ExampleDeleteView for deleting. ExampleCreateView is primarily a create view:

    class ExampleCreateView(CreateView):
        template_name = 'example.html'
        form_class = ExampleCreateForm
        model = Example
    
        def form_valid(self, form):
            pass  # Stuff to do with a valid form
    
        # add user info from request to the form
        def get_form_kwargs(self, *args, **kwargs):
            kwargs = super().get_form_kwargs(*args, **kwargs)
            kwargs['user'] = self.request.user
            return kwargs
    
        # Create appropriate context    
        def get_context_data(self, **kwargs):
            kwargs['object_list'] = Example.objects.order_by('ip')  # list
            try:  # If we have pk, create object with that pk
                pk = self.kwargs['pk']
                instances = Example.objects.filter(pk=pk)
                if instances:
                    kwargs['object'] = instances[0]
            except Exception as e:
                pass # No pk, so no detail
            return super().get_context_data(**kwargs)
    

    Because I'm inheriting from CreateView, all the form processing is taken care of by default.

    Adding the line kwargs['object_list'] =... makes it work as a list view, and the try block after that line makes it work as a detail view.

    In the template all relevant parts are displayed:

    {% if object %}
        {% comment %}
            ...display the object...   (Detail section)
        {% endcomment %}
    {% endif %}
    
    {% if form %}
        {% comment %}
            ...display the form...   (Create section)
        {% endcomment %}
    {% endif %}
    
    {% if object_list %}
        {% comment %}
            ...display the list...   (List section)
        {% endcomment %}
    {% endif %}
    

    Let me know if this helps