YOLO813

Django初学(2)- ORM初了解

  •     Django的ORM初步了解

    在介绍ORM之前,先介绍一下Django操作Mysql数据库的常用方法,因为我只是对Mysql数据库比较了解,所以其它的Oracle等数据库不做介绍,在Django的settings.py中配置Mysql相关,具体配置可在下方参考的官方文档查看

    Django底层还是使用python操作数据库,我们需要知道python怎么操作Mysql即可,python操作Mysql驱动主要有4种:

    MySQL-python(MySQLdb),只支持python2,C语言封装

    mysqlclient,MySQL-python的分支,支持python3

    pymysql,我经常用的驱动,纯python,效率肯定比C低,但是和python无缝衔接

    MySQL Connector/Python, MySQL官方推出的,纯python书写

    推荐使用mysqlclient,在django中,我们操作数据库,可以使用官方封装好的python db api的接口,在文件中,从django.db中导出connection对象,即可通过connection.cursor()获取游标,这种方式和我们在普通项目中操作数据库的形式差不多,只不过不用再配置数据库相关信息,因为在python db api中会动态的获取我们settings.py中的Mysql相关配置,但是用原生的sql语句会存在一些问题,首先是迁移效率问题,因为我们的sql都是一句句固定的,那么可能当你想要迁移项目至非关系型数据库或者是修改了某个字段,很多地方都需要去重构;第二个是安全问题,例如我们在views视图层写了一个sql语句去接收前端页面传过来的GET请求,展示个人用户信息

user_id = req.GET.get('user_id ')
cursor.execute("SELECT id,username from members WHERE id = %s" %user_id)
rows = cursor.fetchall()

    这种动态拼接字符串的方式是很常用的,但是前端页面用户如果修改了访问路径,如下:

?user_id=1 or 1=1

    那么所有的用户信息都会被展示,也就是所谓的sql注入(当然这是比较简单的例子)。解决方法就是绝对不要采用动态拼接sql语句的方法来进行数据查询,如果确实需要使用原生sql语句,采用参数化形式的方法,例如:cursor.execute(sql, (user_id,)),其始终把后面的传入参数当做一个条件,并不会拼接到你的sql语句。或者使用Django封装好的ORM。

    ORM(Object Relational Mapping),对象关系映射,Django封装好的一个用于操作数据库的API,我们可以通过操作类的方式去操作数据库(类=>表,模型中的属性=>字段,模型创建的对象=>对应数据库里面的一条数据),基本不用担心数据库迁移性和SQL注入等安全问题,当然由于ORM转换成底层sql语句会有一些开销,对性能肯定是有影响的,但是据说实测不超过5%。ORM模型建立例子如下:

from django.db import models
# Create your models here.
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    category = models.ForeignKey("Category",on_delete=models.CASCADE, null= True, related_query_name='articlename',related_name='test_name')
    create_time = models.DateTimeField(auto_now_add=True, null= True)
    class Meta:
        # 定义表名
        db_table = "modifiedArticle_table"
    def __str__(self):
        return "QuerySet<%s, %s, %s, %s>"%(self.pk, self.title, self.content, self.create_time)

class Category(models.Model):
    name = models.CharField(max_length=100)
    class Meta:
        # 定义表名
        db_table = "modifiedCategory_table"
    def __str__(self):
        return "QuerySet<%s>"%(self.name)

    这里面有几个要点,第一,我在学习模型的时候由于经常开倍速看,结果看到表单验证的时候一下子就懵逼了,因为表单验证的时候字段类型是差不多的(例如:CharField(最大支持254个字符)),你可以通过max_length和min_length验证表单,但是在模型对象中,我们建立Article类时,其实是在建表,所以一个max_length就够了,就相当于设置字段的长度一样;第二,ForeignKey我也是第一次接触,大致就是一个关联表,其中第一个参数就是关联的模型,例如上面的"Category",其实也可以使用Category,但是不推荐,因为如果是这种写法,你必须确保Category这个模型存在于Article(上文)的上方,例如上面例子中这种写法,就会报错;第三,默认表的名称为"app名字的小写_模型名字的小写",除非指定Meta属性中的db_table 。

    其中DateField比较麻烦,首先我们需要知道在python中的时间是可以分为两种,一种是naive(不带时区标志的,例如我们经常使用的datetime模块里面的datetime.now()),一种是aware(知道自己的时区);pytz库可以专门来处理时区;astimezone方法专门将一个时区的时间转换为另外一个时区,但是它仅能处理aware time(需要注意的是,在windows系统中做了兼容处理,其也能处理naive time);replace方法可以将一个时间的属性进行更改,例如我们可以将一个naive time转变为aware time,now = now.replace(tzinfo = pytz.timezone('Asia/Shanghai')),在django的settings.py中,如果我们将USE_TZ = True设置为False,那么django获取到的时间将会是一个naive time,naive time是无法转化为另外一个时区的,当然如果仅仅只做一个非国际化的网站,我们是可以这么操作的,非常省事,不用配置这个选项。

    但如果想要做一个国际化网站,最好还是将这个配置打开,当我们在模型中配置了DateField字段,设置其默认值为timezone.now,当插入一条数据时可以在数据库里面看到其默认值为UTC时间,与中国时间(UTC+8)相差8个小时,但是可以利用django.utils.timezone的localtime将其转化成当地时间,这个当地时间取决于你settings.py中的TIME_ZONE配置,但是如果是在前端页面中渲染,django中其实已经帮你配置好相关时间过滤器

# index.html
<body>
    {{mytimes}}
</body>
# views.py
context = {
    'mytimes':art.create_time
}
>>>Dec. 5, 2020, 8:57 p.m.  # 这个时间正是该条数据插入数据库的中国时间

 

 

参考:

https://docs.djangoproject.com/zh-hans/3.0/intro/