一, django 的 contenttypes
contenttypes 是 Django 内置的一个应用 , 可以追踪项目中所有 App 和 model 的对应关系, 并记录 djang_content_type 表中.
每当我们创建了新的 model 并执行数据库迁移后 , django_content_type 表中就会自动新增一条记录 , 比如我在应用 pp01 的 models.py 中创建表 class Electrics(models.Model): pass. 从数据库查看 django_content_type 表, 显示如下:
id | app_label | model |
.... | admin,auth 等内置应用 | .... |
4 | contenttypes | contenttype |
5 | app01 | electrics |
那么这个表有什么作用呢? 这里提供一个场景, 网上商城购物时, 会有各种各样的优惠券, 比如通用优惠券, 满减券, 或者是仅限特定品类的优惠券. 在数据库中, 可以通过外键将优惠券和不同品类的商品表关联起来:
- from django.db import models
- class Electrics(models.Model):
- """
- id name
- 1 日立冰箱
- 2 三星电视
- 3 小天鹅洗衣机
- """
- name = models.CharField(max_length=32)
- class Foods(models.Model):
- """
- id name
- 1 面包
- 2 烤鸭
- """
- name = models.CharField(max_length=32)
- class Clothes(models.Model):
- name = models.CharField(max_length=32)
- class Coupon(models.Model):
- """
- id name Electrics Foods Clothes more...
- 1 通用优惠券 null null null
- 2 冰箱满减券 2 null null
- 3 面包狂欢节 null 1 null
- """
- name = models.CharField(max_length=32)
- electric = models.ForeignKey(to='Electrics', null=True, on_delete=models.CASCADE)
- food = models.ForeignKey(to='Foods', null=True, on_delete=models.CASCADE)
- cloth = models.ForeignKey(to='Clothes', null=True, on_delete=models.CASCADE)
如果是通用优惠券, 那么所有的 ForeignKey 为 null, 如果仅限某些商品, 那么对应商品 ForeignKey 记录该商品的 id, 不相关的记录为 null. 但是这样做是有问题的: 实际中商品品类繁多, 而且很可能还会持续增加, 那么优惠券表中的外键将越来越多, 但是每条记录仅使用其中的一个或某几个外键字段.
通过使用 contenttypes 应用中提供的特殊字段 GenericForeignKey, 我们可以很好的解决这个问题. 只需要以下三步:
- 在 model 中定义 ForeignKey 字段, 并关联到 ContentType 表. 通常这个字段命名为 "content_type";
- 在 model 中定义 PositiveIntegerField 字段, 用来存储关联表中的主键. 通常这个字段命名为 "object_id";
- 在 model 中定义 GenericForeignKey 字段, 传入上述两个字段的名字;
为了更方便查询商品的优惠券, 我们还可以在商品类中通过 GenericRelation 字段定义反向关系.
示例代码如下:
- from django.db import models
- from django.contrib.contenttypes.models import ContentType
- from django.contrib.contenttypes.fields import GenericForeignKey
- class Electrics(models.Model):
- name = models.CharField(max_length=32)
- coupons = GenericRelation(to='Coupon') # 用于反向查询, 不会生成表字段
- def __str__(self):
- return self.name
- class Foods(models.Model):
- name = models.CharField(max_length=32)
- coupons = GenericRelation(to='Coupon')
- def __str__(self):
- return self.name
- class Clothes(models.Model):
- name = models.CharField(max_length=32)
- coupons = GenericRelation(to='Coupon')
- def __str__(self):
- return self.name
- class Coupon(models.Model):
- name = models.CharField(max_length=32)
- content_type=models.ForeignKey(to=ContentType, on_delete=models.CASCADE) # step1
- object_id=models.PositiveIntegerField() # step2
- content_object=GenericForeignKey('content_type', 'object_id') # step3
- def __str__(self):
- return self.name
ContentType 表对象有 model_class() 方法, 能取到对应 model, 如下:
- content = ContentType.objects.filter(app_label='app01', model='electrics').first()
- electrics_class = content.model_class() # electrics_class 就相当于 models.Electrics
- res = electrics_class.objects.all()
- print(res) # res 表示 electrics 表中的所有对象
以下是表操作示例:
- # 为三星电视 (id=2) 创建一条优惠记录
- s_tv = models.Electrics.objects.filter(id=2).first()
- models.Coupon.objects.create(name='电视优惠券', content_object=s_tv)
- # 查询优惠券 (id=1) 绑定了哪些商品
- coupon_obj = models.Coupon.objects.filter(id=1).first()
- prod = coupon_obj.content_object
- # 查询三星电视 (id=2) 的所有优惠券
- res = s_tv.coupons.all()
- # 查询 obj 的所有优惠券: 如果没有定义反向查询字段, 通过如下方式:
- content = ContentType.objects.filter(app_label='app01', model='model_name').first()
- res = models.OftenAskedQuestion.objects.filter(content_type=content, object_id=obj.pk).all()
总结: 当一张表和多个表 FK 关联, 并且多个 FK 中只能选择其中一个或其中 n 个时, 可以利用 contenttypes 应用, 只需定义三个字段就搞定!
二, django 的缓存相关
有时候你不想缓存一个页面, 甚至不想某个页面的一部分, 只是想缓存某个数据库检索的结果, django 提供了底层次的 API, 你可以是用这些 API 来缓存任何粒度的数据,
如果你想了解所有的 API, 强烈建议你去看 django\core\cache\backends 目录下的 cache.py 文件, 这里仅仅列举一些简单的用法:
- from django.core.cache import cache
- cache.set('token', 'safrgerjge') # 在缓存中设置一个类似字典的键值对
- cache.get('token') # 通过键取出值
- 'safrgerjge'
- cache.set('token', 'safrgerjge', 5) # 第三个参数代表过期时间, 5 秒后清除
- cache.get('token') # 在 5 秒内取出, 可以取出对应的值
- 'safrgerjge'
- cache.get('token') # 超过 5 秒, 键值被清除
- cache.get('token')
来源: http://www.bubuko.com/infodetail-2892966.html