2
votes

I'm still new at django so please bear with me. I'm trying to make a website about books. Now I'm having error like this

MultipleObjectsReturned at /premium/1/ get() returned more than one Book -- it returned 2!

I don't know where to look for the error. Here is my example code.

class PageDetailView(LoginRequiredMixin, generic.View):

    def get(self, request, *args, **kwargs):
        book = get_object_or_404(Book)
        page = get_object_or_404(Page)
        user_membership = get_object_or_404(Customer, user=request.user)
        user_membership_type = user_membership.membership.membership_type
        user_allowed = book.allowedMembership.all()
        context = {'object': None}
        if user_allowed.filter(membership_type=user_membership_type).exists():
            context = {'object': page}
        return render(request, "catalog/page_detail.html", context)

Traceback:

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\exception.py" in inner 34. response = get_response(request)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\base.py" in _get_response 126. response = self.process_exception_by_middleware(e, request)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\core\handlers\base.py" in _get_response 124. response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\views\generic\base.py" in view 68. return self.dispatch(request, *args, **kwargs)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\contrib\auth\mixins.py" in dispatch 52. return super().dispatch(request, *args, **kwargs)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\views\generic\base.py" in dispatch 88. return handler(request, *args, **kwargs)

File "C:\Users\admin\thesis\blackink_website\catalog\views.py" in get 127. book = get_object_or_404(Book)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\shortcuts.py" in get_object_or_404 93. return queryset.get(*args, **kwargs)

File "C:\Users\admin\AppData\Local\Programs\Python\Python37-32\lib\site-packages\django\db\models\query.py" in get 403. (self.model._meta.object_name, num)

Exception Type: MultipleObjectsReturned at /premium/1/ Exception Value: get() returned more than one Book -- it returned 2!

I will update the post if the information is not enough. Thanks in advance.

UPDATE

here's my models.py

class Book(models.Model):


title = models.CharField(max_length=200) #i deleted some info to make it shorter

allowedMembership = models.ManyToManyField(Membership, blank=True)


def get_absolute_url(self):
    return reverse('book-detail',  args=[str(self.id)])

def __str__(self):
    return self.title

@property
def pages(self):
    return self.page_set.all()



class Page(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE, null=True)
file = models.FileField(upload_to='book_content', validators=[pdf_file_extension], help_text="PDF File Only")
timestamp = models.DateTimeField(default=timezone.now)


def __str__(self):
    return self.book.title

def get_absolute_url(self):
    return reverse('page-detail',   args=[str(self.id)])

page_detail.html

{% if user_membership and user_membership.user == request.user %}
        {% for content in book.pages %}
            <a href="{{ content.get_absolute_url }}" class="site-btn">Read</a>
        {% endfor %}
        {% else %}
        <button class="site-btn" disabled="disabled">VIP</button>
        {% endif %}
2
Is there a model relationship between your Book and Page? Please post your models.pyDaniel Holmes
i posted my models.py.Giranyx Pornasdoro
get_object_or_404 expects to return a single object. book = get_object_or_404(Book) is failing because there are two books in the database. Which book do you want to fetch? On the next line you have page = get_object_or_404(Page) - which page do you want to fetch? Finally, please show your URL patterns.Alasdair

2 Answers

2
votes

The MultipleObjectsReturned exception is raised by a query if only one object is expected, but multiple objects are returned. A base version of this exception is provided in django.core.exceptions; each model class contains a subclassed version that can be used to identify the specific object type that has returned multiple objects.

I am assuming that you are using the recent Django version. See the proper documentation based on your own version by switching from very bottom the official site.

The following would be a best links for you.

Just try like this, I have not tested but I am sure it will work as I had already tested before. Also check the above documentation & that is enough to get rid of this problem.

Configure your url based on https://docs.djangoproject.com/en/2.1/topics/class-based-... so that you could pass page_id and your url pattern should look something like path('pages/<int:page_id>', PageDetailView.as_view()) or re_path("^pages/(?<page_id>\d+)$", PageDetailView. as_view()).

Be careful while using path(), re_path(), url() as they have their own style + advantage.

Update:

After looking into the relationship between Book (parent model) and Page (child model), I changed the urlpattern from path('pages/<int:page_id>/books/<int:book' to path('pages/<page_id> and id of page is enough to fetch related book as there's a ForeignKey relationship between both the model.

As your view is PageDetailView, it's good to pass only id of the page for better design as others suggest (you can pass several url parameters too based on requirements but here we don't need).

from django.http import Http404

class PageDetailView(LoginRequiredMixin, generic.View):

    def get(self, request, *args, **kwargs):
        try:
            # page = get_object_or_404(Page)
            page_id = self.kwargs["page_id"]
            page = Page.objects.get(pk=page_id) 

            # book = get_object_or_404(Book)
            book_id = page.book.pk
            book = Book.objects.get(pk=book_id)

            # user_membership = get_object_or_404(Customer, user=request.user)
            user_membership = Customer.objects.get(user=request.user)

            user_membership_type = user_membership.membership.membership_type
            user_allowed = book.allowedMembership.all()
            context = {'object': None}
            if user_allowed.filter(membership_type=user_membership_type).exists():
                context = {'object': page}
            return render(request, "catalog/page_detail.html", context)
        except Book.DoesNotExist:
            raise Http404("Book with id {0} does not exist".format(book_id))
        except Page.DoesNotExist:
            raise Http404("Page with id {0} does not exist".format(page_id))
        except Customer.DoesNotExist:
            raise Http404("Cutomer does not exist")
        except Exception as e: 
            raise Exception(str(e)) 
0
votes

The reason you are getting this error is because you are using get_object_or_404 to get a single book, but the issue is your query is returning multiple books. This usually happens when there is no unique constraint on the field you use to lookup the book with (i.e. getting object by the title "Jungle Book" returns two books with the same title).

To fix this, get the object using a field that is guaranteed to be unique, such as an id. For example:

urls.py

path('page-details/<str:book_id>/<str:page_id>/', PageDetailView.as_view(), name='page_details'),

views.py

class PageDetailView(LoginRequiredMixin, generic.View):
    def get(self, request, *args, **kwargs):
        book = get_object_or_404(id=kwargs['book_id'])
        page = get_object_or_404(id=kwargs['page_id'])
        [...]