提供有关 Web 应用程序的建模和设计的一些很好的见解。 Web 开发和一般的软件开发不仅仅是编码。
实体是将要创建的模型,它与 Django 应用程序将处理的数据密切相关。考虑到城市景点
的项目案例,至少需要实现以下模型:City、Spot和 User。
思考模型如何相互关联很重要。实线告诉我们的是,在一个城市中,需要一个字段来识别它。同样,景点需要一个字段来表示它属于哪个城市,以便我们可以列出在特定城市中创建的景点。最后,我们将需要城市中的字段来知道谁创建了城市和景点中的字段,以便我们可以识别用户操作。
还可以与 City、Spot和 User 模型建立关联,这样就可以确定谁创建了给定的 City与Spot。
现在有了基本的类表示,必须考虑每个模型将携带什么样的信息。开始开发所需的信息。稍后,我们可以使用迁移改进模型。
花时间思考模型如何相互关联也很重要。 实线中,在一个City
中,需要字段来识别它。 类似地,Spot
需要字段来识别,这将是我们模型字段的基本表示:
这个类图强调模型之间的关系。 这些关系线条最终将转换为字段。
对于 City
模型,将从两个字段开始:名称和描述。名称字段必须是唯一的,以避免重复的城市名。该描述只是为了进一步说明其信息内容。
Spot
模型也由两个字段组成,景点将用于定义一个名为city
的字段,用于定义特定景点属于哪个城市。
最后是User
模型。在类图中,只提到了用户名、密码、电子邮件和超级用户标志字段,因为这几乎就是将要使用的全部内容。需要注意的是,这里不需要创建 User 模型,因为 Django 在 contrib
包中已经内置了 User 模型。我们将要使用它。
关于类图中的多重性(数字 1、0..* 等),可以这样解读:
类图中的城市
和景点
关联,一个景点必须与一个城市相关联(这意味着它不能为空),并且一个城市可以与许多景点相关联(1..*)
。
一个城市必须有一个且只有一个用户关联: 一个用户可以创建很多城市(*)
。同样,一个景点必须有一个用户关联:由用户创建。 一个用户可能创建多个景点(*)
。 Cityt和 User 之间的关联以及Spot和 User 之间的关联是直接关联,这意味着针对 User 编辑给定 Spot 或者City的关系,它将是更新的字段。 多重性表示 (1)
,这意味着字段最多可能仅与一个用户相关联。
模型基本上代表了应用程序的数据库布局。 现在要做的是创建前面类图
建模的类表示:City、Spot。 User 模型已经在名为 auth
的内置应用程序中定义,该应用程序在命名空间 django.contrib.auth
下的 INSTALLED_APPS
配置中列出。同时需要在MIDDLEWARE
中列出相关的中间件:
settings.configure(
...
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'widget_tweaks',
'cityspot',
],
MIDDLEWARE=[
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
],
...
)
在 cityspot/models.py
文件中完成所有工作。 下面为类图设计程序代码 :
cityspot/models.py
from django.db import models
from django.contrib.auth.models import User
class City(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True)
created_by = models.ForeignKey(User, related_name='citys',on_delete=models.CASCADE)
updated_by = models.ForeignKey(User, null=True, related_name='+',on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
def to_json(self):
json_city = {
'id': self.id,
'name': self.name,
'description': self.description
}
return json_city
class Spot(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=100)
city = models.ForeignKey(City, related_name='spots',on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(null=True)
created_by = models.ForeignKey(User, related_name='spots',on_delete=models.CASCADE)
updated_by = models.ForeignKey(User, null=True, related_name='+',on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
def to_json(self):
json_spot = {
'id': self.id,
'name': self.name,
'description': self.description,
'city': self.city.to_json()
}
return json_spot
所有模型都是 django.db.models.Model
类的子类。 每个类都将转换为数据库表。 每个字段由 django.db.models.Field 子类(内置 Django 核心)的实例表示,并将被转换为数据库字段。
CharField
、DateTimeField
等字段都是 django.db.models.Field
的子类,它们包含在 Django 核心中随时准备可用。
这里只使用 CharField
、DateTimeField
和 ForeignKey
字段来定义我们的模型。 但是 Django 提供了广泛的选项来表示不同类型的数据, 将根据需要引用它们。
某些字段具有必需的参数,例如 CharField
。 应该始终设置一个 max_length
。 此信息将用于创建数据库列
。 Django 需要知道数据库列需要多大。 Django Forms API
也将使用 max_length
参数来验证用户输入。
在 City
模型定义中,更具体地说,在 name
字段中,还设置了参数 unique=True
,顾名思义,它将在数据库级别强制该字段的唯一性。
在 City
和Spot
模型中, created_at
字段有一个可选参数, auto_now_add
设置为 True
。 这将指示 Django 在创建 City
或Spot
对象时设置当前日期和时间。
在模型之间创建关系的一种方法是使用 ForeignKey
字段。 它将在模型之间创建链接并在数据库级别创建适当主-外键
的关系。ForeignKey
字段需要一个位置参数,其中包含对其相关模型的引用。
例如,在 Spot
模型中,city
字段是 City
模型的 ForeignKey
。 它告诉 Django 一个 Spot
实例只与一个 City
实例相关。 related_name 参数将用于创建反向关系,其中 City
实例将有权访问属于它的 Spot
实例列表。
Django 自动创建这种反向关系——related_name
是可选的。 但是如果我们不为它设置名称,Django 将使用名称生成它:(class_name)_set
。 例如,在 City 模型中,Spot 实例将在 spot_set 属性下可用,作为代替,我们只是将其重命名为spots
,以使其感觉更自然。
在 Spot
模型中,updated_by
字段设置了related_name='+'
。 这告诉 Django 我们不需要这种反向关系,所以会忽略它。
在以上的模型设计中,我们并没有为模型指定主键,Django 会自动为我们生成它。
下一步是告诉 Django 创建数据库表,方便我们使用它。
打开并激活虚拟环境,进入城市景点
项目案例所在的文件夹,运行以下命令:
python scenic_spot.py makemigrations
作为输出,将得到如下内容:
Migrations for 'cityspot':
cityspot/migrations/0001_initial.py
- Create model City
- Create model Spot
此时,Django 在 cityspot/migrations
目录中创建了一个名为 0001_initial.py
的文件。 它代表我们应用程序模型的当前状态。 在下一步中,Django 将使用这个文件来创建表和列。
迁移文件被翻译成 SQL
语句,所有工作都将使用 Django ORM
完成,这是一个与数据库通信的抽象层。现在下一步是将生成的迁移应用到数据库:
python scenic_spot.py migrate
输出应该是下列这样的内容:
Operations to perform:
Apply all migrations: admin, auth, cityspot, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying cityspot.0001_initial... OK
Applying sessions.0001_initial... OK
如果这是第一次迁移数据库,所以 migrate
命令还应用了来自 Django contrib
应用程序的现有迁移文件,在 INSTALLED_APPS
中列出。 这是意料之中的。
Applying cityspot.0001_initial... OK
这行是我们在前一步中生成的迁移。现在,数据库已经可以使用了。在配置中,数据库是sqlite3:
settings.configure(
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'db.sqlite3',
}
},
...
)
在当前的项目路径scenic_spot
下,使用sqlite3
命令操作数据库:
sqlite3 db.sqlite3
执行命令后,进入了sqlite3
客户端的交互控制台(Console
):
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>
使用.tables
查看数据库表,显示了迁移执行后生成的数据库表:
sqlite> .tables
auth_group cityspot_city
auth_group_permissions cityspot_spot
auth_permission django_admin_log
auth_user django_content_type
auth_user_groups django_migrations
auth_user_user_permissions django_session
Django Admin
当我们开始时,已经在配置了的 INSTALLED_APPS
中列出了相关 Django Admin
的内置应用'django.contrib.admin'
。
Django Admin 是一个强大的工具,但我们需要知道何时使用它,其旨在用作内容驱动网站的后台应用程序,而不打算由网站的访问者使用。现在,我们将配置 Django Admin
来维护我们应用程序。
让我们从创建管理员帐户开始:
python scenic_spot.py createsuperuser
按照提示说明进行操作:
Username (leave blank to use 'zhaocj'): admin
Email address: 3261524748@qq.com
Password:
Password (again):
Superuser created successfully.
现在,启动服务器:
python scenic_spot.py runserver
观察服务器启动后的信息:
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
May 08, 2022 - 07:05:42
Django version 4.0.3, using settings None
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
然后在项目根urls
中增加admin
的映射:
from django.urls import path, include
from django.contrib import admin
urlpatterns = [
path('admin/', admin.site.urls),
path('cityspot/',include(('cityspot.urls','cityspot'),namespace='cityspot')),
]
接着在网络浏览器中打开 URL:http://localhost:8000/admin/
在登录页面,输入用户名和密码,点击[LOGIN]
按钮,成功登录后进入管理页面:
它已经具有了一些功能。 在这里我们可以添加用户和组来管理权限。点击Users
进入到用户信息管理的界面,可以看到已创建的管理员用户信息。
这里使用管理员登录账号admin
,以创建City
模型的实例为例,需要对视图控制类CityView
进行一个微调:
class CityView(View):
def get(self, request):
form = CityForm()
return render(request, 'new_city.html', {'form': form })
def post(self, request):
form = CityForm(request.POST)
if form.is_valid():
city = form.save(commit=False)
city.created_by = request.user # <--- 这里添加已登录用户的设置
city.save()
return redirect('cityspot:home')
else:
return render(request, 'new_city.html', {'form': form })
然后,启动Web服务器:
$ python scenic_spot.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
June 08, 2022 - 00:13:13
Django version 4.0.3, using settings None
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
在网络浏览器中打开 URL:http://localhost:8000/admin/
, 进行管理员账号的登录。接下来新启浏览器窗口,并在地址栏打开URL: http://localhost:8000/cityspot/addcity
,添加新的城市实例:
在发布
一个新增城市的实例后,在当前的项目scenic_spot
的目录下,使用sqlite3
的管理命令查看数据库情况:
$ sqlite3 db.sqlite3
SQLite version 3.19.3 2017-06-27 16:48:08
Enter ".help" for usage hints.
sqlite>
查看数据库表:
sqlite>.tables
auth_group cityspot_city
auth_group_permissions cityspot_spot
auth_permission django_admin_log
auth_user django_content_type
auth_user_groups django_migrations
auth_user_user_permissions django_session
查看auth_user
表:
sqlite> select * from auth_user;
1|pbkdf2_sha256$320000$4noFZjeq5LuqAEXiHHjFJ9$TMp7xXOgCA/GDUDZdQ/tUJSdyIErpOS45oZNc4cbYJQ=|2022-05-08
08:40:18.137345|1|admin||3261524748@qq.com|1|1|2022-05-08 06:56:05.542979|
查看cityspot_city
表:
sqlite> select * from cityspot_city;
1|广州|五羊城|2022-05-08 08:40:57.420875||1|
博文最后更新时间: