在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’)

  1. def __str__(self):
  2. 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), ]

  1. **注意:**
  2. - 外键字段映射到数据库中,外键字段名会默认被添加上 **_id**,即定义的属性category,映射到数据库中则是字段category_id
  3. - 一个模型只能作为一个模型的外键关联对象,关联到多个模型会报错;
  4. <a name="O8nn5"></a>
  5. #### 跨app定义模型
  6. 如果想要引用另外一个app中的模型,那么需在传递to参数时,使用`**app_name.model_name**`进行指定。
  7. ```python
  8. # User模型在user这个app中
  9. class User(models.Model):
  10. username = models.CharField(max_length=20)
  11. password = models.CharField(max_length=100)
  12. # Article模型在article这个app中
  13. class Article(models.Model):
  14. title = models.CharField(max_length=100)
  15. content = models.TextField()
  16. # 引入user中的User模型
  17. author = models.ForeignKey("user.User",on_delete=models.CASCADE)

定义外键为自身的模型

如果模型的外键引用的是本身,那么to参数可以为’self’,或者是这个模型的名字。

在论坛开发中,一般评论都可以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身。

  1. '''models.py'''
  2. class Article(models.Model):
  3. name = models.CharField(max_length=20)
  4. comment = models.CharField(max_length=50)
  5. uid = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
  6. # 或者
  7. # uid = models.ForeignKey('Article', on_delete=models.CASCADE, null=True)
  8. '''views.py'''
  9. def index(request):
  10. # 一级评论保存
  11. # article = Article(name='rick', comment='我是一级评论')
  12. # article.save()
  13. # 二级评论保存
  14. article = Article(name='tony', comment='我回复一级评论的评论')
  15. first_article = Article.objects.get(pk=5)
  16. article.uid = first_article
  17. article.save()
  18. return HttpResponse('首页')

插入数据方式

  1. from django.http import HttpResponse
  2. from .models import Category, Book
  3. def add_data(request):
  4. """第一次需先添加分类数据并save"""
  5. # category = Category(name='计算机')
  6. # category.save()
  7. """第二次之后,若分类已存在,则直接查询分类即可"""
  8. category = Category.objects.get(pk=4)
  9. print(category)
  10. """获取到分类后,再添加书籍"""
  11. # book = Book(name='Python基础', author='龟叔', price=26)
  12. book = Book(name='Java从入门到精通', author='佚名')
  13. book.category = category # 将book中的category字段关联到模型category
  14. book.save()
  15. """获取分类名称"""
  16. books = Book.objects.first()
  17. print(books.category.name) # 外键字段赋值的是主表对象,故可以通过外键字段,直接访问主表中的数据
  18. return HttpResponse('添加数据成功')

通过books.category.name获取数据时,实际上是先通过category``_id找到对应的数据,然后再提取Category表中的name字段数据。

查询数据

  1. def find_data(request):
  2. from datetime import datetime
  3. sdate = datetime(year=2020, month=7, day=27, hour=10)
  4. edate = datetime(year=2020, month=7, day=28)
  5. book = Book.objects.filter(create_time__range=(sdate, edate))
  6. print(book)
  7. print(book.query)
  8. return HttpResponse('查询数据成功')

on_delete常用值

当主表中的数据被删除后,从表的这个字段该如何处理,需要通过on_delete来指定。

可以指定的类型如下:

  • CASCADE:级联操作
    • 如果主表中的某一条数据删除了,那么从表中使用该条数据作为外键的数据也会被删除。
  • PROTECT:受保护。
    • 即只要这条数据引用了主表中的某一条数据作为外键,那么就不能删除主表的那条数据。
  • SET_NULL:设置为空。
    • 如果外键的那条主表数据被删除了,那么在本条数据上就将这个字段设置为空。
    • 如果设置这个选项,前提是要指定这个字段可以为空。
  • SET_DEFAULT:设置默认值。
    • 如果外键的那条主表数据被删除了,那么本条数据上就将这个字段设置为默认值。
    • 如果设置这个选项,前提是要指定这个字段一个默认值。
  • SET():
    • 如果外键的那条主表数据被删除了,那么将会获取SET函数中的值来作为这个外键的值。
    • SET函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。
  • DO_NOTHING:不采取任何行为,一切全看数据库级别的约束。

以上这些选项只是Django级别的,数据级别依旧是RESTRICT!