django初学教程 投票应用 02 使用数据库
数据库配置
默认使用内置SQLite的单数据库配置:
# mysite/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'mydatabase',
    }
}
连接到其他数据库时,需要额外参数,如PostgreSQL:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydatabase',
        'USER': 'mydatabaseuser',
        'PASSWORD': 'mypassword',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}
参考 https://docs.djangoproject.com/zh-hans/3.2/ref/settings/#std:setting-DATABASES
INSTALLED_APPS 默认包括了以下 Django 的自带应用:
- django.contrib.admin-- 管理员站点。
- django.contrib.auth-- 认证授权系统。
- django.contrib.contenttypes-- 内容类型框架。
- django.contrib.sessions-- 会话框架。
- django.contrib.messages-- 消息框架。
- django.contrib.staticfiles-- 管理静态文件的框架。
运行migrate命令,为INSTALLED_APPS中的每个应用创建数据表,进行数据库迁移:
python manage.py migrate

创建模型
投票应用需要Question问题和Choice选项两个模型,问题模型包括问题描述和发布时间,选项模型包括选项描述和当前得票数。每个选项属于一个问题。
用python类来描述:
# polls/models.py
from django.db import models
class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('data 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)
CharField:字符字段
DateTimeField:日期时间字段
IntegerField:整数字段
激活模型
先将poll应用安装到项目中:在installed_apps中添加polls.app.PollsConfig:
INSTALLED_APPS = [
    'polls.apps.PollsConfig', # 添加
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
运行makemigrations命令让django检测我们对模型文件的修改,并把修改的部分存储为一次迁移,存储在polls/migrations/0001_initial.py中:
python manage.py makemigrations polls

sqlmigrate命令接收一个迁移的名称,返回对应的SQL(用于查看,并未执行迁移):
python manage.py sqlmigrate polls 0001
输出(不同数据库输出也不一样,以下为默认SQLite):

运行migrate命令,应用数据库迁移:
python manage.py migrate

改变模型的三个步骤:
- 编辑 models.py文件,改变模型。
- 运行 python manage.py makemigrations为模型的改变生成迁移文件。
- 运行 python manage.py migrate来应用数据库迁移。
初试API
通过shell打开交互式python命令行:
python manage.py shell
注意: manage.py 会设置 DJANGO_SETTINGS_MODULE 环境变量,这个变量会让 Django 根据 mysite/settings.py 文件来设置 Python 包的导入路径,所以不要简单使用python命令进入命令行。
命令行:
>>> from polls.models import Choice, Question  # Import the model classes we just wrote.
# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> q.save()
# Now it has an ID.
>>> q.id
1
# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
给模型增加__str__()方法
# 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
给模型添加一个自定义方法
# 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)
继续命令行,处理Choice:
>>> from polls.models import Choice, Question
# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>
# Create three 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 objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>
# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
介绍Django管理页面
创建一个管理员账号
python manage.py createsuperuser
密码过于简单会有提示:

启动开发服务器
python manage.py runserver
打开浏览器,网址http://127.0.0.1:8000/admin/,出现管理员登陆界面(LANGUAGE_CODE可以设置界面语言):

进入管理站点页面
使用创建的超级用户登录,可以看到Django管理页面的索引页:

组和用户是django.contrib.auth (django开发的认证框架)提供的。
向管理页面中加入投票应用
告诉管理问题Question对象需要一个后台接口,编辑polls/admin.py文件:
# polls/admin.py
from django.contrib import admin
from .models import Question
admin.site.register(Question)
体验便捷的管理功能
刷新页面,可以看到Question已经显示:

点击"Questions"→"What's up?"可以编辑问题对象:

页面的底部提供了几个选项:
- 保存(Save) - 保存改变,然后返回对象列表。
- 保存并继续编辑(Save and continue editing) - 保存改变,然后重新载入当前对象的修改界面。
- 保存并新增(Save and add another) - 保存改变,然后添加一个新的空对象并载入修改界面。
- 删除(Delete) - 显示一个确认删除页面。
这个Note提示时差问题,因为服务器默认TIME_ZONE设置时区是UTC,没有更改,时区列表在pytz的init文件中可以看到,改为"Asia/Shanghai"后刷新页面,时间正常:

通过点击now更改时间后,点击右上角history界面可以看到页面中列出了时间戳和进行修改操作的用户名:

这个时间46分过于巧合,又修改了一次,

下一部分学习为投票应用添加更多视图。
 
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号