数据库与ORM
1 django默认支持sqlite,mysql, oracle,postgresql数据库。
<1> sqlite
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3
<2> mysql
引擎名称:django.db.backends.mysql
2 mysql驱动程序
- MySQLdb(mysql python)
- mysqlclient
- MySQL
- PyMySQL(纯python的mysql驱动程序)
3 在django的项目中会默认使用sqlite数据库,在settings里有如下设置:
# Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }
如果我们需要更改数据库,就需要改其配置信息,一般是mysql
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_learn', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建 USER和PASSWORD分别是数据库的用户名和密码
这样我们的数据库就基本配置完成,我们可以创建一个表来测试一下:
我简单的创建一个数据表:
from django.db import models # Create your models here. class Book(models.Model): #必须继承 name = models.CharField(max_length=32) price = models.FloatField()
然后启动:
python manage.py makemigrations
启动后会报错,报错信息如下:
Traceback (most recent call last): File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\backends\mysql\base.py", line 15, in <module> import MySQLdb as Database ModuleNotFoundError: No module named 'MySQLdb' The above exception was the direct cause of the following exception: Traceback (most recent call last): File "manage.py", line 15, in <module> execute_from_command_line(sys.argv) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\core\management\__init__.py", line 371, in execute_from_command_line utility.execute() File "G:\PythonLearning\django_project\venv\lib\site-packages\django\core\management\__init__.py", line 347, in execute django.setup() File "G:\PythonLearning\django_project\venv\lib\site-packages\django\__init__.py", line 24, in setup apps.populate(settings.INSTALLED_APPS) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\apps\registry.py", line 112, in populate app_config.import_models() File "G:\PythonLearning\django_project\venv\lib\site-packages\django\apps\config.py", line 198, in import_models self.models_module = import_module(models_module_name) File "G:\PythonLearning\django_project\venv\lib\importlib\__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "<frozen importlib._bootstrap>", line 994, in _gcd_import File "<frozen importlib._bootstrap>", line 971, in _find_and_load File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked File "<frozen importlib._bootstrap>", line 665, in _load_unlocked File "<frozen importlib._bootstrap_external>", line 678, in exec_module File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed File "G:\PythonLearning\django_project\venv\lib\site-packages\django\contrib\auth\models.py", line 2, in <module> from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager File "G:\PythonLearning\django_project\venv\lib\site-packages\django\contrib\auth\base_user.py", line 47, in <module> class AbstractBaseUser(models.Model): File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\base.py", line 114, in __new__ new_class.add_to_class('_meta', Options(meta, app_label)) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\base.py", line 315, in add_to_class value.contribute_to_class(cls, name) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\models\options.py", line 205, in contribute_to_class self.db_table = truncate_name(self.db_table, connection.ops.max_name_length()) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\__init__.py", line 33, in __getattr__ return getattr(connections[DEFAULT_DB_ALIAS], item) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\utils.py", line 202, in __getitem__ backend = load_backend(db['ENGINE']) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\utils.py", line 110, in load_backend return import_module('%s.base' % backend_name) File "G:\PythonLearning\django_project\venv\lib\importlib\__init__.py", line 126, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "G:\PythonLearning\django_project\venv\lib\site-packages\django\db\backends\mysql\base.py", line 20, in <module> ) from err django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module. Did you install mysqlclient?
这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL 所以,我们只需要找到项目名文件下的__init__,在里面写入:
#在该文件写入如下代码
import pymysql
pymysql.install_as_MySQLdb()
这样问题就解决了。
但表并未成功创建,还需运行:
python manage.py migrate
这样就完全成功,可以在数据库上查看我们创建的表了。
ORM表模型
表(模型)的创建:
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名。
学生模型:每个学生对应一张身份证,每张身份证对应一位学生,学生和身份证模型之间是一对一的关系(one-to-one)
班级模型:一个班级有多个学生,但一个学生不能在多个班上,学生和班级是一对多关联关系(one-to-many),也被称作外键。
班级老师模型:一个老师可以教多个班级,一个班级可以由多个老师教,老师和班级模型就是多对多的关联关系(many-to-many)
表关系一对一:
#实质就是在主外键(author_id就是foreign key)的关系基础上,
# 给外键加了一个UNIQUE=True的属性,基本上很少用到一对一,就一张表表示就可以了
单表操作
创建表:
class Student(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() gender = models.BooleanField()
若表已经被创建,想要增加新的字段,直接在我们创建的类里面加,例:
class student(models.Model): name = models.CharField(max_length=30) age = models.IntegerField() gender = models.BooleanField() sign_date = models.CharField(max_length=10) #新加字段
然后运行:
python manage.py makemigrations
这时会出现选择框,让我们填一下,我们之前上传数据后,该列的默认值:
(venv) G:\PythonLearning\orm_learn>python manage.py makemigrations You are trying to add a non-nullable field 'sign_date' to student without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option: 1 #这里选择一,然后填写对应的数值
增加数据(也叫添加表记录):
def one_increate(request): #添加表数据一 推荐使用 student.objects.create(name="wallace",age=12,gender=True) student.objects.create(**{ "name":"夏露", "age":13, "gender":False }) #添加表数据二 stu1 = student(name="小米",age=14,gender=False) stu1.save()
stu2 = student() stu2.name="小张" stu2.age=13 stu2.gender=True stu2.save() return HttpResponse("创建成功")
删除表记录
student.objects.filter(name="wallace").delete()
修改表记录
#方法一 student.objects.filter(id=6).update(age=20) #找到直接修改对应字段 #方法二 stu1 = student.objects.filter(id=4)[0] #找到后全部修改一遍,再保存不推荐用 stu1.age=50 stu1.save()
查询表记录
关于查询的API
# <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象,结果是一个QuerySet对象,里面的元素是表对象,也叫表记录 # <2>all(): 查询所有结果,结果是Query对象,里面的元素是表对象,也叫表记录 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个就是表对象,也叫表记录,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
# <4>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象,与filter相反,结果是一个QuerySet对象,里面元素是表对象也叫表记录
例:
#关于查找的API def one_filter(request): #单用filter得到是一个QuerySet的对象,类似列表,可迭代,可切片 stu_obj = student.objects.filter(id=4) print(stu_obj) #将里面的数据迭代,结构是一个表记录对象,可以通过点的方式取值 for i in stu_obj: print(i) #表对象,就是一个表的记录 print(i.name) #取值 return HttpResponse("查询结束") def one_get(request): #直接得到一个表对象,也就是表记录 如果得到多个会报错 stu_obj=student.objects.get(id=4) print(stu_obj) #取值 print(stu_obj.name) return HttpResponse("查询结束") def one_all(request): #查询结果是一个QuerySet,类似列表,里面是所有的表对象也叫表记录 stu_obj_list=student.objects.all() print(stu_obj_list) #迭代出每一个表记录,通过属性对其取值 for i in stu_obj_list: print(i) print(i.name) return HttpResponse("查询结束")
def one_exclude(request):
#查询结果是QuerySet对象,里面的元素是除了给定条件的所有表对象,也叫表记录
stu_obj = student.objects.exclude(name="wallace")
#和上面的效果一样
stu_obj1 = student.objects.all().exclude(name="wallace")
print(stu_obj)
for i in stu_obj:
print(i.name)
print(stu_obj1)
return HttpResponse("exclude success")
对查询结构处理的方法:
values:返回得到的是一个Queryset对象,但里面的元素是一个个字典。单表情况下配合all或直接引用。多表以后介绍
def one_value(request): #values得到的是一个特殊的QuerySet对象,类似列表,但里面的元素是一个一个的字典 stu_obj = student.objects.values("name")
print(stu_obj) #得到的是一个特殊的QuerySet对象,类似列表,里面的元素是一个一个字典 stu_obj2 = student.objects.filter(id=3).values("name") print(stu_obj2)
#all取得了全部的表记录,类似于一张表,所以可以去的对应的值 stu_obj3 = student.objects.all().values("name") print(stu_obj3) return HttpResponse("value success")
def one_value_list(request): #和value类似,返回的是QuerySet对象,不过里面元素是列表的形式 stu_obj = student.objects.values_list() print(stu_obj)
正向排序order_by_反向排序reverse
# <6>order_by(*field): 对查询结果排序,默认是正向排序,*field为按照那个字段排序 # <7>reverse(): 在排序的基础上加上这个语句,进行反向排序,单独使用无意义
例
def one_order(request): #排序 stu_obj = student.objects.all().order_by("age") stu_obj1 = student.objects.order_by("age") stu_obj3 = student.objects.filter(age__gt=20).order_by("age") print(stu_obj3) for i in stu_obj3: print(i.age) return HttpResponse("order success") def one_reverse(request): stu_obj = student.objects.all().order_by("age").reverse() stu_obj1 = student.objects.filter(age__gte=20).order_by("age").reverse() print(stu_obj) for i in stu_obj: print(i.age) return HttpResponse("rever success")
distinct:从返回结果中剔除重复记录
def one_distinct(request): #取重,这里的去重指的是全部字段都重复时进行去重,除了id字段 stu_obj = student.objects.all().distinct().values("age","name") stu_obj1 = student.objects.distinct().values("age","name") print(stu_obj) print(stu_obj1) return HttpResponse("distinct success")
count:返回数据库中匹配查询(QuerySet)的对象数量
def one_count(request): #统计得到表对象的数量 stu_obj = student.objects.all().count() print(stu_obj) stu_obj1 = student.objects.filter(age__gte=30).count() print(stu_obj1) stu_obj2 = student.objects.count() print(stu_obj2) return HttpResponse("success")
first和last:返回得到QuerySet对象中的第一或最后一个表对象
def one_first_last(request): #fiest返回找到的第一个表对象 stu_obj = student.objects.first() stu_obj1 = student.objects.filter(age=13).first() print(stu_obj,stu_obj1) #last返回的是最后一个表对象 stu_obj2 = student.objects.last() print(stu_obj2) return HttpResponse('success')
单表的双下划线操作
__gt 大于 __lt 小于 __gte 大于等于 __lte 小于等于 __in 在几个值中 __range 在一定范围内
例
#双下滑线机制,单表 def one__select(request): #大于13 stu_obj = student.objects.filter(age__gt=13).values("age") #大于等于13 stu_obj1 = student.objects.filter(age__gte=13).values("age") print(stu_obj) print(stu_obj1) #小于 stu_obj2 = student.objects.filter(age__lt=13).values("age") #小于等于 stu_obj3 = student.objects.filter(age__lte=13).values("age") print(stu_obj2) print(stu_obj3) #等于13 stu_obj4 = student.objects.filter(age__exact=13).values("age") print(stu_obj4) #在多个值里面 stu_obj5 = student.objects.filter(age__in=[12,13,14]).values("age") print(stu_obj5) #在一个范围里面 stu_obj6 = student.objects.filter(age__range=[12,14]).values("age") print(stu_obj6) return HttpResponse("success")
注意:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。如在遍历或条件
判断的时候,查询数据库的操作才会执行。
查找表字段,也叫对象查找:
def one_obj(request): stu_obj_list = student.objects.all() #从Queryset集合中得到表对象,通过对象获取里面的字段 for stu_obj in stu_obj_list: print(stu_obj.name) print(stu_obj.age) print(stu_obj.gender) return HttpResponse("success")
一对多的表操作
在创建多表关系时,先讨论一下,FroeignKey的创建时需注意的地方:
我现在用的是django2.0.2版本,当使用models.ForeignKey设置外键,但是不添加on_delete时,总是会报错: TypeError: __init__() missing 1 required positional argument: 'on_delete' 而django自从1.9版本之后,on_delete这个参数就必须设置了 看看官网怎么说的吧: ForeignKey.on_delete 当一个ForeignKey 引用的对象被删除时,Django 默认模拟SQL 的ON DELETE CASCADE 的约束行为,并且删除包含该ForeignKey的对象。这种行为可以通过设置on_delete 参数来改变。例如,如果你有一个可以为空的ForeignKey,在其引用的对象被删除的时你想把这个ForeignKey 设置为空: user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL) on_delete 在django.db.models中可以找到的值有: CASCADE 级联删除;默认值。 PROTECT 抛出ProtectedError 以阻止被引用对象的删除,它是django.db.IntegrityError 的一个子类。 SET_NULL¶ 把ForeignKey 设置为null; null 参数为True 时才可以这样做。 SET_DEFAULT¶ ForeignKey 值设置成它的默认值;此时必须设置ForeignKey 的default 参数。 SET()¶ 设置ForeignKey 为传递给SET() 的值,如果传递的是一个可调用对象,则为调用后的结果。在大部分情形下,传递一个可调用对象用于避免models.py 在导入时执行查询: from django.conf import settings from django.contrib.auth import get_user_model from django.db import models def get_sentinel_user(): return get_user_model().objects.get_or_create(username='deleted')[0] class MyModel(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET(get_sentinel_user)) DO_NOTHING¶ Take no action. 如果你的数据库后端强制引用完整性,它将引发一个IntegrityError ,除非你手动添加一个ON DELETE 约束给数据库自动(可能要用到初始化的SQL)。
创建一对多关系表Fromkey:
class Teacher(models.Model): name = models.CharField(max_length=30) galary = models.IntegerField() book = models.ForeignKey("Book",null=True,on_delete=models.SET_NULL) #外键关联我们要关联的表
class Book(models.Model): name = models.CharField(max_length=30) price = models.FloatField()
增加表数据:
def one_more_create(request): #添加方式一(推荐) 通过下划线的方式获取书籍id teach_obj = Teacher.objects.create( name="天蚕土豆", galary=3000, book_id=1 ) teach_obj2 = Teacher.objects.create( name="老男孩", galary=4000, book_id=2 ) #添加方式二,先获取书籍对象,再将对象赋值给book book_obj = Book.objects.filter(id=3).first() teach_obj3 = Teacher.objects.create( name="wallace", galary=1000000, book=book_obj ) return HttpResponse("SUCCESS")
删除和修改和单表操作一致,这里就不再赘述了。
一对多查询表记录:
方法一:从一个表中得到关联表的对象,然后从对象中取值,分为正向查询和反向查询
def one_more_filter(request): #正向查询,由多的一方查询一个的一方,通过老师查询出版的书籍名字 #查询方式一 # teach_obj = Teacher.objects.get(name="wallace") # book_obj = teach_obj.book # print(book_obj.name) #查询过程:得到表对象,找到对应的外键字段,这个外键字段就是一个表记录对象 #通过这个表记录对象去查找对应表对象的字段 # 反向查询:一个的一方查多个的一方,比如查询出版某本书籍的老师 book_obj1 = Book.objects.filter(name="傲视九重天").first() teach_name = book_obj1.teacher_set.all().values("name") #通过关联的表的类名加上_set得到对应表的对象 classname_set #类似于在父表也创建了一个隐藏的字段 print(teach_name) return HttpResponse("查询成功")
方式二:通过(filter,values)的双下滑线机制,理论是将两个表内联结在一起:
def one_more_filter1(request): #一对多表查询(filter,values)的双下划线的使用:当你要跨表操作时,就通过双下划线来操作 #这种双下划线就像吧两个表内联结在一起,同过下划线获取关联表的字段 #正向 classname__filed
#查找老师为wallace的所有书的书名 book_name = Teacher.objects.filter(name="wallace").values("book__name") print(book_name)
#查找数目为极品家丁的老师:通过老师表,得到书籍名,跨表 teach_name = Teacher.objects.filter(book__name="极品家丁").values("name") print(teach_name) #反向 book_name1 = Book.objects.filter(teacher__name="wallace").values("name") teach_name1 = Book.objects.filter(name="大主宰").values("teacher__name") print(book_name1) print(teach_name1) #更多筛选条件 classname__filed__逻辑条件 teach_name2=Book.objects.filter(name="大主宰",teacher__galary__gt=5000).values("teacher__name") print(teach_name2) return HttpResponse("success")
多对多表关系
创建多对多表关系:
class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() class Book(models.Model): title = models.CharField(max_length=100) price = models.IntegerField() author = models.ManyToManyField("Author") #通过该字段就可以创建一个Book和Author的多对多表字段 #这种方式的创建会自动帮我们生成一张关系表,不需要我们自己手动创建
还有另一种创建方式就是我们自己手动的创建第三张关系表,表中只有两表的主键id,因为这种用的不多所以在这里就不赘述了。
多对多表添加绑定关系,如果是单纯的添加字段,可以直接进行单表添加:
def more_create(request): #其他字段正常添加,这里添加的是两个表之间的关系 #例如:向给id=3的书,添加作者 ''' 第一步:获取该书的对象 第二步: 获取你给书绑定的作者的集合 第三步: 通过书的表对象获取添加多对多表关系的字段用add方式添加 ''' book_obj = Book.objects.get(id=4) author_obj = Author.objects.all() #author_obj是一个表集合,所以要加*号 # book_obj.author.add(*author_obj) #通过remove方法,可以将我们建立的表关系解除 # book_obj.author.remove(*author_obj) #也可以传人数字,删除的id为时的关联关系,不常用 #同理反向添加也是一样的 author_obj1 = Author.objects.get(id=2) book_obj1 = Book.objects.all() author_obj1.book_set.add(*book_obj1) return HttpResponse("success")
多对多表查询:
def more_to_more(request): #找书名id为1的作者的名字 book_obj = Book.objects.filter(id=1).values("author__name") print(book_obj) book_obj2 = Book.objects.filter(id=1).first() print(book_obj2.author) book_obj3 = Book.objects.get(id=3) # 这里得到的是一个author的Qureyset的对象 print(book_obj3.author) # 所以需要对他进行去取值 print(book_obj3.author.all()) # 而反向查找的时候用set的方式查询 author_obj = Author.objects.get(id=3) print(author_obj.book_set.all()) return HttpResponse("success")
聚合查询和分组查询:
#聚合查询 def aggre_query(request): #这里是选择对应的作者,查询他所有的出的书的价格 price_avg = Author.objects.filter(id=1).aggregate(Avg("book__price")) print(price_avg) price_avg2 = Book.objects.all().aggregate(Avg("price")) price_max = Book.objects.all().aggregate(Max("price")) price_min = Author.objects.filter(id=1).aggregate(Min("book__price")) print(price_avg2,price_max,price_min) return HttpResponse("success") #分组查询 def anno_query(request): #对作者进行价格的分组,一般分组时会联合聚合函数 price_avg = Author.objects.values("name").annotate(Avg("book__price")) print(price_avg) return HttpResponse("success")
F查询和Q查询:是一种自定义的查询手段,当聚合和组合无法满足我们时,可以使用F和Q查询
def q_query(request): #这里得到的是一个Queryset集合 q1 = Author.objects.filter(Q(name__startswith="天")).all() #可以用逻辑运算符一起作用 q2 = Author.objects.filter(Q(name__startswith="天") | Q(name__startswith="失")) print(q1.first().name) return HttpResponse("success") def f_query(request): #F查询是当你需要对表中的某个字段进行操作时,例如运算等的时候使用 book_obj = Book.objects.update(price=F("price")+100)
admin
admin是django强大功能之一,它能共从数据库中读取数据,呈现在页面中,进行管理。默认情况下,它的功能已经非常强大,如果你不需要复杂的功能,它已经够用,但是有时候,一些特殊的功能还需要定制,比如搜索功能,下面这一系列文章就逐步深入介绍如何定制适合自己的admin应用。
如果你觉得英文界面不好用,可以在setting.py 文件中修改以下选项:
LANGUAGE_CODE = 'zh-hans'
第二步:
admin.site.register(Book) #将你要使用的数据库进行注册,这是默认样式
第三步:如果希望使用更多的样式,可以进行定制
class Myadmin(admin.ModelAdmin): list_display = ("title","price") search_fields = ("title","price") admin.site.register(Book,Myadmin)
定制样式的语句字段:
list_display: 指定要显示的字段
search_fields: 指定搜索的字段
list_filter: 指定列表过滤器
ordering: 指定排序字段
第二种使用样式方法:不推荐
# @admin.register(Book) class Myadmin(admin.ModelAdmin): list_display = ("title","price") search_fields = ("title","price") #一般是单个表进行样式添加