Django Model View Template 之间的简单交互 (二)
前言
接续前文,上一篇文章主要涉及了 Django 项目的基础配置等,这篇主要涉及数据库相关的 ORM ,也就是 Django 中的 Model 的使用,MVT 三层之间的交互
教程基本都是东拼西凑的,防止有些东西表述不准确,因为我之前写 JavaScript 比较多。但是里边注入了自己的理解,尽量讲清楚。
基础环境
- Pycharm 2018
- Django 2.0.3
- Python 3.6.4
- mxonline start 分支
Django Model 配置
代替使用原生的 SQL 语句操作数据库。
原生 SQL 语句操作数据库
# {BASE_DIR}/apps/message/models.py import MySQLdb def book_list(request): db = MySQLdb.connect(user="me", db="mydb", password="secret", host="localhost") cursor = db.cursor() cursor.execute('SELECT name FORM books ORDER BY name') names = [row[0] for row in cursor.fetchall()] db.close()
配置 Django Model
具体的一些细节知识下面会进行叙述。这里只是展示一下如何配置。
# {BASE_DIR}/apps/message/models.py # 从 Django 中引入 models from django.db import models # Create your models here. class UserMessage(models.Model): name = models.CharField(max_length=20, verbose_name=u"用户名") email = models.EmailField(verbose_name=u"邮箱") address = models.CharField(max_length=100, verbose_name=u"联系地址") message = models.CharField(max_length=500, verbose_name=u"留言信息") class Meta: verbose_name = u"用户留言信息" verbose_name_plural = verbose_name
model
创建完成,接下来是生成数据表。
生成数据表
$ python manage.py makemigrations message $ python manage.py migrate message
然后查看数据库是不是生成了表。
之前有十个表,分别是:
+----------------------------+ | Tables_in_test_django | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 10 rows in set (0.00 sec)
现在:
+----------------------------+ | Tables_in_test_django | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | django_admin_log | | django_content_type | | django_migrations | | django_session | | message_usermessage | +----------------------------+ 11 rows in set (0.00 sec)
mysql> desc message_usermessage; +---------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+--------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | name | varchar(20) | NO | | NULL | | | email | varchar(254) | NO | | NULL | | | address | varchar(100) | NO | | NULL | | | message | varchar(500) | NO | | NULL | | +---------+--------------+------+-----+---------+----------------+ 5 rows in set (0.04 sec)
对应上面 Model 中的字段,完美~
注意事项
- ChartField 必须指定 max_length。
ORM 功能介绍
稍候会有完整的 Model 例子。
字段 Field
每一个模型都可以包含有任意数量的字段,每个字段都会对应数据表中的一个字段,我们需要指定字段的属性。
name = models.CharField(max_length=20, verbose_name=u"用户名")
上述字段的名称是 name
,类型是 models.CharField
。对应到 MySQL 数据中是 varchar
类型。varchar
类型的字段都是需要去指定一个长度值,对应到 Django 的 ORM 模型上就是 max_length
属性。
字段参数
下面列举一下目前笔者在开发中用到的一些字段:
- max_length:指定字段的长度值,接受一个数字,
CharField
必须指定最大长度,TextField
不需要。 - verbose_name:字段标签的可读名称,接受一个字符串。如果不指定,Django 会从字段名称去推断默认的详细名称,建议每一个字段都进行指定。
- default:字段默认值。
- null:是否可以为
null
,接受True
或者False
。 - blank: 是否可以为空,同样接受
True
或者False
。 - primary_key:如果设置为
Ture
,则该字段置为模型主键,如果模型中没有指定主键,则 Django 会自动为模型添加一个主键,默认为id
。 - help_text:为 HTML 表单文本提供单文本标签。
- choices:一组字段选项,提供这一项的时候,默认对应的表单不见是选择字段的盒子,而不是标准文本字段。
字段类型
- CharField:用来定义短到中等长度的字段字符串,必须指定 max_length 属性。
- TextField:用于大型的任意长度字符串,不强制要求指定
max_length
属性,指定的max_length
仅仅当该字段以表单显示才会使用,不会再数据库级别进行强制执行。 - IntegerField:用于存储整形数据,在用于表单中验证输入的值需要时整数。
- FloatField:用于存储浮点型数据
- DateField 和 DateTimeField:用于存储/表示日期和日期/时间信息(分别是Python.datetime.date和datetime.datetime对象。这些字段可以另外表明(互斥)参数auto_now=Ture (在每次保存模型时将该字段设置为当前日期),auto_now_add(仅设置模型首次创建时的日期)和default(设置默认日期,可以被用户覆盖)。一般笔者选择的默认日期是
datetime.now
。 - EmailField:用来存储和验证电子邮件地址。
- FileField:用于上传文件,需要提供
upload_to
指定上传到的地方。 - ImageField:和上传文件表现基本相似,会额外进行是否为图像的验证。
- AutoField:是一种
IntegerField
自增的特殊类型,如果模型没有指定主键的话,此类型的主键将自动添加到模型中。 - ForeignKey:外键,用于指定与另一个数据库模型的一对多关系。关系 “一” 侧是包含密钥的模型。和 flask 指定外键的方式不同。
- ManyToManyField:用于指定多对多关系,例如,一本书可以有几种类型,每种类型可以包含几本书)。在我们的图书馆应用程序中,我们将非常类似地使用它们ForeignKeys,但是可以用更复杂的方式来描述组之间的关系。这些具有参数on_delete来定义关联记录被删除时会发生什么(例如,值models.SET_NULL将简单地设置为值NULL)。笔者用的不是很多。
元数据
通过声明 class Meta
来声明模型级别的元数据
class UserMessage(models.Model): # Config Field class Meta: ordering = ["id"] verbose_name = u"用户留言信息" verbose_name_plural = verbose_name
这里最有用的功能是可以指定模型返回数据时候的默认的顺序,更多的文档可以查看这里
方法
一个模型也可以有方法,最基本的使用就是定义一个标准的 Python 类方法: __str__
:
class UserMessage(models.Model): # Config Field # Config Meta def __str__(self): return self.message
这样为每个对象返回一个人类可读的字符串。当然还有其他高级的使用,日后再说
完整的 model
# {BASE_DIR/apps/message/models.py} from django.db import models # Create your models here. class UserMessage(models.Model): name = models.CharField(max_length=20, verbose_name=u"用户名") email = models.EmailField(verbose_name=u"邮箱") address = models.CharField(max_length=100, verbose_name=u"联系地址") message = models.CharField(max_length=500, verbose_name=u"留言信息") class Meta: ordering = ["id"] verbose_name = u"用户留言信息" verbose_name_plural = verbose_name def __str__(self): return self.message
使用 Django ORM
之前已经定义好了数据模型的字段(Field)、元数据(Meta)、方法(Method)等。现在要做的是把页面上提交过来的数据通过 ORM 来存放到数据库中,通过 ORM 来进行数据的 CURD 操作。
创建数据
在 templates
中已经创建了 message_form.html
模板文件,现在进行修改,修改 form
的 action
目标地址:
<form action="/" method="post" class="smart-green">
这里根据自己配置的 Url
来自行决定,由于笔者配置的是 /
,所以这里就配置成这个样子。
这里指定的是使用 form
的原生事件 post
事件进行提交,但是在实际的开发中,为了实现更精确的控制,我们常常不会使用原生事件,而更倾向于使用 ajax
进行提交,当然这里的重点不是前端的逻辑,重点在于 Django 后端逻辑的处理,顾不赘述。
接下来的任务就是:拿到 POST 发来的数据,然后存入数据库中。
存储数据
之前使用 Django 的 ORM 进行了数据库中数据表的配置,现在使用 Django 的 ORM 将数据保存到数据库中。
在 Django 中,我们使用不是传统的 MVC
架构,我们使用的是一种叫 MVT
的方式。不同的 Template(模板)
呈现不同的 View
。我们将在 View(请求视层)
中获取用户提交的数据,以及将从 Model(数据层)
中获得的数据传递给 Template(模板层)
。
MVT 的概念本身就来自于 Django 框架,下面进行代码的展示:
# {BASE_DIR/apps/message/views.py} from django.shortcuts import render # 引入 render 方法 from .models import UserMessage # 引入之前配置好的 Model # Create your views here. def get_form(request): if request.method == 'POST': name = request.POST.get('name', '') message = request.POST.get('name', '') address = request.POST.get('address', '') email = request.POST.get('email', '') user_message = UserMessage() user_message.name = name user_message.message = message user_message.address = address user_message.email = email user_message.save() return render(request, 'message_form.html')
通过 post
方法提交的数据会存储到 request
对象的 post
属性下边,通过 Django
提供的 get
方法就可以取到对应的字段。其中 get
接收两个参数,分别是字段的名称和默认值。
在取到 Template
提交过来的每一个字段之后,就可以使用 ORM 提供的方法将其存入数据库中。
实例化引入的 Model,然后将之前定义的字段进行赋值,然后就可以调用实例的 save()
方法将数据存入数据库。
然后就可以通过 Navicat
或者终端等方式查看数据是否保存到了数据库中。
读取数据
之前已经实现了数据的存储,这部分将实现数据的读取功能。
# {BASE_DIR/apps/message/views.py} from django.shortcuts import render from .models import UserMessage # Create your views here. def get_form(request): message = None all_message = UserMessage.objects.filter(name='test') if all_message: message = all_message[0] return render(request, 'message_form.html', {'my_message': message})
这里会涉及到 Django 的 QuerySets(查询集)
相关知识,这里捡着用的着的部分看一下。
首先先声明 message
,值为 None
,用于存储取到的数据。
从本质上讲,QuerySets
是给定对象模型(这里是 UserModel
)的对象列表(list),允许我们从数据库中读取数据,选择以及排序。通过这种方式操作的话,就可以避免直接操作数据库。从而抹平不同数据库操作的差异,这部分由 Django 帮我们来完成。
上面的代码中有这样的一句:
UserMessage.objects.filter(name='test')
作用是从数据库中查找 name
值为 test
的所有条目,返回的是一个 <QuerySet>
列表,并赋值给 all_message
。同时我们也可以发现,QuerySet
可以链式调用。类似于 JavaScript
中的 Promise
。
然后如果 all_message
不为空的话,取出列表第一项,然后传递给 my_message
模板。
关于 QuerySet
的详细知识,可以查看 Django 的官方文档的这一部分
渲染到模板
在上面步骤中,我们将符合预设条件的数据从数据库中取出来,传递到模板中,这里的目标是将数据正确的显示与渲染。部分语法类似于 ejs
模板的语法,但同时 Django 又在模板中内置了很多常用的函数。但是 Django 不像 Java 那样,允许在模板中写一些 Java 代码,Django 的 Template
中不允许将 Python
代码混进来。
由于模板代码过长,这里只放一些关键部分的代码,完整的代码可以查看文章对应的代码仓库。
<form action="{% url 'go_form' %}" method="post" class="smart-green"> <label> <span>姓名 :</span> <input id="name" type="text" name="name" value="{% if my_message.name == 'test' %}test{% endif %}" class="error" placeholder="请输入您的姓名"/> </label> <label> <span>留言 :</span> <textarea id="message" name="message" placeholder="请输入你的建议">{{ my_message.message }}</textarea> </label> {% csrf_token %} </form>
在上一篇文章中,提到过,path
接收 name
参数。在 Template
中可以通过 name
来取到对应的 Url
,方法如下:
action="{% url 'go_form' %}"
这样做提供了另一种获取 Url
的方式,当我们因为某种原因去修改了 Url
地址之后,通过 name
还能找到它。
在 textarea
中,有这样一段代码:
{{ my_message.message }}
作用是取到传入的 my_message
对象的 message
属性取出来并显示,由于 html 基本属于前端部分了,所以用前端的方式进行描述。
双花括号(八字胡)语法: {{...}}
在任何模板语言中都很常见,作用是将数据渲染到双括号内部。
上面还有一部分代码是这样子的:
{% if my_message.name == 'test' %}test{% endif %}"
意思很好懂的,是吧。
具体的 Django 中模板的语法可以查看官方文档。
后记
这里只是简单的介绍了一下 Django 中 Model 层、View 层、以及 Template 层之间交互的部分知识,很简略,不详细。在每部分的后边都附加了详细的官方文档地址。如果以后有时间了可以对每部分进行详细的阐述。