我个人对django中ORM的实现的理解:在models中写好类,通过migrate写入数据库。接着使用django封装好的方法,进行传参、调用。
一、数据库配置
(一).在settings.py中配置DATABASES
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # 数据库引擎 'NAME': 'mydb', # 数据库名称 'USER': 'root', # 连接数据库的用户名 'PASSWORD': 'qwe123', # 连接数据库的密码 'HOST': '127.0.0.1', # mysql服务器的域名和ip地址 # 'USER': 'admin', # 'PASSWORD': 'Root110qwe', # 'HOST': '192.168.60.128', 'PORT': '3306', # mysql的一个端口号,默认是3306 } }
注意事项:
(1).只要"HOST"不为127.0.0.1的时候,对于MySQL的root来说,都是属于远程连接(填写虚拟机中Linux的IP也算远程了)
而root用户只能在本地使用,它是拒绝远程连接的。所以,不填写127.0.0.1的话,可以考虑使用与root同权限的用户进行连接。
(2).NAME中所指定的数据库,一定要事先建好!数据库都没有,你让django怎么去连?连哪个?Ps:这是MySQL,不是MongoDB
(二).安装数据库连接器
在你自己使用的那个环境中,pip install pymsql
(三).
import pymysql pymysql.install_as_MySQLdb()
二、使用django中的模型
(一).进入你的app,打开"models.py"
最简单的示例1:
# blog/models.py from django.db import models # Create your models here. class User(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=30) age = models.IntegerField() def __str__(self): return 'User<id=%s,name=%s,age=%s>'%( self.id,self.name,self.age)
注意事项:
(1).模型必须都写在app的models.py中。
(2).模型所属的app必须被注册到"settings.py"的"INSTALL_APPS"中去,不然下面(二)的时候,是映射不到数据库中去的。
(3).所有的类,都必须继承"models.Model"才能生成映射后的表格。
(4).一个数据表对应模型中的类名。表中的字段,对应模型中的类属性。
(5).字段是varchar类型的话(它对应的是CharField),必须在CharField中设置max_length的值,不然会报错。
(6).AutoField()可以不写,django也会为我们自动创建。
(二).将模型映射到数据库中
(1).首先要创建一个映射文件
进入"manage.py"这个文件所在的目录,敲命令:python manage.py makemigrations
(2).将映射文件中的映射数据真正提交到数据库中
敲命令:python manage.py migrate
(3).两个命令长相差不多,一开始傻傻分不清,咋记?
migration是名词,所以只是个映射文件。migrate是动词,[/动起来~],才会真正起作用了。
(三).添加新字段的方式
有新字段要增加了?不用去MySQL中敲那么长一大段代码了,直接在django中进行操作就行。[/窃窃私语]:MySQL原生态添加新字段的代码真心很长 — —!
(1).直接点开models.py,把你想要加的字段,写进对应的类中
注意:新的字段需要指定"null=True"或者也可以用"default='xxx'"来代替。这两个你要是一个都不写,migration就会报错!因为原来的字段已经存在于数据表中了,数据表中可能已有数据了。那你这个新字段的值是什么?什么都没有,那不就报错了。
(2).cd到"manage.py"这个文件所在的目录中。
(3).再次执行命令:
python manage.py makemigrations # 等它执行完 # 新增字段会让你确认。直接输入 y 回车 # 再执行 python manage.py migrate
(四).修改字段
发现字段写错了?参考(三)同理可得
再次执行migrations和migrate
三、数据的增删改查
(一).增
# blog/views.py from .models import User def add_user(request): # 方法一: # tizi = User(name='tizi',age=18) # tizi.save() # 方法二: # xm = User() # xm.name = 'xiaoming' # xm.age = 19 # xm.save() # 方法三: # User.objects.create(name='xiaohong',age=20,) # 方法四: User.objects.get_or_create(name='xiaohua',age=21) return HttpResponse('插入数据成功!!!')
(1).字段编码有问题该如何处理
当带有汉字的数据记录插入表时报错了,基本是属于表的编码没有设置好,执行下面的代码
alter table 数据表名 CONVERT TO CHARACTER SET utf8;
(二).删
在MySQL中,我已写了老师曾说过一段话:“在开发中,有个不成文的规矩:宁可让数据作废,也不要去删”
我个人比较认可,万一你删错了呢?哪怕是测试用的数据,删错了,不是给自己找麻烦!
# blog/views.py def delete_user(request): User.objects.get(id=1).delete() return HttpResponse('删除数据成功!!!')
(三).改
# blog/views.py def update_user(request): # rs = User.objects.get(name='xiaoming') # rs.name = 'Xiaoming' # rs.save() # User.objects.filter(name='Xiaoming').update(name='XM') User.objects.all().update(city='changsha') return HttpResponse('更新数据成功!!!')
(四).查
# blog/views.py def search_user(request): # 查询所有记录对象 # rs = User.objects.all() # 查询一个记录对象 # rs = User.objects.get(id=1) # 获取满足条件的对象 rs = User.objects.filter(name='xiaoming') print(rs) return HttpResponse('查询数据成功!!!')
(1).常用的查询
获取所有记录: rs = User.objects.all() 获取第一条数据: rs = User.objects.first() 获取最后一条数据: rs = User.objects.last() 根据参数提供的条件获取过滤后的记录: rs = User.objects.filter(name='xiaoming') 注意:filter(**kwargs)方法:根据参数提供的提取条件,获取一个过滤后的QuerySet。 排除name等于xiaoming的记录: rs = User.objects.exclude(name='xiaoming') 获取一个记录对象: rs = User.objects.get(name='xiaoming') 注意:get返回的对象具有唯一性质,如果符合条件的对象有多个,则get报错! 对结果排序order_by: rs = User.objects.order_by('age') 多项排序: rs = User.objects.order_by('age','id') 逆向排序: rs = User.objects.order_by('-age') 将返回来的QuerySet中的Model转换为字典 rs = User.objects.all().values() 获取当前查询到的数据的总数: rs = User.objects.count()
(2).常用的查询对象的条件参数
查找对象的条件的意思是传给以上方法的一些参数。相当于是SQL语句中的where语句后面的条件,语法为"字段名__规则",以下将对这些规则进行说明: exact 相当于等于号: rs = User.objects.filter(name__exact='xiaoming') iexact:跟exact,只是忽略大小写的匹配。 contains 包含: rs = User.objects.filter(name__contains='xiao') icontains 跟contains,唯一不同是忽略大小写。 startwith 以什么开始: rs = User.objects.filter(name__startswith='xiao') istartswith:同startswith,忽略大小写。 endswith:同startswith,以什么结尾。 iendswith:同istartswith,以什么结尾,忽略大小写。 in 成员所属: rs = User.objects.filter(age__in=[18,19,20]) gt 大于: rs = User.objects.filter(age__gt=20) gte 大于等于: rs = User.objects.filter(age__gte=20) lt 小于: rs = User.objects.filter(age__lt=20) lte 小于等于: rs = User.objects.filter(age__lte=20) range 区间: rs = User.objects.filter(age__range=(18,20)) isnull 判断是否为空: rs = User.objects.filter(country__isnull=True) 切片: rs = User.objects.all()[:2] 注意:不能使用负数作为切片。
(五).注意事项
(1).filter()返回一个查询集合(可以视作列表)
(2).get()只返回一个对象,没有对象返回会报错(类名.DoesNotExist)
四、常用的模型字段类型
(一).IntegerField
整型,映射到数据库中的int类型。
(二).CharField
字符类型,映射到数据库中的varchar类型。必须制定max_length的值,不然就报错。
(三).TextField
文本类型,映射到数据库中的text类型。
(四).BooleanField
布尔类型,映射到数据库中的tinyint类型,在使用的时候,传递True/False进去。如果要可以为空,则用NullBooleanField。
(五).DateField
日期类型,没有时间。映射到数据库中是date类型,
在使用的时候,可以设置DateField(auto_now=True)每次修改对象时,自动设置该字段为当前时间。
设置DateField(auto_now_add=True)当对象第一次被创建时自动设置当前时间。
注意:DateField(auto_now=True)只有调用Model.save()方法才会调用,QuerySet.update方法将不会调用。这个参数只是Date和DateTime以及Time类才有的。
(六).DateTimeField
日期+时间类型。映射到数据库中的是datetime类型,在使用的时候,传递datetime.datetime()进去。
同样可以设置auto_now_add和auto_now
(七).补充
(1).关于django时区的问题
此博主总结得挺好:https://www.cnblogs.com/alan-babyblog/p/5739004.html
如果要使用本地时间,设置这两个属性:TIME_ZONE="Asia/Shanghai"; USE_TZ=False
(2).修改MySQL的时区
用root用户进入MySQL中,直接敲命令:set time_zone='+8:00';
然后重启MySQL服务:sudo service mysqld restart
再次进入MySQL中,查看时间的命令:select now();
五、表关系的实现
(一).一对多表关系
在MySQL中一对多是通过外键实现的,在django模型中通过ForeignKeyField类型实现。
(二).一对一表关系
在Mysql中一对一是通过外键+唯一键实现的,在django模型中通过OneToOneField类型实现。
(三).多对多表关系
在MySQL中多对多是通过中间表外键+联合唯一键实现的,在django模型中通过ManyToManyField类型实现。中间表模型会自动帮我们创建好。
(四).示例
(1).在app中的models.py中写好类
class Department(models.Model): d_id = models.AutoField(primary_key=True) d_name = models.CharField(max_length=30) def __str__(self): return 'Department<d_id=%s,d_name=%s>'%( self.d_id,self.d_name ) class Student(models.Model): s_id = models.AutoField(primary_key=True) s_name = models.CharField(max_length=30) department = models.ForeignKey('Department') course = models.ManyToManyField('Course') def __str__(self): return 'Student<s_id=%s,s_name=%s>'%( self.s_id,self.s_name ) class Course(models.Model): c_id = models.AutoField(primary_key=True) c_name = models.CharField(max_length=30) def __str__(self): return 'Course<c_id=%s,c_name=%s>'%( self.c_id,self.c_name ) class Stu_detail(models.Model): s_id = models.OneToOneField('Student') age = models.IntegerField() gender = models.BooleanField(default=1) country = models.CharField(max_length=30,null=True) def __str__(self): return 'Stu_detail<s_id=%s,age=%s,gender=%s,country=%s>'%( self.s_id,self.age,self.gender,self.country )
(2).分别执行
python manage.py makemigrations App_name
python manage.py migrate App_name
(3).在视图中对关系表的操作
from .models import Department,Student,Course def add_info(request): # d1 = Department(d_name='CC') # d1.save() # 一对多关系加内容 # s1 = Student(s_name='xiaoming') # s1.department = d1 # s1.save() # 多对多关系添加内容 # c1 = Course(c_name='python') # s1 = Student.objects.first() # c1.save() # s1.course.add(c1) return HttpResponse('添加数据成功') def search_info(request): rs = Student.objects.all()[0] # 一对多的查询 print(rs.department) # 多对多的正向查询 print(rs.course.all()) cs = Course.objects.first() # 多对多反向查询 print(cs.student_set.all()) return HttpResponse('查询数据成功')
(4).前向查询
如果一个模型有ForeignKey(),那么该模型的实例可以通过属性访问的方式,去访问关联的(外部)对象。
s1 = Student.objects.get(s_id=1) print(s1) # <Student: Student<s_id=1,s_name=xiaoming>> print(s1.department) # <Department: Department<d_id=6,d_name=CC>> # 还可以通过外键属性获取和设置 dx = Department.objects.get(d_id=3) print(dx) # <Department: Department<d_id=3,d_name=设计>> s1.department = dx print(s1.department ) # <Department: Department<d_id=3,d_name=设计>> s1.save() # 一定要save()了才会保存到数据库
(5).反向查询,方式1
d1 = Department.objects.get(d_id=1) print(d1) # <Department: Department<d_id=1,d_name=软件>> print(d1.student_set.all()) """ <QuerySet [<Student: Student<s_id=2,s_name=xiaohong>>, <Student: Student<s_id=3,s_name=xiaohua>>, <Student: Student<s_id=4,s_name=xiaoxin>>, <Student: Student<s_id=5,s_name=小明>>]> """
from blog.models import Student,Department,Course d1 = Department.objects.get(d_id=1) print(d1.student.all()) """ <QuerySet [<Student: Student<s_id=2,s_name=xiaohong>>, <Student: Student<s_id=3,s_name=xiaohua>>, <Student: Student<s_id=4,s_name=xiaoxin>>, <Student: Student<s_id=5,s_name=小明>>]> """
六、处理关联对象的其它方法
(一).add(obj1, obj2, ...)
添加一指定的模型对象到关联的对象集中。(一对多,多对多)。使用这个add的前提是要数据库中已经有的数据。
d1 = Department.objects.get(d_id=4) print(d1) # <Department: Department<d_id=4,d_name=AA>> st = Student.objects.get(s_id=6) print(st) #<Student: Student<s_id=6,s_name=xx>> d1.student.add(st) c1 = Course.objects.get(c_id=2) st.course.add(c1)
(二).create(**kwargs)
创建一个新的对象,将它保存并放在关联的对象集中,返回新创建的对象。(一对多,多对多)。添加不存在的数据,将数据直接存入数据库。
# 继续使用上面的代码 print(st) #<Student: Student<s_id=6,s_name=xx>> print(st.course.create(c_name='c++')) #<Course: Course<c_id=3,c_name=c++>> print(st.course.all()) # <QuerySet [<Course: Course<c_id=3,c_name=c++>>]>
(三).remove(obj1, obj2, ...)
从关联的对象集中删除指定的模型对象。(多对多)。删除的是关系表中的数据。
print(s3) # <Student: Student<s_id=3,s_name=xiaohua>> print(c1) # <Course: Course<c_id=2,c_name=java>> print(s3.course.all()) """ <QuerySet [<Course: Course<c_id=1,c_name=python>>, <Course: Course<c_id=2,c_name=java>>, <Course: Course<c_id=3,c_name=c++>>]> """ s3.course.remove(c1)
(四).clear()
从关联的对象集中删除所有的对象。(多对多)
s3.course.clear()
(五).直接赋值
print(s2) # <Student: Student<s_id=2,s_name=xiaohong>> print(cs) # <QuerySet [<Course: Course<c_id=3,c_name=c++>>]> print(s2.course.all()) # <QuerySet []> s2.course = cs print(s2.course.all()) # <QuerySet [<Course: Course<c_id=3,c_name=c++>>]>
(六).注意事项
(1).add()、create()、remove()和clear()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。
七、多表查询
(一).跨关联关系的查询
(1).例:
# 查询学院名字为‘软件的’的学生的信息 Student.objects.filter(department__d_name='软件')
(二).反向工作
若要引用一个“反向”的关系,只需要使用该模型的小写的名称。
(1).例:
# 查询学生名字中包含‘xiao’的学生的学院信息 Department.objects.filter(student__s_name__contains='xiao')
(三).
# 查询学号为1的学生所有的课程 Course.objects.filter(student__s_id=1) # 查询报了课程1的所有的学生 Student.objects.filter(course__c_id=1) # 查询报了'python'课程的的学生的所属学院的信息 Department.objects.filter(student__course__c_name='python')
博客小案例
这个小案例只是单表的增删改查。事先先建好一个名为myblog的应用。
(一).先把模板做出来
有了实际的页面,可以根据页面来知道要实现些什么功能。
Ps:代码都是测试通过后放上来的,功能也都实现了,所以页面上的变量都不删了。
(1).创建父模板
{% load static %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}{% endblock %}</title> <link rel="stylesheet" href="{% static "css/myblog.css" %}"> </head> <body> <div class="nav"> <ul> <li> <a href="{% url "myblog_index" %}" class="goto_homepage" style="margin-left: 200px;">首页</a> </li> </ul> </div> <br><br><br> <div> {% block bodyblock %} {% endblock %} </div> </body> </html>
(2).index页面,主页
{% extends 'myblog/demo_base.html' %} {% block title %} 首页 {% endblock %} {% block bodyblock %} <table width="150"> <tr> <td><a href="{% url "write_blog" %}" class="homepage_function_button">添加文章</a></td> <td><a href="{% url "blog_list" %}" class="homepage_function_button">文章列表</a></td> </tr> </table> {% endblock %}
(3).add页面,添加一篇博客
{% extends 'myblog/demo_base.html' %} {% block title %} 添加博客 {% endblock %} {% block bodyblock %} {% block h1_title %} <h1>添加新文章</h1> {% endblock %} <br> <form action="" method="POST"> {% csrf_token %} 标题:<input type="text" autocomplete="off" id="title" placeholder="请输入标题" name="title" value="{% block blog_title %}{% endblock %}"> <br><br> 内容:<textarea name="content" id="content" placeholder="请输入内容" cols="30" rows="10">{% block blog_content %}{% endblock %}</textarea> <br><br> <button type="submit">发布博客</button> </form> {% endblock %}
(4).update页面,修改一篇博客
修改页面与添加页面很像,所以直接继承add.html
{% extends "myblog/demo_add.html" %} <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>{% block title %}修改博客{% endblock %}</title> </head> <body> {% block h1_title %} <h1>修改文章</h1> {% endblock %} {% block blog_title %}{{ rs.blog_tilt }}{% endblock %} {% block blog_content %}{{ rs.blog_content }}{% endblock %} </body> </html>
(5).detail页面,显示一篇博客的详情
{% extends 'myblog/demo_base.html' %} {% block title %} 文章详情 {% endblock %} {% block bodyblock %} <h1>文章详情</h1> <br> 标题:{{ rs.blog_tilt }} <br> 博客内容:{{ rs.blog_content }} {% endblock %}
(6).list页面,列出所有的博客
不仅有所有博客的列表,还拥有功能键。
{% extends 'myblog/demo_base.html' %} {% block title %} 文章列表 {% endblock %} {% block bodyblock %} <h1 style="margin-left: 100px">文章列表</h1> <br> <table width="500px"> <thead style="font-size:20px"> <tr> <th>标题</th> <th>操作</th> </tr> </thead> <tbody> {% for each in rs %} <tr class="lightstick"> <th><a href="{% url "blog_detail" each.id %}">{{ each.blog_tilt }}</a><br></th> <th><a href="{% url "update_blog" each.id %}">编辑</a> | <a href="javascript:void(0);" onclick="delete_confirm('{% url "delete_blog" each.id %}');">删除</a> </th> </tr> {% endfor %} </tbody> {# 看着下面的表格的格式写循环 #} {# <tbody>#} {# <tr>#} {# <th><a href="">文章1</a></th>#} {# <th><a href="">编辑</a> | <a href="">删除 </a></th>#} {# </tr>#} {# <tr>#} {# <th><a href="">文章2</a></th>#} {# <th><a href="">编辑</a> | <a href="">删除 </a></th>#} {# </tr>#} {# </tbody>#} </table> <script> function delete_confirm(url) { if (confirm("确定要删除吗?")) { window.location = url; } } </script> {% endblock %}
(二).写CSS(可选步骤)
只是为了稍微好看一点点,此步骤可省略。
/* 此CSS文件是myblog的样式 */ * { margin: 0; padding: 0; } /*导航栏 START*/ .nav { width: 100%; height: 50px; position: absolute; background: ghostwhite; border: none; border-bottom: lightblue 1px solid; } .nav ul li { list-style: none; } /*导航栏 END*/ /*导航栏 首页 链接,用于返回首页 START*/ .goto_homepage { display: block; width: 50px; height: 50px; line-height: 50px; text-decoration: none; text-align: center; transition: all 300ms; } .goto_homepage:hover { text-decoration: none; background: rgb(180, 9, 28); color: white; box-shadow: 4px 4px 5px lightgrey; transition: all 300ms; } /*导航栏 首页 链接,用于返回首页 END*/ /*首页 添加文章、文章列表 按钮样式 START*/ .homepage_function_button { display: block; width: 80px; height: 30px; line-height: 30px; text-align: center; font-family: "微软雅黑"; font-size: 16px; transition: all 300ms; } .homepage_function_button:hover { text-decoration: none; background: rgb(180, 9, 28); color: white; box-shadow: 2px 2px 5px lightgrey; } /*首页 添加文章、文章列表 按钮样式 END*/ /*博客列表的光棒效果*/ .lightstick:hover { background: rgba(211, 211, 211, 0.25); } /* 通用的a标签的伪类 START */ a { text-decoration: none; transition: color 200ms; color: black; } a :link { color: black; } a:visited { color: black; } a:hover { text-decoration: underline; color: rgb(180, 9, 28); } a:active { color: black; } /* 通用的a标签的伪类 END */
(三).设计模型
# myblog/models.py from django.db import models # Create your models here. class SimpleBlog(models.Model): blog_tilt = models.CharField(max_length=100) blog_content = models.TextField() create_time = models.DateTimeField(auto_now_add=True) update_time = models.DateTimeField(auto_now=True)
注意:别忘了去Linux上makemigrations和mirgate
(四).编写视图函数
from django.shortcuts import render, HttpResponse, redirect, reverse from .models import * # Create your views here. # --------------- app:myblog 的逻辑 START --------------- def index(request): """ myblog的首页 : "myblog/demo_index.html" :param request: :return: 显示首页 -> myblog/demo_index.html """ return render( request, "myblog/demo_index.html", ) def write_blog(request): """ 写博客的页面 : "myblog/demo_add.html" :param request: :return: 显示写博客的页面 -> myblog/demo_add.html """ if request.method == "GET": return render(request, "myblog/demo_add.html") elif request.method == "POST": # 获取到前端form中的内容 tile = request.POST.get("title") content = request.POST.get("content") # 放入数据库 try: SimpleBlog.objects.create(blog_tilt=tile, blog_content=content) return HttpResponse("提交成功") except Exception as e: return HttpResponse("创建博客时,发生错误:{}".format(e)) else: return HttpResponse("无效的请求!") def get_all_blog_list(request): """ 列出所有的博客 : "myblog/demo_list.html" 左侧是博客标题。 右边是编辑、删除等功能键。 :param request: :return: 显示所有博客到页面 -> myblog/demo_list.html """ rs = SimpleBlog.objects.all().values() return render( request, "myblog/demo_list.html", context={ "rs": rs, }, ) def get_one_blog_detail(request, b_id): """ 查看一篇博客 : "myblog/demo_detail.html" :param request: :param b_id: 点详细信息时,传过来的博客id 为了避免与id可能会发生二义性,故命名b_id :return: 查看某篇博客的内容 -> myblog/demo_detail.html """ rs = SimpleBlog.objects.get(id=int(b_id)) return render( request, "myblog/demo_detail.html", context={ "rs": rs, }, ) def update_one_blog(request, b_id): """ 修改一篇博客 :param request: :param b_id: :return: """ rs = SimpleBlog.objects.get(id=b_id) if request.method == "GET": return render( request, "myblog/demo_update.html", context={ "rs": rs, }, ) elif request.method == "POST": title = request.POST.get("title") content = request.POST.get("content") try: rs.blog_tilt = title rs.blog_content = content rs.save() # 为了有update_time,才使用了这种方式 except Exception as e: return HttpResponse("修改时发生错误:" + e) else: return HttpResponse("修改成功!") else: return HttpResponse("修改成功!") def delete_one_blog(request, b_id): """ 删除一篇博客 :param request: :param b_id: :return: """ try: SimpleBlog.objects.get(id=b_id).delete() except: return HttpResponse("没有此篇博客") else: return redirect(reverse("blog_list")) # 直接重定向到列表页面 # --------------- app:myblog 的逻辑 END ---------------
(五).注册urls
# myblog/views.py from django.conf.urls import url from myblog import views urlpatterns = [ url(r"^$", views.index, name="myblog_index"), # 博客的首页 url(r"^write_blog/$", views.write_blog, name="write_blog"), # 写博客 url(r"^blog_list/$", views.get_all_blog_list, name="blog_list"), # 博客列表 url(r"^blog_detail/(?P<b_id>d+)/$", views.get_one_blog_detail, name="blog_detail"), # 某篇博客的详情 url(r"^delete_blog/(?P<b_id>d+)", views.delete_one_blog, name="delete_blog"), # 删除一篇博客 url(r"^update_blog/(?P<b_id>d+)", views.update_one_blog, name="update_blog"), # 更新一遍博客 ]