Django(数据库操作)

Posted MISF

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django(数据库操作)相关的知识,希望对你有一定的参考价值。

一.settings配置mysql

一.修改settings.py

DATABASES = {
    \'default\': {
        \'ENGINE\': \'django.db.backends.mysql\',
        \'NAME\': "userinfo",             #mysql数据库名
        \'USER\': \'root\',                    #mysql登陆名
        \'PASSWORD\': \'root\',           #密码
        \'HOST\': \'localhost\',             #主机
        \'PORT\': \'3306\', 
    }
}
#settings配置文件中将USE_TZ的值改为False是为了将utc时间改为当地时间

二.命令行语句

python manage.py makemigrations  # 记录models.py中的变更信息,将其记录在所在app下的migrations文件夹中

python manage.py migrate #数据库迁移,将变更记录添加到数据库中

三.使用pymysql模块连接mysql数据库

因mysqldb不支持python3.4及其之下的版本。在与settings同级目录下的`__init__.py`中写
import pymysql
pymysql.install_as_MySQLdb()
WARNINGS: 
?: (mysql.W002) MySQL Strict Mode is not set for database connection \'default\'
HINT: MySQL\'s Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it.
数据库迁移时候出现警告

在DATABASES配置中添加OPTIONS参数:

\'OPTIONS\': {
    \'init_command\': "SET sql_mode=\'STRICT_TRANS_TABLES\'"},

二.orm对象关系映射(object relational mapping)

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
Django会根据配置文件中的数据库类型来生成相应的SQL语句
Django支持MySQL5.5及其更高版本

2.1 orm简单理解

orm过程(原理):orm语句转化为sql语句调用pymysql模块发送给mysql服务端执行。

类----------------》表          #models.py中的类对应数据库中的表
对象--------------》记录(数据行)  #obj=models.User.objects.get(id=1) obj对应数据库表中的行数据
属性--------------》字段         #models.py类中的属性对应数据库表中的字段

2.2 orm基本操作

注意:在django中,我们查询获取的结果往往有两种(1.query_set对象,相当于一个列表(models.Books.objects.all()等等). 2.model对象,相当于一行的记录)

创建表

class AuthorDetail(models.Model):
    sex = models.BooleanField(max_length=1, choices=((0, \'男\'),(1, \'女\'),))
    email = models.EmailField()
    address = models.CharField(max_length=50)
    birthday = models.DateField()
    author = models.OneToOneField(Author)

常用字段

AutoField
    --自增的整形字段,primary_key=True参数必填,成为数据库的主键,无该字段django自动创建
    --models.AutoField(primary_key=True)
BooleanField
    --choices=((True,"男"),(False,"女"))   
    models.BooleanField(choices=choices)
    --choices选择器用于页面上的选择框标签,需要先提供一个二维的二元元组,元素1是在数据库的真实值,元素2是显示在界面上的值
IntegerField
    --一个整数类型
    --models.IntegerField()
CharField
    --字符类型,必须提供max_length参数。max_length表示字符的长度。
    --models.CharField(max_length=20)
DateField
    --日期类型,日期格式为YYYY-MM-DD,相当于Python中的datetime.date的实例
    --auto_now:数据有变动时修改为当前时间,如果修改当前时间字段是无法修改的。
    --auto_now_add:新创建对象时自动添加当前日期时间。
    --auto_now和auto_now_add和default参数是互斥的,不能同时设置。
    --在前端界面显示为Sept. 27, 2019,使用过滤器{{article.publish_date|date:"Y"}}
DatetimeField
    --格式为YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime的实例
TextField(Field)
    --文本类型
EmailField(CharField):
    --字符串类型,Django Admin以及ModelForm中提供验证机制,自己手动写入无法验证
DecimalField(Field)
    --10进制小数
    --参数:必须提供两个参数
        max_digits,小数总长度
        decimal_places,小数位长度
    --在插入数据时,字符串类型的插不进去,需要from decimal import Decimal 通过Decimal(字段)将其转为浮点型再插入
settings.py配置:
MEDIA_URL = "/img/"
MEDIA_ROOT = os.path.join(BASE_DIR,"media")

models.py
class Image(models.Model):
    image = models.ImageField(upload_to="images",blank=True,null=True) #上传的图片会在media文件夹下新建images文件夹,图片放在此文件夹下

img.html
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="file">
    <input type="submit">
</form>

views.py
def img(req):
    if req.method == "GET":
        return render(req,"img.html")
    else:
        img = req.FILES.get("file")
        models.Image.objects.create(
            image=img
        )
ImageField用法

增删改查

#增(create,save,bulk_create)
#1.create:
Author.objects.create(name=\'Alvin\')  # Author是类名,也是表名
Author.objects.create(**{"name": "alex"})  # create()参数如果是字典,前面**,如果是列表,前面*。
  --data=request.POST.dict() 能够将querydict转为普通的python字典格式
      models.Book.objects.create(
            # title=title,
            # price=price,
            # publish_date=publish_date,
            # publish=publish
            **data
        )
#2.save:
author = Author()
author.name = "alvin"
author.save()
#3.批量插入bulk_create
book_list = []
    for i in range(10):
        bk_obj = models.Book(
            name=\'chao%s\'%i,
            addr=\'北京%s\'%i
        )
        book_list.append(bk_obj)
models.Book.objects.bulk_create(book_list) #批量插入,速度快

#删(delete)
Book.objects.filter(id=1).delete() #queryset对象调用
Book.objects.filter(id=1)[0].delete() #model对象调用

#查(queryset类型的数据来调用)
1.filter(**kwargs): #它包含了与所给筛选条件相匹配的对象,返回queryset类型
2.all():  #查询所有结果,返回queryset类型
3.get():#返回行记录对象,结果超过1个或0个都会报错
4.exclude():查询与所给条件不匹配的对象,返回queryset类型
    --Book.objects.exclude(id=6)返回所有id=6之外的
5.values() #返回特殊的queryset类型,是一个字典序列
    --Book.objects.values("name")  #<QuerySet [{\'name\': \'alex\'}, {\'name\': \'egon\'}]>
6.values_list():返回特殊的queryset类型,是一个元组序列
    --Book.objects.values_list("name")  #<QuerySet [(\'alex\',), (\'egon\',)]>
7.order_by():对查询结果进行排序,默认按照id来升序排列
8.reverse():将已经排序的结果倒转(Book.objects.order_by().reverse()),返回queryset类型
9.count():返回查询结果的数量
10.exists():内不含参数,判断查询的结果是否包含数据,如果包含,返回True。
11.distinct():无参。values()或values_list()得到的queryset类型的数据来调用,从返回结果中剔除重复记录
12.first():返回第一条数据,非queryset类型数据
13.last():返回最后一条数据,非queryset类型数据

#改
方式1:
    models.UserInfo.objects.filter(id=2).update(name=\'篮子文\',checked = 0,)
    # 错误示例,model对象不能调用update方法
    models.UserInfo.objects.filter(id=2)[0].update(name=\'加篮子+2\',checked = 0,)
方式2
    ret = models.UserInfo.objects.filter(id=2)[0]
    ret.name = \'加篮子+2\'
    ret.checked = 1
    ret.save()
更新时的auto_now参数
    # 更新记录时,自动更新时间,创建新纪录时也会帮你自动添加创建时的时间,但是在更新时只有使用save方法的方式2的形式更新才能自动更新时间,有缺陷,放弃
    now2 = models.DateTimeField(auto_now=True,null=True)    

字段参数:

null                数据库中字段是否可以为空 null=True可为空
db_column           数据库中字段的列名
default             数据库中字段的默认值
primary_key         数据库中字段是否为主键
db_index            数据库中字段是否可以建立索引
unique              数据库中字段是否可以建立唯一索引
unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

verbose_name        Admin中显示的字段名称
blank               Admin中是否允许用户输入为空
editable            Admin中是否可以编辑
help_text           Admin中该字段的提示信息
choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
					如:gf = models.IntegerField(choices=[(0, \'何穗\'),(1, \'大表姐\'),],default=1)

error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
					字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date
					如:{\'null\': "不能为空.", \'invalid\': \'格式错误\'}

validators          自定义错误验证(列表类型),从而定制想要的验证规则
					from django.core.validators import RegexValidator
					from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\\
					MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
					如:
						test = models.CharField(
							max_length=32,
							error_messages={
								\'c1\': \'优先错信息1\',
								\'c2\': \'优先错信息2\',
								\'c3\': \'优先错信息3\',
							},
							validators=[
								RegexValidator(regex=\'root_\\d+\', message=\'错误了\', code=\'c1\'),
								RegexValidator(regex=\'root_112233\\d+\', message=\'又错误了\', code=\'c2\'),
								EmailValidator(message=\'又错误了\', code=\'c3\'), ]
						)

查询进阶(双下参数、方法):

#参数:
大于,小于
models.Tb1.objects.filter(id__gt=1)              # 获取id大于1的值
models.Tb1.objects.filter(id__gte=1)              # 获取id大于等于1的值
models.Tb1.objects.filter(id__lt=10)             # 获取id小于10的值
models.Tb1.objects.filter(id__lte=10)             # 获取id小于10的值
models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值

成员判断in
models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in

是否为空(判断是否为\'null\',而不是\'\')
Entry.objects.filter(pub_date__isnull=True)

包括contains
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.exclude(name__icontains="ven")

范围range
models.Tb1.objects.filter(id__range=[1, 2])   #大于等于1小于等于2

regex正则匹配,iregex 不区分大小写
Entry.objects.get(title__regex=r\'^(An?|The) +\')
Entry.objects.get(title__iregex=r\'^(an?|the) +\')

date
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))

year
Entry.objects.filter(pub_date__year=2005)
Entry.objects.filter(pub_date__year__gte=2005)

month
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)

day
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)

week_day
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)

hour
Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)

minute
Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)

second
Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)


#方法:

获取个数
models.Tb1.objects.filter(name=\'seven\').count()

排序order by
models.Tb1.objects.filter(name=\'seven\').order_by(\'id\')    # asc
models.Tb1.objects.filter(name=\'seven\').order_by(\'-id\')   # desc

分组group by
from django.db.models import Count, Min, Max, Sum
models.Tb1.objects.filter(c1=1).values(\'id\').annotate(c=Count(\'num\'))
SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"

limit 、offset
models.Tb1.objects.all()[10:20]

三.一对多操作

基于双下划线的查询就一句话:正向查询按字段,反向查询按表名小写用来告诉ORM引擎join哪张表,一对一、一对多、多对多都是一个写法,注意,我们写orm查询的时候,哪个表在前哪个表在后都没问题,因为走的是join连表操作。

1.表的创建+正反向查询

#创建两张表Author/Book
#在Author表中添加外键,外键是在多的表中添加的(一个作者可写多本书)。
class Author(models.Model):
    name=models.CharField(max_length=50)
    # book=models.ForeignKey("Book",on_delete=models.CASCADE)
class Book(models.Model):
    name=models.CharField(max_length=50)
    author=models.ForeignKey(to="Author",on_delete=models.CASCADE) #外键在此处创建,在表中生成author_id字段,在django1.xx版本中,on_delete默认级联删除,在2.xx版本中需要自己写
    # author = models.ForeignKey(to="Author", on_delete=models.CASCADE, related_name=\'xx\',related_query_name="xxx")

#正向查询(由author_id所在表查询另一张表)
book=Book.objects.get(id=4)       #从Book表中得到id=4的一行
print(book.author_id)             #得到author_id字段值
print(book.author.name)           #book.author得到关联的对象(行记录),再通过字段名取值

#反向查询
cls=Author.objects.get(id=1)  #Author表中id=1的行记录
cls.book_set.all() #cls.附表的小写表名_set.all()
cls.xx.all()   #有related_name参数没有related_query_name参数
cls.xxx.all()  #有related_name、related_query_name参数

#基于双下划线跨表查询
#正向跨表查询,参数为外键名__字段名
models.Book.objects.filter(author_name="施耐庵")   

#反向跨表查询,参数为附表名__字段
models.Author.objects.filter(book_name="三国演义") 名
models.Author.objects.filter(xx_name="三国演义")   #有related_name参数没有related_query_name参数
models.Author.objects.filter(xxx_name="三国演义")  #有related_name、related_query_name参数

四.多对多操作

1.表的创建

创建方式一:

#一个学生可以在多个班级上课,一个班级可以有多个学生
class Stuent(models.Model):
    name=models.CharField(max_length=50) 
    clas=models.ManyToManyField("Class")         #会自动产生新表student_class,且不会在当前表中生成新的字段
class Class(models.Model):
    name=models.CharField(max_length=50)

创建方式二:

class Stuent(models.Model):
    name=models.CharField(max_length=50) 
class Class(models.Model):
    name=models.CharField(max_length=50)
class Student_Class(models.Model):
  s=models.ForeignKey(Student)           #s在表中自我生成s_id,和Student表中的id绑定
  c=models.ForeignKey(Class)             #c在表中自我生成c_id,和Student表中的id绑定

操作:

以上定义了两个类,明面上是创建了两个新表,实际上是创建了三个表(因为有ManyToManyFiled
对待自动创建的表无法直接对第三张表操作
student = Student.objects.get(id=1)
student.name

# 对第三张表操作
#添加
student.clas.add(1)  #对第三张表添加student_id=1,class_id=1
student.clas.add(2,3)    #对第三张表添加student_id=1,class_id=2;student_id=1,class_id=3
student.clas.add(*[1,2,3])  #对第三张表添加student_id=1,class_id=1;student_id=1,class_id=2;student_id=1,class_id=3
#删除
student.clas.remove(1)     
student.clas.remove(2,3)    
student.clas.remove(*[1,2,3]) 

student.clas.clear()    

#更新
student.name = \'new_name\'
student.save()
student.clas.set([1,2,3])    

#查询
obj.stu.all()  #这是对另一张表的所有遍历
for i in obj.stu.all():
  print(i.name)
  #print(i.value("name"))
#基于双下划线的查询方法和一对多相同

五.聚合、分组、F和Q查询

先创建表

#Department
+----+-----------+
| id | name      |
+----+-----------+
|  1 | 人事部    |
|  2 | 销售部    |
|  3 | 技术部    |
+----+-----------+

#emplee
+----+------+-----+--------+--------+
| id | name | age | salary | dep_id |
+----+------+-----+--------+--------+
|  1 | alex |  12 |   2000 |      1 |
|  2 | egon |  20 |   3100 |      1 |
|  3 | meet |  21 |   1100 |      2 |
|  4 | cls  |  31 |    500 |      3 |
|  5 | chao |  30 |   5000 |      3 |
+----+------+-----+--------+--------+

#books
+----+------------+---------+
| id | commentNum | keepNum |
+----+------------+---------+
| 1 | 1 | 2 |
| 2 | 10 | 10 |
| 3 | 20 | 10 |
+----+------------+---------+

  

聚合

#aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典
from django.db.models import Avg,Sum,Max
ret = models.Emplee.objects.all().aggregate(Max(\'salary\')) #查询最大薪水
print(ret)  #{\'salary__max\': 5000.0}

#为字段起别名
ret = models.Emplee.objects.all().aggregate(max_salary=Max(\'salary\')) #查询最大薪水并为其设置别名
print(ret)  #{\'max_salary\': 5000.0}

#生成多个聚合
ret = models.Emplee.objects.all().aggregate(Max(\'salary\'),Sum(\'salary\')) #查询最大、最小薪水
print(ret)  #{\'salary__max\': 5000.0, \'salary__sum\': 11700.0}

分组

#分组中必须要有别名,values写在annotate前面是作为分组依据用的,并且返回给你的值就是这个values里面的字段(name)和分组统计的结果字段数据(max_price)
#单表查询
ret = models.Emplee.objects.values("dep_id").annotate(Max(\'salary\'))
    # ret = models.Department.objects.values("emplee__dep_id").annotate(c=Avg(\'emplee__salary\'))
    print(ret)
#跨表查询
ret = models.Department.objects.values("emplee__dep_id").annotate(c=Avg(\'emplee__salary\'))
print(ret) #<QuerySet [{\'emplee__dep_id\': 1, \'c\': 2550.0}, {\'emplee__dep_id\': 2, \'c\': 1100.0}, {\'emplee__dep_id\': 3, \'c\': 2750.0}]>

F查询

如果我们要对同一行记录中的两个字段的值做比较,就要用到F()

# 查询评论数大于收藏数的书籍
   from django.db.models import F
   Book.objects.filter(commentNum__lt=F(\'keepNum\'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。
# 查询评论数大于收藏数2倍的书籍
    Book.objects.filter(commentNum__lt=F(\'keepNum\')*2)

修改操作也可以使用F函数,比如将每一本书的价格提高30元:
Book.objects.all().update(price=F("price")+30) 

Q查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR 语句),你可以使用Q 对象。

Q 对象可以使用&(与) 、|(或)、~(非) 操作符组合起来。当一个操作符在两个Q 对象上使用时,它产生一个新的Q 对象。
bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

你可以组合& 和|  操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询:
bookList=Book.objects.filter(Q(authors__name="yuan") & ~Q(publishDate__year=2017)).values_list("title")
bookList=Book.objects.filter(Q(Q(authors__name="yuan") & ~Q(publishDate__year=2017))&Q(id__gt=6)).values_list("title") #可以进行Q嵌套,多层Q嵌套等,其实工作中比较常用

六.python脚本调用Django环境

import os

if __name__ == \'__main__\':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()

    from app01 import models  #引入也要写在上面三句之后

    books = models.Book.objects.all()
    print(books)

  django shell执行项目中py脚本

python3 manage.py shell < xx.py 

 

多对多操作:https://blog.csdn.net/shangliuyan/article/details/7920037  

以上是关于Django(数据库操作)的主要内容,如果未能解决你的问题,请参考以下文章

django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE的解决办法(转)(代码片段

Django REST框架--认证和权限

如何在 Django 中显式重置模板片段缓存?

使用 Django 模板作为片段

VSCode自定义代码片段——git命令操作一个完整流程

python 通过django片段很多很多