1

My application in django 1.11, with django floppy forms, after entering the e-mail address - it's ok, sending mail.

The problem is when there is no mail address and press the "send" button.

I am getting error:

'ProductDetailView' object has no attribute 'object'

Traceback said someting about this line in view:

context = super(ProductDetailView, self).get_context_data(**kwargs)

View.py

class ProductDetailView(PageeditorMixin, DetailView, FormView):
    model = models.Product
    form_class = forms.DownloadLinkForm
    template_name = 'products/product_detail.html'

    def get_success_url(self):
        return reverse('products:detail', args=[self.kwargs['slug'], self.kwargs['pk']])

    def get_context_data(self, **kwargs):
        context = super(ProductDetailView, self).get_context_data(**kwargs)
        context['events'] = self.object.events.all()
        return context

    def get_form_kwargs(self):
        kwargs = super(ProductDetailView, self).get_form_kwargs()
        kwargs['product_pk'] = self.kwargs['pk']
        return kwargs

    def form_valid(self, form):
        form.send_download_links()
        return super(ProductDetailView, self).form_valid(form)

forms.py

from django.core.mail import send_mail
from django.template.loader import render_to_string
import floppyforms as forms

from products import models


class DownloadLinkForm(forms.Form):
    email = forms.EmailField(required=True)
    email_template = 'products/email.html'

    def __init__(self, product_pk, *args, **kwargs):
        super(DownloadLinkForm, self).__init__(*args, **kwargs)
        self.product = self.get_product_object(product_pk)

    def get_product_object(self, product_pk):
        return models.Product.objects.get(pk=product_pk)

    def get_downloads(self):
        return self.product.downloads.all()

    def get_download_links(self):
        list_download_links = []
        for download in self.get_downloads():
            list_download_links.append(models.DownloadLink.objects.create(download_file=download, email=self.cleaned_data['email']))
        return list_download_links

    def send_download_links(self):
        context = {
            'list_download_links': self.get_download_links()
        }
        html_message = render_to_string(self.email_template, context)
        return send_mail('Download link', '', 'mail@mail.com', [self.cleaned_data['email']], html_message=html_message)

EDIT

url:

url(r'^/(?P<slug>[-\w]+)/(?P<pk>[\d]+)$', views.ProductDetailView.as_view(), name='detail'),

model:

class Product(models.Model):
    title = models.CharField(max_length=500)
    subtitle = models.CharField(max_length=1000, blank=True)
    image = FilerImageField(related_name="image_product")
    logo = FilerImageField(blank=True, null=True, related_name="logo_product")
    events = models.ManyToManyField('events.Event', related_name="products")

Traceback

AttributeError at /en/products/next-title-de/2
'ProductDetailView' object has no attribute 'object'
Request Method: POST
Request URL:    http://127.0.0.1:8000/en/products/next-title-de/2
Django Version: 1.11.2
Exception Type: AttributeError
Exception Value:    
'ProductDetailView' object has no attribute 'object'
Exception Location: Path/venv/lib/python3.5/site-packages/django/views/generic/detail.py in get_context_data, line 101
Python Executable:  Path/venv/bin/python
Python Version: 3.5.2
Python Path:    
['Path/www',
 'Path/www',
 'Path/venv/lib/python35.zip',
 'Path/venv/lib/python3.5',
 'Path/venv/lib/python3.5/plat-x86_64-linux-gnu',
 'Path/venv/lib/python3.5/lib-dynload',
 '/usr/lib/python3.5',
 '/usr/lib/python3.5/plat-x86_64-linux-gnu',
 'Path/venv/lib/python3.5/site-packages',
 'Path/venv/src/next',
 'Path/venv/src/page',
 'Path/venv/src/progress',
 'Path/www/../..']
Server time:    Thu, 22 Feb 2018 17:28:16 +0100
Traceback Switch to copy-and-paste view

Path/venv/lib/python3.5/site-packages/django/core/handlers/exception.py in inner
            response = get_response(request) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/core/handlers/base.py in _legacy_get_response
            response = self._get_response(request) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/core/handlers/base.py in _get_response
                response = self.process_exception_by_middleware(e, request) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/core/handlers/base.py in _get_response
                response = wrapped_callback(request, *callback_args, **callback_kwargs) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/views/generic/base.py in view
            return self.dispatch(request, *args, **kwargs) ...
▶ Local vars
/Path/venv/src/page/page/views.py in dispatch
            from page.page import utils
            page = self.lookup_page(self.get_url(kwargs.get('url')))
            if not utils.check_page_permission(self.request.user, page, "view"):
                return HttpResponseForbidden()
        self.is_admin = self.has_page_permissions(request)
        self.is_edit = self.is_admin and 'edit' in request.GET
        return super(PageMixin, self).dispatch(request, *args, **kwargs) ...
    def has_page_permissions(self, request):
        """
        This method is used to check if the user has permissions to use the
        page editor.
        """
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/views/generic/base.py in dispatch
        return handler(request, *args, **kwargs) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/views/generic/edit.py in post
            return self.form_invalid(form) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/views/generic/edit.py in form_invalid
        return self.render_to_response(self.get_context_data(form=form)) ...
▶ Local vars
Path/www/products/views.py in get_context_data
        context = super(ProductDetailView, self).get_context_data(**kwargs) ...
▶ Local vars
Path/venv/src/page/page/views.py in get_context_data
        return super(PageMixin, self).get_context_data(**context) ...
▶ Local vars
Path/venv/lib/python3.5/site-packages/django/views/generic/detail.py in get_context_data
        if self.object: ...
▶ Local vars
user9192656
  • 549
  • 3
  • 16
  • Click the `Switch to copy-and-paste view` button before you copy the traceback. – Alasdair Feb 22 '18 at 16:48
  • There is a [section in the docs](https://docs.djangoproject.com/en/2.0/topics/class-based-views/mixins/#using-formmixin-with-detailview) about how to combine a form view and detail view. It suggests that it's better to have a `DetailView` to handle the GET requests and a separate `FormView` to handle the POST requests. – Alasdair Feb 22 '18 at 22:28
  • @Alasdair my unit test failed here: `self.assertContains(response, "
    – user9192656 Feb 23 '18 at 08:22
  • Your dont mention a unit test anywhere, so that error doesn’t seem to have anything to do with your question. Try and debug the problem.. what *does* the response contain? How could you change it to make the test pass? – Alasdair Feb 23 '18 at 09:50

1 Answers1

2

The object attribute is only set in the get_context_data() of the SingleObjectMixin, which is used by the DetailView. This method probably is overridden by one of the classes/mixin in your multiple inheritance.

But you can use the get_object() method instead:

def get_context_data(self, **kwargs):
    context = super(ProductDetailView, self).get_context_data(**kwargs)
    context['events'] = self.get_object().events.all()
    return context
Vitor Freitas
  • 3,550
  • 1
  • 24
  • 35