在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_articlearticle.save()return HttpResponse('首页')
插入数据方式
from django.http import HttpResponsefrom .models import Category, Bookdef 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字段关联到模型categorybook.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 datetimesdate = 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!
