在MySQL中,表有两种常用引擎,一种是InnoDB,另外一种是MyISAM。如果使用的是InnoDB引擎,是支持外键约束的,MyISAM则不支持外键;外键的存在使得ORM框架,在处理表关系的时候异常的强大。
ORM中定义外键
定义外键字段
类定义为class ForeignKey(to,on_delete,**options)
参数:
- to:外键的模型名
- on_delete:当主表中的数据被删除后,从表的这个字段该如何处理,其值有CASCADE、SET_NULL等
- 从表中定义外键字段:category = models.ForeignKey(“Category”, on_delete=models.CASCADE)**
注意:
- django中定义了外键的话,会自动创建一个和flask中backref一样的反向引用,名为模型名(类名)的小写,所谓反向引用暂时理解为:可以在主表中进行查询时,可以通过反向引用,来使用从表的字段进行判断;
- 当反向引用后面没跟字段时,代表默认跟了从表的主键,然后进行判断;
- 使用的是联合查询inner join…on… (on后面跟的条件相等的才会查询出来)
定义ORM外键模型
在app内部定义模型
```python “””models.py””” from django.db import models
class Category(models.Model): name = models.CharField(max_length=100)
class Book(models.Model): name = models.CharField(max_length=100) author = models.CharField(max_length=100) price = models.IntegerField(default=0) create_time = models.DateTimeField(auto_now=True) category = models.ForeignKey(‘Category’, on_delete=models.CASCADE, related_name=’bk’)
def __str__(self):
return "书名:%s, 作者:%s, 创建时间:%s" % (self.name, self.author, self.create_time)
“””app内部的urls.py””” from django.urls import path from . import views
urlpatterns = [ path(‘’, views.index), path(‘add/‘, views.add_data), path(‘find/‘, views.find_data), ]
**注意:**
- 外键字段映射到数据库中,外键字段名会默认被添加上 **_id**,即定义的属性category,映射到数据库中则是字段category_id。
- 一个模型只能作为一个模型的外键关联对象,关联到多个模型会报错;
<a name="O8nn5"></a>
#### 跨app定义模型
如果想要引用另外一个app中的模型,那么需在传递to参数时,使用`**app_name.model_name**`进行指定。
```python
# User模型在user这个app中
class User(models.Model):
username = models.CharField(max_length=20)
password = models.CharField(max_length=100)
# Article模型在article这个app中
class Article(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
# 引入user中的User模型
author = models.ForeignKey("user.User",on_delete=models.CASCADE)
定义外键为自身的模型
如果模型的外键引用的是本身,那么to参数可以为’self’,或者是这个模型的名字。
在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。
'''models.py'''
class Article(models.Model):
name = models.CharField(max_length=20)
comment = models.CharField(max_length=50)
uid = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
# 或者
# uid = models.ForeignKey('Article', on_delete=models.CASCADE, null=True)
'''views.py'''
def index(request):
# 一级评论保存
# article = Article(name='rick', comment='我是一级评论')
# article.save()
# 二级评论保存
article = Article(name='tony', comment='我回复一级评论的评论')
first_article = Article.objects.get(pk=5)
article.uid = first_article
article.save()
return HttpResponse('首页')
插入数据方式
from django.http import HttpResponse
from .models import Category, Book
def add_data(request):
"""第一次需先添加分类数据并save"""
# category = Category(name='计算机')
# category.save()
"""第二次之后,若分类已存在,则直接查询分类即可"""
category = Category.objects.get(pk=4)
print(category)
"""获取到分类后,再添加书籍"""
# book = Book(name='Python基础', author='龟叔', price=26)
book = Book(name='Java从入门到精通', author='佚名')
book.category = category # 将book中的category字段关联到模型category
book.save()
"""获取分类名称"""
books = Book.objects.first()
print(books.category.name) # 外键字段赋值的是主表对象,故可以通过外键字段,直接访问主表中的数据
return HttpResponse('添加数据成功')
通过books.category.name
获取数据时,实际上是先通过category``_id
找到对应的数据,然后再提取Category表中的name字段数据。
查询数据
def find_data(request):
from datetime import datetime
sdate = datetime(year=2020, month=7, day=27, hour=10)
edate = datetime(year=2020, month=7, day=28)
book = Book.objects.filter(create_time__range=(sdate, edate))
print(book)
print(book.query)
return HttpResponse('查询数据成功')
on_delete常用值
当主表中的数据被删除后,从表的这个字段该如何处理,需要通过on_delete来指定。
可以指定的类型如下:
- CASCADE:级联操作
- 如果主表中的某一条数据删除了,那么从表中使用该条数据作为外键的数据也会被删除。
- PROTECT:受保护。
- 即只要这条数据引用了主表中的某一条数据作为外键,那么就不能删除主表的那条数据。
- SET_NULL:设置为空。
- 如果外键的那条主表数据被删除了,那么在本条数据上就将这个字段设置为空。
- 如果设置这个选项,前提是要指定这个字段可以为空。
- SET_DEFAULT:设置默认值。
- 如果外键的那条主表数据被删除了,那么本条数据上就将这个字段设置为默认值。
- 如果设置这个选项,前提是要指定这个字段一个默认值。
- SET():
- 如果外键的那条主表数据被删除了,那么将会获取SET函数中的值来作为这个外键的值。
- SET函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。
- DO_NOTHING:不采取任何行为,一切全看数据库级别的约束。
以上这些选项只是Django级别的,数据级别依旧是RESTRICT!