遵循Mozilla django教程时异常"unhashable type: 'list'"



我正在 https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django 学习Mozilla出色的Django教程,并引入了一个我找不到的错误。

本教程设置了一个简单的库目录系统。在上一节中,本教程设置了一个书籍详细信息视图,我可以在其中查看具有给定 id 的书籍的所有实例,

方法是转到:
http://192.168.0.28:8000/catalog/book/<book-id>

这工作正常,但我以某种方式设法将其丢弃。当试图到达http://192.168.0.28:8000/catalog/book/4时,我现在得到:

TypeError at /catalog/book/4
unhashable type: 'list'
Request Method:     GET Request URL:    http://192.168.0.28:8000/catalog/book/4 Django Version:     2.1.1 Exception Type:   TypeError Exception Value:  unhashable type: 'list' Exception Location:     /home/mike/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/sql/compiler.py in get_order_by, line 287 Python Executable:     /home/mike/anaconda3/envs/miketestenv/bin/python Python Version:
3.6.6 Python Path:  
['/home/mike/Projects/locallibrary',  '/home/mike/anaconda3/envs/miketestenv/lib/python36.zip',  '/home/mike/anaconda3/envs/miketestenv/lib/python3.6',  '/home/mike/anaconda3/envs/miketestenv/lib/python3.6/lib-dynload',  '/home/mike/anaconda3/envs/miketestenv/lib/python3.6/site-packages']
Server time:    Tue, 16 Oct 2018 11:20:39 +0100 Error during template rendering
In template /home/mike/Projects/locallibrary/catalog/templates/base_generic.html, error at line 0 unhashable type: 'list' 1     <!DOCTYPE html> 2   <html lang="en"> 3  <head> 4      {% block title %}<title>Local Library</title>{% endblock %} 5       <meta charset="utf-8"> 6    <meta name="viewport" content="width=device-width, initial-scale=1"> 7      <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"> 8       <!-- Add additional CSS in static file
--> 9     {% load static %} 10    <link rel="stylesheet" href="{% static 'css/styles.css' %}">

书 ID 号 4 是有效的书 ID。所有图书 ID 都会发生相同的错误。

urls.py 中的相关部分是:

path('book/<int:pk>', views.BookDetailView.as_view(), name='book-detail'),

从 views.py:

class BookDetailView(generic.DetailView):
model = Book

base_generic.html模板很好,适用于所有其他类型的页面。导入book_detail模板时出现问题。book_detail.py如下所示:

{% extends "base_generic.html" %}
{% block content %}
<h1>Title: {{ book.title }}</h1>
<p><strong>Author:</strong> <a href="">{{ book.author }}</a></p> <!-- author detail link not yet defined -->
<p><strong>Summary:</strong> {{ book.summary }}</p>
<p><strong>ISBN:</strong> {{ book.isbn }}</p> 
<p><strong>Language:</strong> {{ book.language }}</p>  
<p><strong>Genre:</strong> {% for genre in book.genre.all %} {{ genre }}{% if not forloop.last %}, {% endif %}{% endfor %}</p> 
<div style="margin-left:20px;margin-top:20px">
<h4>Copies</h4>
{% for copy in book.bookinstance_set.all %}
<hr>
<p class="{% if copy.status == 'a' %}text-success{% elif copy.status == 'm' %}text-danger{% else %}text-warning{% endif %}">{{ copy.get_status_display }}</p>
{% if copy.status != 'a' %}
<p><strong>Due to be returned:</strong> {{copy.due_back}}</p>
{% endif %}
<p><strong>Imprint:</strong> {{copy.imprint}}</p>
<p class="text-muted"><strong>Id:</strong> {{copy.id}}</p>
{% endfor %}
</div>

如果我从中删除行

{% for copy in book.bookinstance_set.all %}

{% endfor %}

异常被清除,

如果我现在转到 python manage.py shell:

from catalog.models import Book
from catalog.models import BookInstance

我可以在书中看到有效数据:

In [8]: Book.objects.all()
Out[8]: <QuerySet [<Book: Hitchikers Guide>, <Book: So Long, and thanks for all the fish>, <Book: Harry potter 1>, <Book: Harry potter 2>, <Book: Oreilly Django>, <Book: another oreilly book>]>

我在尝试列出 BookInstance 项目时收到"类型错误:不可哈希类型:'列表'"错误(即使在删除所有 BookInstance 之后(:

In [13]: BookInstance.objects.all() Out[13]:
--------------------------------------------------------------------------- TypeError                                 Traceback (most recent call last) ~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj)
700                 type_pprinters=self.type_printers,
701                 deferred_pprinters=self.deferred_printers)
--> 702             printer.pretty(obj)
703             printer.flush()
704             return stream.getvalue()
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/IPython/lib/pretty.py in pretty(self, obj)
398                         if cls is not object 
399                                 and callable(cls.__dict__.get('__repr__')):
--> 400                             return _repr_pprint(obj, self, cycle)
401
402             return _default_pprint(obj, self, cycle)
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/IPython/lib/pretty.py in _repr_pprint(obj, p, cycle)
693     """A pprint that just redirects to the normal repr function."""
694     # Find newlines and replace them with p.break_()
--> 695     output = repr(obj)
696     for idx,output_line in enumerate(output.splitlines()):
697         if idx:
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/query.py in __repr__(self)
242
243     def __repr__(self):
--> 244         data = list(self[:REPR_OUTPUT_SIZE + 1])
245         if len(data) > REPR_OUTPUT_SIZE:
246             data[-1] = "...(remaining elements truncated)..."
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/query.py in __iter__(self)
266                - Responsible for turning the rows into model objects.
267         """
--> 268         self._fetch_all()
269         return iter(self._result_cache)
270
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/query.py in _fetch_all(self)    1184     def _fetch_all(self):    1185         if self._result_cache is None:
-> 1186             self._result_cache = list(self._iterable_class(self))    1187         if self._prefetch_related_lookups and not self._prefetch_done:    1188    self._prefetch_related_objects()
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/query.py in __iter__(self)
52         # Execute the query. This will also fill compiler.select, klass_info,
53         # and annotations.
---> 54         results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
55         select, klass_info, annotation_col_map = (compiler.select, compiler.klass_info,
56                                                   compiler.annotation_col_map)
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/sql/compiler.py in execute_sql(self, result_type, chunked_fetch, chunk_size)    1050   result_type = result_type or NO_RESULTS    1051         try:
-> 1052             sql, params = self.as_sql()    1053             if not sql:    1054                 raise EmptyResultSet
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/sql/compiler.py in as_sql(self, with_limits, with_col_aliases)
447         refcounts_before = self.query.alias_refcount.copy()
448         try:
--> 449             extra_select, order_by, group_by = self.pre_sql_setup()
450             for_update_part = None
451             # Is a LIMIT/OFFSET clause needed?
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/sql/compiler.py in pre_sql_setup(self)
49         """
50         self.setup_query()
---> 51         order_by = self.get_order_by()
52         self.where, self.having = self.query.where.split_having()
53         extra_select = self.get_extra_select(order_by, self.select)
~/anaconda3/envs/miketestenv/lib/python3.6/site-packages/django/db/models/sql/compiler.py in get_order_by(self)
285             descending = order == 'DESC'
286
--> 287             if col in self.query.annotation_select:
288                 # Reference to expression in SELECT clause
289                 order_by.append((
TypeError: unhashable type: 'list'

谁能告诉我下一步该怎么做?我显然可以再次开始本教程,但这不会让我更接近于理解我做错了什么,并学习如何在将来找到解决此问题的方法。谢谢!

根据布鲁诺的要求,models.py 如下所示:

from django.db import models
from django.contrib.auth.models import User
from datetime import date
# Create your models here.
class Genre(models.Model):
"""Model representing a book genre."""
name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
def __str__(self):
"""String for representing the Model object."""
return self.name
from django.urls import reverse # Used to generate URLs by reversing the URL patterns
class Book(models.Model):
"""Model representing a book (but not a specific copy of a book)."""
title = models.CharField(max_length=200)
# Foreign Key used because book can only have one author, but authors can have multiple books
# Author as a string rather than object because it hasn't been declared yet in the file
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
# ManyToManyField used because genre can contain many books. Books can cover many genres.
# Genre class has already been defined so we can specify the object above.
genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
def __str__(self):
"""String for representing the Model object."""
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book."""
return reverse('book-detail', args=[str(self.id)])
def display_genre(self):
"""Create a string for the Genre. This is required to display genre in Admin."""
return ', '.join(genre.name for genre in self.genre.all()[:3])
display_genre.short_description = 'Genre'    
import uuid # Required for unique book instances
class BookInstance(models.Model):
"""Model representing a specific copy of a book (i.e. that can be borrowed from the library)."""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
book = models.ForeignKey('Book', on_delete=models.SET_NULL, null=True) 
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
borrower = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
@property
def is_overdue(self):
if self.due_back and date.today() > self.due_back:
return True
return False    
LOAN_STATUS = (
('m', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='m',
help_text='Book availability',
)
class Meta:
ordering = ['due_back'],
permissions = (("can_mark_returned", "Set book as returned"),)  
def __str__(self):
"""String for representing the Model object."""
return f'{self.id} ({self.book.title})'
class Author(models.Model):
"""Model representing an author."""
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
"""Returns the url to access a particular author instance."""
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
"""String for representing the Model object."""
return f'{self.last_name}, {self.first_name}'    

在 BookInstance 中定义ordering后有一个杂散逗号,这会将其转换为包含列表的元组。删除逗号。

最新更新