我正在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
我可以在Book中看到有效数据:
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项时(即使删除所有BookInstances之后),我也会收到“ TypeError:无法散列的类型:'list'”错误:
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'
谁能给我一些建议,下一步该怎么做?很明显,我可以再次开始本教程,但这不会使我更加了解我做错了什么,并在将来学习如何找到解决此问题的方法。谢谢!
按照Bruno的要求,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}'
答案 0 :(得分:2)
在BookInstance中定义ordering
后,您会有一个逗号,它将其变成包含列表的元组。删除逗号。