Part 2:模型与后台

阅读: 270077     评论:153

接着第一部分,本节将讲述如何配置数据库,编写第一个模型以及简要的介绍下Django自动生成的后台管理admin站点。

一、数据库配置

打开mysite/settings.py配置文件,这是整个Django项目的设置中心。Django默认使用SQLite3数据库,因为Python原生支持SQLite3数据库,所以你无须安装任何程序,就可以直接使用它。当然,如果你是在创建一个实际的项目,可以使用类似MySQL的数据库,避免以后数据库迁移的相关问题。

下面是默认的数据库配置:

# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

如果你想使用其他的数据库,请先安装相应的数据库操作模块,并将settings文件中DATABASES位置的’default’字典进行相应的修改,用于连接你的数据库。其中:

  • ENGINE(引擎):可以是django.db.backends.sqlite3django.db.backends.postgresqldjango.db.backends.mysqldjango.db.backends.oracle,当然其它的也行。

  • NAME(数据库名称):类似Mysql数据库管理系统中用于保存项目内容的数据库的名字。如果你使用的是默认的SQLite3,那么数据库将作为一个文件将存放在你的本地机器内,此时的NAME应该是这个文件的完整绝对路径包括文件名,默认情况下该文件储存在你的项目根目录下。

注意

  • 在使用非SQLite3数据库时,请务必预先在数据库管理系统的提示符交互模式下创建数据库,你可以使用命令:CREATE DATABASE database_name;。Django不会自动帮你做这一步工作。
  • 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据表。(在实际项目中也需要确认这一条要求。)
  • 如果你使用的是SQLite3,那么你无需做任何预先配置,直接使用就可以了。

在修改settings文件时,请顺便将TIME_ZONE设置为国内所在的时区Asia/Shanghai,这样显示的就是我们北京时间。

同时,请注意settings文件中顶部的INSTALLED_APPS设置项。它列出了所有的项目中被激活的Django应用(app)。你必须将你自己创建的app注册在这里。每个应用可以被多个项目使用,并且可以打包和分发给其他人在他们的项目中使用。

默认情况,INSTALLED_APPS中会自动包含下列条目,它们都是Django自动生成的:

  • django.contrib.admin:admin管理后台站点
  • django.contrib.auth:身份认证系统
  • django.contrib.contenttypes:内容类型框架
  • django.contrib.sessions:会话框架
  • django.contrib.messages:消息框架
  • django.contrib.staticfiles:静态文件管理框架

上面的那些应用会默认被启动,并且也需要建立一些数据库表,所以在使用它们之前我们要在数据库中创建这些表。使用下面的命令创建数据表:

$ python manage.py migrate

migrate命令将遍历INSTALLED_APPS设置中的所有项目,在数据库中创建对应的表,并打印出每一条动作信息。如果你感兴趣,可以在你的数据库命令行下输入:\dt (PostgreSQL)、 SHOW TABLES;(MySQL)或 .schema(SQLite3) 来列出 Django 所创建的表。

提示:对于极简主义者,你完全可以在INSTALLED_APPS内注释掉任何或者全部的Django提供的通用应用。这样,migrate也不会再创建对应的数据表。

二、创建模型

现在,我们来定义模型model,模型本质上就是数据库表的布局,再附加一些元数据。

Django通过自定义Python类的形式来定义具体的模型,每个模型的物理存在方式就是一个Python的类Class,每个模型代表数据库中的一张表,每个类的实例代表数据表中的一行数据,类中的每个变量代表数据表中的一列字段。Django通过模型,将Python代码和数据库操作结合起来,实现对SQL查询语言的封装。也就是说,你可以不会管理数据库,可以不会SQL语言,你同样能通过Python的代码进行数据库的操作,这就是所谓的ORM。Django通过ORM对数据库进行操作,奉行代码优先的理念,将Python程序员和数据库管理员进行分工解耦。

在这个简单的投票应用中,我们将创建两个模型:QuestionChoice。Question包含一个问题和一个发布日期。Choice包含两个字段:该选项的文本描述和该选项的投票数。每一条Choice都关联到一个Question。这些都是由Python的类来体现,编写的全是Python的代码,不接触任何SQL语句。现在,编辑polls/models.py文件,具体代码如下:

# polls/models.py

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

注意:新手在学习过程中出现了问题,90%是代码输入错误,比如question输入成questions。9%是没有仔细学习教程,缺少步骤。

上面的代码非常简单明了。每一个类都是django.db.models.Model的子类。每一个字段都是Field类的一个实例,例如用于保存文本数据的CharField和用于保存时间类型的DateTimeField,它们告诉Django每一个字段保存的数据类型。

每一个Field实例的名字就是字段的名字(如: question_text 或者 pub_date )。在你的Python代码中会使用这个值,你的数据库也会将这个值作为表的列名。

你也可以在每个Field中使用一个可选的第一位置参数用于提供一个人类可读的字段名,让你的模型更友好,更易读,并且将被作为文档的一部分来增强代码的可读性。比如例子中的Question.pub_date 定义了对人类友好的名字。

一些Field类必须提供某些特定的参数。例如CharField需要你指定max_length。这不仅是数据库结构的需要,同样也用于数据验证功能。

有必填参数,当然就会有可选参数,比如在votes里我们将其默认值设为0.

最后请注意,我们使用ForeignKey定义了一个外键关系。它告诉Django,每一个Choice关联到一个对应的Question(注意要将外键写在‘多数’的一方)。Django支持通用的数据关系:一对一,多对一和多对多。

三、启用模型

上面的代码看着有点少,其实包含了大量的信息,据此,Django会做下面两件事:

  • 创建该app对应的数据库表结构
  • 为Question和Choice对象创建基于Python的数据库访问API

但是,首先我们得先告诉Django项目,我们要使用投票app。

要将应用添加到项目中,需要在INSTALLED_APPS设置中增加指向该应用的配置文件的链接。对于本例的投票应用,它的配置类文件PollsConfig位于polls/apps.py脚本内,所以配置的点式路径为polls.apps.PollsConfig。我们需要在INSTALLED_APPS中,将该路径添加进去(字符串格式):

# mysite/settings.py

INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

实际上,在多数情况下,我们简写成‘polls’就可以了:

# mysite/settings.py

INSTALLED_APPS = [
'polls',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]

现在Django已经知道你的投票应用的存在了,并把它加入了项目大家庭。

我们需要再运行下一个命令:

$ python manage.py makemigrations polls

你会看到类似下面的提示:

Migrations for 'polls':
  polls\migrations\0001_initial.py
    - Create model Question
    - Create model Choice

通过运行makemigrations命令,Django 会检测你对模型文件的修改,也就是告诉Django你对模型有改动,并且你想把这些改动保存为一个“迁移(migration)”。

migrations是Django保存模型修改记录的文件,这些文件保存在磁盘上。在例子中,它就是polls/migrations/0001_initial.py,你可以打开它看看,里面保存的都是人类可读并且可编辑的内容,方便你随时手动修改。

接下来有一个叫做migrate的命令将对数据库执行真正的迁移动作。但是在此之前,让我们先看看在迁移的时候实际执行的SQL语句是什么。有一个叫做sqlmigrate的命令可以展示SQL语句,例如:

$ python manage.py sqlmigrate polls 0001

你将会看到如下类似的文本(经过适当的格式调整,方便阅读):

BEGIN;
--
-- Create model Question
--
CREATE TABLE "polls_question" (
    "id" serial NOT NULL PRIMARY KEY,
    "question_text" varchar(200) NOT NULL,
    "pub_date" timestamp with time zone NOT NULL
);
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
    "id" serial NOT NULL PRIMARY KEY,
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL,
    "question_id" integer NOT NULL
);
ALTER TABLE "polls_choice"
  ADD CONSTRAINT "polls_choice_question_id_c5b4b260_fk_polls_question_id"
    FOREIGN KEY ("question_id")
    REFERENCES "polls_question" ("id")
    DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");

COMMIT;

请注意:

  • 实际的输出内容将取决于您使用的数据库会有所不同。上面的是PostgreSQL的输出。
  • 表名是自动生成的,通过组合应用名 (polls) 和小写的模型名questionchoice 。 ( 你可以重写此行为。)
  • 主键 (IDs) 是自动添加的。( 你也可以重写此行为。)
  • 按照惯例,Django 会在外键字段名上附加 "_id" 。 (你仍然可以重写此行为。)
  • 生成SQL语句时针对你所使用的数据库,会为你自动处理特定于数据库的字段,例如 auto_increment (MySQL), serial (PostgreSQL), 或integer primary key (SQLite) 。 在引用字段名时也是如此 – 比如使用双引号或单引号。
  • 这些SQL命令并没有在你的数据库中实际运行,它只是在屏幕上显示出来,以便让你了解Django真正执行的是什么。

如果你感兴趣,也可以运行python manage.py check命令,它将检查项目中的错误,并不实际进行迁移或者链接数据库的操作。

现在,我们可以运行migrate命令,在数据库中进行真正的表操作了。

$ python manage.py migrate
Operations to perform:
    Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
    Rendering model states... DONE
    Applying polls.0001_initial... OK

migrate命令对所有还未实施的迁移记录进行操作,本质上就是将你对模型的修改体现到数据库中具体的表中。Django通过一张叫做django_migrations的表,记录并跟踪已经实施的migrate动作,通过对比获得哪些迁移尚未提交。(请务必牢记这张表的作用和名称)

迁移的功能非常强大,允许你随时修改你的模型,而不需要删除或者新建你的数据库或数据表,在不丢失数据的同时,实时动态更新数据库。我们将在后面的章节对此进行深入的阐述,但是现在,只需要记住修改模型时的操作分三步

  • 在models.py中修改模型;
  • 运行python manage.py makemigrations为改动创建迁移记录文件;
  • 运行python manage.py migrate,将操作同步到数据库。

之所以要将创建和实施迁移的动作分成两个命令两步走是因为你也许要通过版本控制系统(例如github,svn)提交你的项目代码,如果没有一个中间过程的保存文件(migrations),那么github如何知道以及记录、同步、实施你所进行过的模型修改动作呢?毕竟,github不和数据库直接打交道,也没法和你本地的数据库通信。但是分开之后,你只需要将你的migration文件(例如上面的0001)上传到github,它就会知道一切。

四、体验模型自带的API

Django模型层自带ORM系统,会自动为每个模型创建数据库访问的API,直接拿来用就可以,非常简单、方便、易学。

下面,让我们进入Python交互环境,尝试使用Django提供的数据库访问API。要进入Python的shell,请输入命令:

$ python manage.py shell

在shell中,我们可以做一些测试性、探索性、研究性的操作,但是要注意,这和在脚本中编写代码一样,也有可能会修改数据库中的实际数据。

相比较直接输入“python”命令的方式进入Python环境,调用manage.py参数能将DJANGO_SETTINGS_MODULE环境变量导入,它将自动按照mysite/settings.py中的设置,配置好你的python shell环境,这样,你就可以导入和调用任何你项目内的模块了。

或者你也可以这样,先进入一个纯净的python环境,然后启动Django,具体如下:

>>> import django
>>> django.setup()

当你进入shell后,尝试一下下面的API吧(这些代码必须执行,否则会影响后面的教程。我们不必管这些API的具体细节,先混个脸熟):

    >>> from polls.models import Question, Choice # 导入我们写的模型类


    # 现在系统内还没有questions对象
    >>> Question.objects.all()
    <QuerySet []>


    # 创建一个新的question对象
    # Django推荐使用timezone.now()代替python内置的datetime.datetime.now()
    # 这个timezone就来自于Django的依赖库pytz
    from django.utils import timezone
    >>> q = Question(question_text="What's new?", pub_date=timezone.now())

    # 你必须显式的调用save()方法,才能将对象保存到数据库内
    >>> q.save()

    # 默认情况,你会自动获得一个自增的名为id的主键
    >>> q.id
    1

    # 通过python的属性调用方式,访问模型字段的值
    >>> q.question_text
    "What's new?"
    >>> q.pub_date
    datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

    # 通过修改属性来修改字段的值,然后显式的调用save方法进行保存。
    >>> q.question_text = "What's up?"
    >>> q.save()

    # objects.all() 用于查询数据库内的所有questions
    >>> Question.objects.all()
    <QuerySet [<Question: Question object>]>

这里等一下:上面的<Question: Question object>是一个不可读的内容展示,你无法从中获得任何直观的信息,为此我们需要一点小技巧,让Django在打印对象时显示一些我们指定的信息。

返回polls/models.py文件,修改一下question和Choice这两个类,代码如下:

# polls/models.py

from django.db import models

class Question(models.Model):
    # ...
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    # ...
    def __str__(self):
        return self.choice_text

这个技巧不但对你打印对象时很有帮助,在你使用Django的admin站点时也同样有帮助。

另外,这里我们再自定义一个模型的方法,用于判断问卷是否最近时间段内发布度的:

# polls/models.py

import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    # 是否在当前发布的问卷
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

请注意上面分别导入了两个关于时间的模块,一个是python内置的datetime,一个是Django工具包提供的timezone。

保存修改后,我们重新启动一个新的python shell,再来看看其他的API:

>>> from polls.models import Question, Choice

# 先看看__str__()的效果,直观多了吧?
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

# Django提供了大量的关键字参数查询API
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>

# 获取今年发布的问卷
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>

# 查询一个不存在的ID,会弹出异常
>>> Question.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Question matching query does not exist.

# Django为主键查询提供了一个缩写:pk。下面的语句和Question.objects.get(id=1)效果一样.
>>> Question.objects.get(pk=1)
<Question: What's up?>

# 看看我们自定义的方法用起来怎么样
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

# 显示所有与q对象有关系的choice集合,目前是空的,还没有任何关联对象。
>>> q.choice_set.all()
<QuerySet []>

# 创建3个choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice对象可通过API访问和他们关联的Question对象
>>> c.question
<Question: What's up?>

# 同样的,Question对象也可通过API访问关联的Choice对象
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

# API会自动进行连表操作,通过双下划线分割关系对象。连表操作可以无限多级,一层一层的连接。
# 下面是查询所有的Choices,它所对应的Question的发布日期是今年。(重用了上面的current_year结果)
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>

# 使用delete方法删除对象
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

关于模型的使用就暂时先介绍这么多。这部分内容是Django项目的核心,也是动态网站与数据库交互的核心,对于初学者,再难理解也要理解。

五、admin后台管理站点

很多时候,我们不光要开发针对客户使用的前端页面,还要给后台管理人员提供相应的管理界面。但是大多数时候为你的团队或客户编写用于增加、修改和删除内容的后台管理站点是一件非常乏味的工作,并且没有多少创造性,需要花不少的时间和精力。

Django最大的优点之一,就是体贴的为你提供了一个基于项目model创建的一个后台管理站点admin。这个界面只给站点管理员使用,并不对大众开放。虽然admin的界面可能不是那么美观,功能不是那么强大,内容不一定符合你的要求,但是它是免费的、现成的,并且还是可定制的,有完善的帮助文档,那么,你还要什么自行车?

(如果你对admin的界面美观有切实需求,可以尝试使用simpleui库,不要用xadmin)

1. 创建管理员用户

首先,我们需要通过下面的命令,创建一个可以登录admin站点的用户:

$ python manage.py createsuperuser

输入用户名:

Username: admin

输入邮箱地址:

Email address: xxx@xxx.xxx

输入密码:

Password: **********
Password (again): *********
Superuser created successfully.

注意:Django1.10版本后,超级用户的密码要求具备一定的复杂性,如果密码强度不够,Django会提示你,但是可以强制通过。

2. 启动开发服务器

执行runserver命令启动服务器后,在浏览器访问http://127.0.0.1:8000/admin/。你就能看到admin的登陆界面了:

小技巧:

以下内容不要实际操作,知道即可。

在实际环境中,为了站点的安全性,我们一般不能将管理后台的url随便暴露给他人,不能用/admin/这么简单的路径。

可以将根url路由文件mysite/urls.pyadmin.site.urls对应的表达式,换成你想要的,比如:

``` from django.contrib import admin from django.urls import path

urlpatterns = [ path('polls/', include('polls.urls')), path('control/', admin.site.urls), ] ```

这样,我们必须访问http://127.0.0.1:8000/control/才能进入admin界面。

3. 进入站点

利用刚才建立的admin账户,登陆admin,你将看到如下的界面:

当前只有两个可编辑的模型:Groups和Users。它们是django.contrib.auth模块提供的身份认证框架内的模型。

4. 注册投票应用

现在还无法看到投票应用,必须先在admin中进行注册,告诉admin站点,请将polls的模型加入站点内,接受站点的管理。

打开polls/admin.py文件,加入下面的内容:

from django.contrib import admin
from .models import Question

admin.site.register(Question)

5. 站点体验

注册question模型后,等待服务器重启动,然后刷新admin页面就能看到Question栏目了。

点击“Questions”,进入questions的修改列表页面。这个页面会显示所有的数据库内的questions对象,你可以在这里对它们进行修改。看到下面的“What’s up?”了么?它就是我们先前创建的一个question对象,并且通过__str__方法的帮助,显示了较为直观的信息,而不是一个冷冰冰的对象类型名称。

下面,点击What’s up?,可以进入该问卷对象的编辑界面:

这里需要注意的是:

  • 页面中的表单是由Question模型自动生成的。
  • 不同的模型字段类型(DateTimeField, CharField)会表现为不同的HTML input框类型。
  • 每一个DateTimeField都会自动生成一个可点击链接。日期是Today,并有一个日历弹出框;时间是Now,并有一个通用的时间输入列表框。

在页面的底部,则是一些可选项按钮:

  • delete:弹出一个删除确认页面
  • save and add another:保存当前修改,并加载一个新的空白的当前类型对象的表单。
  • save and continue editing:保存当前修改,并重新加载该对象的编辑页面。
  • save:保存修改,返回当前对象类型的列表页面。

如果Date published字段的值和你在前面教程创建它的时候不一致,可能是你没有正确的配置TIME_ZONE,在国内,通常是8个小时的时间差别。修改TIME_ZONE配置并重新加载页面,就能显示正确的时间了。

在页面的右上角,点击History按钮,你会看到你对当前对象的所有修改操作都在这里有记录,包括修改时间和操作人员。


 Part 1:请求与响应 Part 3:视图和模板 

评论总数: 153


点击登录后方可评论

请问刘老师,为什么我在Question.objects.all()后出现的是<QuerySet [<Question: Question object (1)>]>而不是<QuerySet [<Question: What's up?>]>呢



老师,具体举个例子,怎么设置,就那个name那,比方说我要用别的路径的mysql数据库。



q.choice_set这玩意哪里来的 前面还能理解 到这直接懵逼了,愁。



请问API结尾部分:c = q.choice_set.filter(choice_text__startswith='Just hacking'),其中的“startswith”这个变量是哪里定义的呢?



startWith是具体的查询操作,这一句是在查询choice_text字段以'Just hacking'开头的数据。 类似的操作还有int类型的__lt(小于)、__gt(大于),字符串类型的__contains(包含)等等



请问老师,我在执行"q.was_published_recently()"之后,报错"File "E:\PycharmProjects\mysite\polls\models.py", line 17, in was_published_recently return self.pub_date >= timezone.now() - datetime.timedelta(days=1) TypeError: can't compare datetime.datetime to datetime.date",请问这该怎么解决呀?



八成是你这里: pub_date = models.DateTimeField('date published') 写成了DateField 。新手解决方法:删掉已有数据,重新makemigrations和migrate



谢谢老师,问题的确是您说的那样!但是我在删除数据之后,也进行数据迁移了,在shell命令中想要查看模型中的数据Question.objects.all(),却报错: File "D:\Anaconda3\envs\python39\lib\sqlite3\dbapi2.py", line 64, in convert_date return datetime.date(*map(float, val.split(b"-"))) ValueError: could not convert string to float: b'01 08:26:05.811388' 百度了许久,也没有找到合适的解决方法,所以麻烦老师指正一下



删了项目重来吧。



这种情况可以删库吗(删库跑路?=-=) 重新迁移吗



请问老师 我在点击“”Question“”之后出现了no such table: polls_question的错误信息能不能帮忙解答一下



大概率是没有执行makemigration和migrate命令



請問老師 為什麼我只要使用__str__來顯示我的資料 在admin裡面點擊進去 就會出現__str__ returned non-string (type tuple)呢? 請問該如何解決



因为model.Model中的str方法本来返回的就是非字符串,需要你重写呀,在子类中重新__str__方法即可。



好强大的自行车



老师 我按照这个步骤到Question.objects.all()的时候获取不到 报<QuerySet [<Question: Question object (1)>]>是什么原因导致的



你这不是有一个对象在里面吗?



没有重新启动新的shell,api没有重新导入,更新。



完成此页练习



老师 我照着你的步骤,然后在>>> q = Question(question_text="What's new?", pub_date=timezone.now()) Traceback (most recent call last): File "<console>", line 1, in <module> TypeError: __init__() got an unexpected keyword argument 'question_text' 出现这个问题,我找不到原因,也百度了,感觉不是我这个错误,请老师指点一下,谢谢!



你看看你的polls/models 文件里面的代码,question_text 是不是有拼写错误



我也遇到这个问题了 请问怎么解决的



我遇到这个问题 是因为models.py里面把question_text达成了question_test 希望我的经验对大家有帮助。



老师,新手API这里看不懂怎么操作的。 您文中提到修改一下question和Choice这两个类 from django.db import models class Question(models.Model): # ... def __str__(self): return self.question_text class Choice(models.Model): # ... def __str__(self): return self.choice_text 就是把原来所有的code都替换成这个吗?还有后面: import datetime from django.db import models from django.utils import timezone class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) 是接着在models.py中继续修改吗?同时出现两个class Question嘛?



看懂了谢谢



请问这部分具体是怎样的 我改成后面这部分的吗 但是后面的操作导入没有choice, 报错了



By 一木岩一 On 2019年3月5日 15:36 统一解答一下修改question和Choice及 <QuerySet [<Question: Question object>]>的问题: 1. 博主文章中:返回polls/models.py文件,修改一下question和Choice这两个类,代码如下中的 #... 意思是省略之前的代码,而第二次修改是在之前的基础上再加 def __str__(self): return self.question_text ,包括后面的用于判断问卷是否最近时间段内发布度也是在最早的代码基础上添加即可,在相应的class中,并非将之前的删除再写。 2.__str__ 前后是两个下划线 不是一个,如果写成一个则会不显示原来数据。 终于给爷发现了



老师还有就是我修改了question_text的值后,而且调用.save()后为什么还是没有变化



修改的问题,我知道了必须用双引号"",用单引号‘’就不行,我以为这两个应该和python中一样是等价的



老师请问一下,数据库里面的默认时区怎么去修改,我创建questions实例后,发现发布日期不是中国时间,这个怎么去修改?我项目中的setting.py文件里面的时区已经修改成上海时间了



执行python manage.py sqlmigrate polls 0001后出现django.db.migrations.exceptions.BadMigrationError: Migration record in app polls has no Migration class的报错,自动生成的文件中时存在Migration类的啊,求解惑,谢谢。



原因已找到:我再migrations文件夹下建立了一个py文件,导致这个错误,但新的问题:为何在文件夹下建立文件后就会报错呢?



migrate会遍历migrations,其中如果文件有错误,执行就会报错。



老师,按照学习流程正常输入admin和密码后,网页发生403错误?这是为什么呢?



描述不清楚,不好回答。 403多半是csrf问题,但访问内置admin一般不会碰到csrf问题。



这是我百度到的,我用第一种方法把问题解决了 有两种解决办法。 方法1:找到项目中的在settings配置文件中,把#'django.middleware.csrf.CsrfViewMiddleware’注释掉 方法2:在html的代码中,凡是遇到post提交表单,都在表单的标签下加上{% csrf_token %} ———————————————— 版权声明:本文为CSDN博主「程序-缘」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_41579555/article/details/89813607



老师,我看视频时,你说有些新手很容易出错,是因为是数据库中django_migrations已经有了记录,所以新的操作都没办法纠正。那请问一下怎么清空django_migrations里面的记录呢?



在视频里有讲解



我买了视频,不过我看的还是第一部分,请问在哪一节讲了这个呢??我做个标注



我也记不住,太细节了。核心是删除initxxx文件和数据库里django_migrations表中的内容



老师,我按照步骤来, 只有这两行 - Create model Question - Create model Choice 没有下面这行?百度很久,没有结果 Add field question to choice



你的这个问题解决了没,我也遇到一样的问题



我也是这个问题,我的django 版本是 3.0 你的呢



我也3.0这个版本。无解



你是说第三行提示信息没有? 如果不影响使用,可以忽略。



没有这个提示影响后面的学习步骤吗?



老师,您好。 from polls.models import Question, Choice 报这个错误: RuntimeError: Model class polls.models.Question doesn't declare an explicit app_label and isn't in an application in INSTALLED_APPS.



要在INSTALLED_APPS里注册app



我的是python3.7 和 django 3.0.3 的为什么在http://127.0.0.1:8000/admin 页面输入账号密码点击登陆后怎么也登不上啊? 动不动就是无法连接localhost服务器什么的。



这好像是个bug,往下翻评论



OperationalError at /admin/polls/question/ no such table: polls_question Request Method: GET Request URL: http://127.0.0.1:8000/admin/polls/question/ Django Version: 2.1.15 Exception Type: OperationalError Exception Value: no such table: polls_question Exception Location: D:\Programs\Python\Python36\lib\site-packages\django-2.1.15-py3.6.egg\django\db\backends\sqlite3\base.py in execute, line 298 Python Executable: D:\Programs\Python\Python36\python.exe Python Version: 3.6.8 Python Path: ['E:\\web\\mysite', 博主,点Question进去发生错误怎么解决



你应该是没有创建数据表,migrate命令



博主, return self.pub_date >= timezone.now() - datetime.timedelta(days=1)这行, pub_date 的类型和 timezone.now() - datetime.timedelta(days=1)的结果可以比较吗? 我使用的python版本是3.7.3 Django版本2.2 ,会报错'<' not supported between instances of 'datetime.datetime' and 'NoneType'



从错误提示来看,问题出在比较的两者中,有一个是空值。所以,查查看self.pub_date的具体值。



是的,之前pub_date字段没有赋值所以无法比较,赋值之后可以了! 谢谢博主!



怎么赋值呢?我也遇到这个问题



在admin中设置了发布时间,但是获取到的pub_date还是None



解决啦解决啦,是我获取question的方式有问题



亲,我也遇到这个问题,return self.pub_date >= timezone.now() - datetime.timedelta(days=1)这行报错,我前面有赋值pub_date = models.DateTimeField('date published'),还是报错,请问你是怎么解决的?



非常棒的分享,感谢刘老师。讲解很详细,跟着博客学习收益颇多。django3.0.2是成功的。再次感谢!



点赞



刘老师,我有一个问题:choice_set是Django自动生成的么?我没看到自己在代码中有设置choice_set字段。如果是自动生成的是因为设置了“外键”的原因么?



是的。反向关联在类名后加下划线再加‘set’。往后继续学习有深入讲解。



刘老师,python 3.7.5和Django 2.2中, 我遇到一个问题:settings.py增加了如下: import pymysql # 一定要添加这两行!通过pip install pymysql! pymysql.install_as_MySQLdb() 然后执行python manage.py shell 后报错: File "D:\Python\Python37\lib\site-packages\django\db\backends\mysql\base.py", line 36, in <module> raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__) django.core.exceptions.ImproperlyConfigured: mysqlclient 1.3.13 or newer is required; you have 0.9.3. 注释了下列两行: #if version < (1, 3, 13): # raise ImproperlyConfigured('mysqlclient 1.3.13 or newer is required; you have %s.' % Database.__version__) 查了原因是:django2.2不支持pyMySQL,但2.2是LTS,但是MySQLDB又不支持python3



这个问题赖我。一会我就去把那段内容删了。 Django官方已经不提倡使用pymysql库了,而是建议使用mysqlclient库。



老师您好,我在创建管理员后首次访问 http://127.0.0.1:8000/admin/ 时显示登陆界面,点击 login 后,服务器立即自动断开,之后在pycharm内启动服务器并访问该页面,会提示 Process finished with exit code -1并断开服务器,使用cmd启动服务器后访问该页面,无任何错误提示,服务器断开,请问是什么原因呢?(python 3.7, django 3.0)



我也是這个问题,请问你解决了吗?



这是由于Django3.0和python3.7.0版本的问题造成的。我搞了一天,升级到3.8.1后解决。参考:https://stackoverflow.com/questions/59491303/django-webserver-automatically-shuts-off-on-local-server-when-i-try-to-access-th



用2.2吧,3.0我也会有这个问题



我的也是这个原因。。



这是由于Django3.0和python3.7.0版本的问题造成的。我搞了一天,升级到3.8.1后解决。参考:https://stackoverflow.com/questions/59491303/django-webserver-automatically-shuts-off-on-local-server-when-i-try-to-access-th



python 3.7 + django 3.0我用了很久了,从未碰到过这个问题。



这是由于Django3.0和python3.7.0版本的问题造成的。我搞了一天,升级到3.8.1后解决。参考:https://stackoverflow.com/questions/59491303/django-webserver-automatically-shuts-off-on-local-server-when-i-try-to-access-th



这确实是个bug。Django在https://code.djangoproject.com/ticket/31067有讨论。 准确的说是Django3.0.1 + Python3.7.0会有这个问题。 解决办法:别让这两个小版本号凑一起。 我目前使用的是Django3.0.0 + Python3.7.3,还没有出过问题。



博主你好,我用的是python3.7.0+django3.0.3,我也遇到这个问题了 请问怎么解决呢



我在pycharm->file->setting 里面卸载了django3.0.3,然后在terminal指定了2.2版本安装,问题解决了,不需要从头开始这个工程了



查看其它API时,运行 >>> Question.objects.all()出现报错: Traceback (most recent call last): File "<console>", line 1, in <module> File "D:\python\Python36\lib\site-packages\django\db\models\query.py", line 247, in __repr__ return '<%s %r>' % (self.__class__.__name__, data) File "D:\python\Python36\lib\site-packages\django\db\models\base.py", line 503, in __repr__ return '<%s: %s>' % (self.__class__.__name__, self) File "E:\python项目\mysite\polls\models.py", line 9, in __str__ return self.question_text AttributeError: 'Question' object has no attribute 'question_text' 重新配置了一次还是这样,什么原因呢?愁



在cmd运行Question.objects.all()这里报错:>>> Question.objects.all() Traceback (most recent call last): File "<console>", line 1, in <module> File "C:\Python27\lib\site-packages\django\db\models\query.py", line 226, in __repr__ data = list(self[:REPR_OUTPUT_SIZE + 1]) File "C:\Python27\lib\site-packages\django\db\models\query.py", line 250, in __iter__ self._fetch_all() File "C:\Python27\lib\site-packages\django\db\models\query.py", line 1121, in _fetch_all self._result_cache = list(self._iterable_class(self)) File "C:\Python27\lib\site-packages\django\db\models\query.py", line 53, in __iter__ results = compiler.execute_sql(chunked_fetch=self.chunked_fetch) File "C:\Python27\lib\site-packages\django\db\models\sql\compiler.py", line 899, in execute_sql raise original_exception OperationalError: no such table: polls_question 但是我检查了一下创建数据库的命令行输出,没有发现什么问题: BEGIN; -- -- Create model Choice -- CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL); -- -- Create model Question -- CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL); -- -- Add field question to choice -- ALTER TABLE "polls_choice" RENAME TO "polls_choice__old"; CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id")); INSERT INTO "polls_choice" ("choice_text", "votes", "id", "question_id") SELECT "choice_text", "votes", "id", NULL FROM "polls_choice__old"; DROP TABLE "polls_choice__old"; CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id"); COMMIT; 求博主帮忙!!!多谢。



自己找到原因, 是没有run python manage.py migrate



我在自定义一个模型方法后的那一个步骤上,在cmd上运行 Question.objects.all()代码后,教程的结果时<QuerySet [<Question: What's up?>]>这样,我的结果是<QuerySet [<Question: Question object (1)>]>这样,我一开始没有注意,一直到教程运行Question.objects.filter(question_text__startswith='What')代码后,我就报错了,django.core.exceptions.FieldError: Cannot resolve keyword 'question_text' into field. Choices are: id 这个是原因,我现在应该怎么办



这个教程开篇的例子,是一个相当基础全面又环环相扣的范例,非常适合新手学习。所以,在学习它的过程中建议不要加入自己的想法和内容,先一步步跟着做一遍,不要漏过每个细节,不要错过每一行代码和提示。从头开始吧,相信我,这不是浪费时间,更不是无用功。



q.choice_set.create(choice_text='The sky', votes=0) Traceback (most recent call last): File "<console>", line 1, in <module> File "C:\Program Files\Python37\lib\site-packages\django\db\models\fields\related_descriptors.py", line 668, in create return super(RelatedManager, self.db_manager(db)).create(**kwargs) File "C:\Program Files\Python37\lib\site-packages\django\db\models\manager.py", line 82, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "C:\Program Files\Python37\lib\site-packages\django\db\models\query.py", line 420, in create obj = self.model(**kwargs) File "C:\Program Files\Python37\lib\site-packages\django\db\models\base.py", line 501, in __init__ raise TypeError("%s() got an unexpected keyword argument '%s'" % (cls.__name__, kwarg)) TypeError: Choice() got an unexpected keyword argument 'votes' 哪错了呢,大大



检查一下你的Choice模型,看看votes这个字段是不是名字写错了或者别的什么问题。



66



没权限登录后台管理页面,superuser创建成功的



想问老师,怎么让其他用户也可以进入到后台管理编辑数据界面



给他创建用户,然后登录后台页面



直接告诉他用户名和密码



进入shell后 q = Question(question_text="What's new?", pub_date=timezone.now()) 报错 File "/home/suns/mysite/mysite/venv/lib/python3.6/site-packages/django/db/models/base.py", line 485, in __init__ raise TypeError("'%s' is an invalid keyword argument for this function" ) TypeError: 'question_text' is an invalid keyword argument for this function 报错图片地址 https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg?&MsgID=1344292000516333038&skey=%40crypt_e7e8bc16_a540d635966e361bbdce48db53d9e9d8



点击“Questions”,进入questions的修改列表页面后,没有见到“What’s up?界面 https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetmsgimg?&MsgID=9048854977713096836&skey=%40crypt_e7e8bc16_a540d635966e361bbdce48db53d9e9d8



# 看看我们自定义的方法用起来怎么样 >>> q = Question.objects.get(pk=1) >>> q.was_published_recently() True 执行>>> q.was_published_recently()的时候报错:raceback (most recent call last): File "<console>", line 1, in <module> AttributeError: 'Question' object has no attribute 'was_published_recently' 是导入的包不对么



import datetime from django.db import models from django.utils import timezone class Question(models.Model): # ... def was_published_recently(self): return self.pub_date >= timezone.now() - datetime.timedelta(days=1) 是你在定义前面那一段函数的时候出问题了吧,回去看看代码有没有错。



统一解答一下修改question和Choice及 <QuerySet [<Question: Question object>]>的问题: 1. 博主文章中:返回polls/models.py文件,修改一下question和Choice这两个类,代码如下中的 #... 意思是省略之前的代码,而第二次修改是在之前的基础上再加 def __str__(self): return self.question_text ,包括后面的用于判断问卷是否最近时间段内发布度也是在最早的代码基础上添加即可,在相应的class中,并非将之前的删除再写。 2.__str__ 前后是两个下划线 不是一个,如果写成一个则会不显示原来数据。



赞一个!



还有一个地方需要注意,写了__str__方法后需要退出重启python shell,,我就是踩了这个坑,



'Question' object has no attribute 'question_text' 后台点击question报这个错误



question_text没有定义 检查你的models 有没有定义,或者是你是不是将之前的代码删除了



注意: 在使用非SQLite的数据库时,请务必预先在数据库管理系统的提示符交互模式下创建数据库,你可以使用命令:“CREATE DATABASE database_name;”。Django不会自动帮你做这一步工作。 确保你在settings文件中提供的数据库用户具有创建数据库表的权限,因为在接下来的教程中,我们需要自动创建一个test数据表。(在实际项目中也需要确认这一条要求。) 如果你使用的是SQLite,那么你无需做任何预先配置,直接使用就可以了。



这个执行命令怎么报错了



添加polls到app列表的配置后,执行python manage.py makemigrations polls命令报错。如下: D:\study\project\mysite>python manage.py makemigrations polls Traceback (most recent call last): File "manage.py", line 15, in <module> execute_from_command_line(sys.argv) File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\management\__init__.py", line 381, in execute_from_command_line utility.execute() File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\site-packages\django\core\management\__init__.py", line 357, in execute django.setup() File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\site-packages\django\__init__.py", line 24, in setup apps.populate(settings.INSTALLED_APPS) File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\site-packages\django\apps\registry.py", line 89, in populate app_config = AppConfig.create(entry) File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\site-packages\django\apps\config.py", line 116, in create mod = import_module(mod_path) File "C:\Users\gtb\AppData\Local\Programs\Python\Python37\lib\importlib\__init__.py", line 127, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 1006, in _gcd_import File "<frozen importlib._bootstrap>", line 983, in _find_and_load File "<frozen importlib._bootstrap>", line 953, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "<frozen importlib._bootstrap>", line 1006, in _gcd_import File "<frozen importlib._bootstrap>", line 983, in _find_and_load File "<frozen importlib._bootstrap>", line 965, in _find_and_load_unlocked ModuleNotFoundError: No module named 'pollsdjango'



配置中少了一个一个逗号



我也遇到这个错误,哪里少了一个逗号



INSTALLED_APPS是一个列表,你输入polls.apps.PollsConfig这个元素之后忘记在这个元素后面加逗号了



由于Python3中不再支持`MySQL-python`和`mysqlclient`等模块,需要使用`pymysql`模块来连接mysql 在安装pymysql模块之后,执行 python manage.py migrate 报错。 此时需要在mysite/mysite/__init__.py中写入 import pymysql pymysql.install_as_MySQLdb() 这一段好像博主没有提到额



使用mongodb数据库的要配置管理后台改如何配



>>> c = q.choice_set.filter(choice_text__startswith='Just hacking') FieldError: Unsupported lookup 'startwith' for CharField or join on the field not permitted. ### ### 发现使用Choice.objects.filter可以 ### c = Choice.objects.filter(choice_text__startswith='Just hacking')



提示得很清楚了,单词写错了,少了个s。 ‘startswith'



报错提示为:1)ImportError at /admin/login/ 2)polls doesn't look like a module path 3)"GET /admin/login/?next=/admin/ HTTP/1.1" 500 106708



TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] 此'context_processors'里面不能添加:'polls'



“返回polls/models.py文件,修改一下question和Choice这两个类,代码如下:”下方的代码, # 在python2版本中使用的是__unique__ 是__unique__还是__unicode__?



比如将“Django administrator”改成“后台管理系统”



path('polls/', include('polls.urls')),跟着做下来到admin那,必须把这一行注释掉,admin页面才不报错



此教程基于1.11.7,最好和我的环境一样。当然2.05也是没有问题的,请提前参看 http://www.liujiangblog.com/course/django/182



我也遇到同样的问题,python版本是3.6.6,django版本是1.11.16,运行程序后在浏览器中输入http://127.0.0.1:8000报错Page not found,如下: Page not found (404) Request Method: GET Request URL: http://127.0.0.1:8000/ Using the URLconf defined in DjProDemo.urls, Django tried these URL patterns, in this order: ^polls ^my/set/ The empty path didn't match any of these. You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.



将出现 __init__() missing 1 required positional argument: 'on_delete'的行的参数里都加设置,on_delete=models.CASCADE



>>> q.choice_set.all() >>> q.choice_set.count() 刘老师好。为什么 choice 是小写而不是大写呢,是笔误吗,模型定义中并没有单独用小写choice的地方啊。我看前面的Question都是大写的。



choice_set指的是q所关联的choice对象集合,用的是Choice这个模型的名字的小写。



那就是说这也是固定语法的一种吗,如果模型名字是大写,需要变成小写,才能用这个函数?……前面的Question都是大写,也是函数,所以有点迷惑。



模型名首字母大写是Python的编码规范,实际上可以不大写。刚才那个模型名变成小写再加上set是Django的套路,你往后看就知道了。很多问题,其实可以先放一放,记到小本本上,随着学习的深入都能自然而然的解决,更有助于加深印象。



我是python2.7 按照要求导入了 pymysql也安装了 怎么报错这个 求指教 实在不知道怎么弄



pymysql没有安装到 你Django运行 所使用的Python环境下



这个是什么原因啊,哪位大神能告诉我下,整了好久。。。



使用 pip 安装一下mysql的pymysql插件



安装完成后记得在 __init__.py 文件导进去



https://i.loli.net/2018/04/13/5ad0587a5a8ab.png



按照教程的代码,在models中设置的 pub_date = models.DateTimeField('date published') 但是在damin后台就2个输入框,没有日期选择的图标。。



被教程感动 学习的路上



江哥 我连接Mysql DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mysite', 'HOST': '192.168.1.1', 'USER': 'root', 'PASSWORD': 'pwd', 'PORT': '3306', } } 用的也是这个代码 但是显示连接超时了.... 是什么情况...



不能死板的套用上面的信息,Host 要用你自己的主机地址,默认是 127.0.0.1或者'localhost'都可以,Name 是你自己的数据库名字,前提 首先有这个数据库。 User 和 password 就不解释了。port 3306 固定的



请问修改question和Choice这两个类的代码和用于判断问卷是否最近时间段内发布度的代码是在同一个文件里吗?? 命令行始终是 <QuerySet [<Question: Question object>]>



已经解决了



遇到了同样的问题,敢问朋友是如何解决的?



博主你好: 为什么改了model.py ,改成了str方法,在命令行里打印出来还是 <QuerySet [<Question: Question object>]>



在python2版本中使用的是__unique__



用的就是python3啊



跟你一个问题,解决了么,谢谢



求教解决方法



__str__ 前后是两个下划线 不是一个



在同一个项目下创建不同的APP,在每个APP中都写入的不同的数据库模型,但后台为什么看不到呢,这是为什么?



好像是没有注册!!



当我启动shell的时候,显示如下: Python 3.6.2 (v3.6.2:5fd33b5, Jul 8 2017, 04:57:36) [MSC v.1900 64 bit (AMD64)] on win32 >>> Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) 无法进行任何操作?请问这是为什么?网上没有找到答案。。。谢谢



admin里面的操作相当于是在数据库?



admin本质上就是个图形化的数据库访问接口



我们需要再运行下一个命令: $ python manage.py makemigrations polls 之后我的pycharm给的是一个warning WARNINGS: ?: (1_8.W001) The standalone TEMPLATE_* settings were deprecated in Django 1.8 and the TEMPLATES dictionary takes precedence. You must put the values of the following settings into your default TEMPLATES dict: TEMPLATE_DIRS. App 'polls' could not be found. Is it in INSTALLED_APPS? 是哪里出错了吗?



将‘polls’添加到INSTALLED_APPS里面。



通过运行makemigrations命令出现错误SyntaxError: invalid syntax。请问是什么情况?



一切皆有可能....



新手,按照你的步骤没有看到question,是刷新的方式不对吗?



问题都处在细节上...



嗯,解决了,谢谢



在polls/admin里面加上如下代码 from .models import Question admin.site.register(Question)



admin的用户名密码忘了怎么办 才几天没登陆



python manage.py createsuperuser