11.关于django的content_type表
******
Django的contenttype表中存放发的是app名称和模型的对应关系 contentType使用方式
- 导入模块
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
- 定义models
class PythonBasic(models.Model):
course_name = models.CharField(max_length=32)
class Oop(models.Model):
course_name = models.CharField(max_length=32)
class Coupon(models.Model):
coupon_name = models.CharField(max_length=32)
# 关联到contenttype外键,用来存放对应的表的id(在contenttype表中的id)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
# 关联对应的商品的id
object_id = models.PositiveIntegerField()
# 关联的对象商品
content_object = GenericForeignKey("content_type", "object_id")
- 使用
class CourseView(APIView):
def get(self, request):
pb = ContentType.objects.get(app_label="myapp", model="pythonbasic")
# model_class()获取对应的表类
object_id = pb.model_class().objects.get(pk=3)
# 其中的关系我们可以使用content_object进行关联,它相当于一个管理器
# 我们将对应的对象给他,他会自动给字段建立关系
# 但是其实我对于它的应用了解还不是很多,如果需要请看django文档
Coupon.objects.create(coupon_name="python通关优惠券", content_object=object_id)
return HttpResponse("ok")
总结:
关于它的使用时机,其实就是如果一个模型类与多个类之间都存在外键关系的时候。比如订单表与书籍表以及衣服表,都会产生订单,但是万一还有其他类的商品也要关联订单的时候,我们可以使用,contentType进行关联。其实就是一个万能的中间表,可以关联任何的其他表。
当一张表作为多个表的FK,就可以使用content_type表;例如上面的优惠券表,被食物和衣服当作FK,数据库表一旦建立就难以更改,如果以后需要增加电器等表并把优惠券表作为FK表,这时就不能做到在优惠券表增加列字段electr_id,因为表只能增加行记录而不能增加列字段(虽然扩张表字段也可以,但是一个是浪费空间,第二个就是麻烦难道没有一种商品类型你就加一个字段,累死了),因此就可以使用content_type表来将表与表中的对象进行关联,从而做到不增加列字段的情况下增加FK关系。
在使用content_type表时,需要在FK表中增加content_type作为FK字段,并增加GenericForeignKey便于优惠券表记录的建立以及单个优惠券对象对应的其他商品的查找。在优惠券表关联的“一”的关系表中增加GenericRelation字段便于查找关联的优惠券记录的查找
def get_order_serializer(queryset):
class OrderItemSerializer(StandardModelSerializer):
object_id = serializers.PrimaryKeyRelatedField(queryset=queryset)
price = serializers.DecimalField(max_digits=10, decimal_places=2) class Meta:
model = OrderItem
fields = ("price", "object_id") class OrderCSerializer(StandardModelSerializer):
item = OrderItemSerializer(many=True) class Meta:
model = Order
fields = ("item",) return OrderCSerializer, OrderItemSerializer class OrderItemSerializer(StandardModelSerializer):
name = serializers.CharField(source="content_type.name") class Meta:
model = OrderItem
fields = ('price', "name") class OrderSerializer(StandardModelSerializer):
item = OrderItemSerializer(many=True) # 与嵌套的serializer中的model表中的related_name关联 class Meta:
model = Order
fields = ("amount", "item")
class OrderViewSet(base_view.BaseGenericViewSet,
CreateModelMixin,
ListModelMixin):
queryset = Order.objects.all()
serializer_class = OrderSerializer
filter_backends = (StandardOrderingFilter, StandardFilterBackend, StandardSearchFilter)
search_fields = ("amount", "id")
filter_fields = ("amount", "id")
ordering = "id" def create(self, request: Request, model, app_label):
content_type = ContentType.objects.get(model=model, app_label=app_label)
prd_model = content_type.model_class()
order_ser, order_item_ser = get_order_serializer(prd_model._default_manager.all())
serializer = order_ser(data=request.data)
serializer.is_valid()
data = serializer.validated_data
order = Order.objects.create(amount=0)
total_amount = 0
for post_item in data["item"]:
content_object = post_item["object_id"]
price = content_object.get_price
if price != post_item["price"]:
raise Invalid
total_amount += int(price)
OrderItem.objects.create(content_object=content_object, price=price, order=order)
order.amount = total_amount
order.save()
ser = self.get_serializer(order)
headers = self.get_success_headers(ser.data)
return Response(data=ser.data, headers=headers, status=status.HTTP_201_CREATED)
view.py
class Order(models.Model):
amount = models.DecimalField(max_digits=10, decimal_places=2, default=-1) class OrderItem(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.DO_NOTHING)
object_id = models.IntegerField()
content_object = GenericForeignKey()
price = models.DecimalField(max_digits=10, decimal_places=2)
order = models.ForeignKey(db_constraint=False, db_column="order_id",
db_index=True, on_delete=models.DO_NOTHING,
related_name="item", to="Order",
to_field="id") # 设置的related_name是很关键的,因为在serializer传入数据的时候,Orde表会根据它进行查找,如果名称不对会发生错误 class Product(models.Model):
name = models.CharField(max_length=32)
price = models.DecimalField(max_digits=10, decimal_places=2) @property
def get_price(self):
return self.price class Meta:
db_table = "product"
verbose_name_plural = verbose_name = "product" def __str__(self):
return "{}".format(self.name)
contenttypes框架¶
Django包含一个contenttypes应用程序,可以跟踪Django驱动的项目中安装的所有模型,为您的模型提供高级通用界面。 概述¶
contenttypes应用程序的核心是ContentType生活在的 模型 django.contrib.contenttypes.models.ContentType。ContentType表示和存储有关项目中安装的模型的信息的实例, 以及安装ContentType新模型时自动创建的新实例 。 ContentType具有返回它们所代表的模型类以及从这些模型查询对象的方法的实例。ContentType 还有一个自定义管理器,它添加了使用ContentType和获取ContentType 特定模型实例的方法。 模型之间的关系 ContentType也可用于启用某个模型的实例与已安装的任何模型的实例之间的“通用”关系。 安装contenttypes框架¶
contenttypes框架包含在由其INSTALLED_APPS创建的默认 列表中,但如果您已将其删除,或者您手动设置了 列表,则可以通过添加到您的设置来启用它 。django-admin startprojectINSTALLED_APPS'django.contrib.contenttypes'INSTALLED_APPS 安装contenttypes框架通常是个好主意; Django的其他几个捆绑应用程序需要它: 管理应用程序使用它来记录通过管理界面添加或更改的每个对象的历史记录。
Django 使用它将用户权限绑定到特定模型。authentication framework
该ContentType模型¶
类ContentType¶
每个实例ContentType 都有两个字段,它们一起唯一地描述了已安装的模型: app_label¶ (这个是你app的名称)
模型所属应用程序的名称。这取自app_label模型的属性,仅包括应用程序的Python导入路径的 最后部分; django.contrib.contenttypes,例如,成为一个 app_label的contenttypes。 model¶ (这个是你模型的表名称,全小写)
模型类的名称。 此外,还提供以下房产: name¶
人类可读的内容类型名称。这取自verbose_name 模型的 属性。 让我们看一个例子来看看它是如何工作的。如果您已安装该contenttypes应用程序,然后添加 到您的 设置并运行以进行安装,则该模型将安装到您的数据库中。除此之外,还将使用以下值创建一个新实例 :the sites applicationINSTALLED_APPSmanage.py migratedjango.contrib.sites.models.SiteContentType app_label 将被设置为'sites'(Python路径的最后一部分django.contrib.sites)。
model 将被设置为'site'。
ContentType实例上的方法¶
每个ContentType实例都有一些方法,允许您从ContentType实例获取 它所代表的模型,或者从该模型中检索对象:
content_type = Content_type.objects.get(app_label="XXX", model="xxx") (下面用的ContConentType=content_type.model_class()就是这样查找出来的)
还有一个办法可以拿到模型多有的对象 content_type.model_class._default_manager.all() (红色部分其实就是objects模型管理器对象)
ContConentType.get_object_for_this_type(** kwargs)¶ (uuid="ssfdsfa")其实就是传入对应的字段与get相同
为 表示的模型获取一组有效的查找参数ContentType,并 在该模型上执行,返回相应的对象。a get() lookup ContentType.model_class()¶ # 可以拿到对应的模型 他很重要
返回此ContentType实例表示的模型类 。 例如,我们可以仰望 ContentType的 User模型: >>> from django.contrib.contenttypes.models import ContentType
>>> user_type = ContentType.objects.get(app_label='auth', model='user')
>>> user_type
<ContentType: user>
然后使用它来查询特定的 User,或者访问User模型类: >>> user_type.model_class()
55 <class 'django.contrib.auth.models.User'>
>>> user_type.get_object_for_this_type(username='Guido')
<User: Guido>
一起, get_object_for_this_type() 并model_class()启用两个非常重要的用例: 使用这些方法,您可以编写对任何已安装模型执行查询的高级通用代码 - 您可以在运行时将一个app_label并 model传入 ContentType查找,然后使用模型类或从中检索对象。
您可以将另一个模型与 ContentType将其实例绑定到特定模型类的方法相关联,并使用这些方法来访问这些模型类。
Django的几个捆绑应用程序使用后一种技术。例如, 在Django的身份验证框架中使用带有外键的 模型; 这可以 代表“可以添加博客条目”或“可以删除新闻故事”等概念。the permissions systemPermissionContentTypePermission 该ContentTypeManager¶
类ContentTypeManager¶
ContentType还有一个自定义管理器,ContentTypeManager它添加了以下方法: clear_cache()¶
清除用于ContentType跟踪其已创建ContentType实例的模型 的内部缓存 。您可能不需要自己调用此方法; Django会在需要时自动调用它。 get_for_id(id)¶
ContentType按ID 查找。由于此方法使用相同的共享缓存 get_for_model(),因此优先使用此方法 ContentType.objects.get(pk=id) get_for_model(model,for_concrete_model = True)¶ (传入模型名)
获取模型类或模型的实例,并返回ContentType表示该模型的 实例。for_concrete_model=False允许获取ContentType代理模型。 get_for_models(* models,for_concrete_models = True)¶
获取可变数量的模型类,并返回将模型类映射到ContentType表示它们的实例的字典 。for_concrete_models=False允许获取 ContentType代理模型。 get_by_natural_key(app_label,model)¶
返回ContentType 由给定应用程序标签和模型名称唯一标识的实例。此方法的主要目的是允许 在反序列化期间ContentType通过自然键引用对象。 get_for_model()当您知道需要使用a ContentType但不想在获取模型的元数据以执行手动查找时遇到麻烦,该方法特别有用 : >>> from django.contrib.auth.models import User
>>> ContentType.objects.get_for_model(User)
<ContentType: user>
通用关系¶
从您自己的模型中添加外键,以 ContentType允许模型有效地将自身绑定到另一个模型类,如Permission上面模型的示例 所示。但是可以更进一步,用于 ContentType在模型之间实现真正通用(有时称为“多态”)关系。 一个简单的例子是标记系统,它可能如下所示: from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models class TaggedItem(models.Model):
tag = models.SlugField()
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id') def __str__(self):
return self.tag
法线ForeignKey只能“指向”另一个模型,这意味着如果TaggedItem模型使用了 ForeignKey它,则必须选择一个且只有一个模型来存储标签。contenttypes应用程序提供了一个特殊的字段类型(GenericForeignKey),它可以解决这个问题,并允许关系与任何模型: 类GenericForeignKey¶
设置一个有三个部分 GenericForeignKey: 给你的模型ForeignKey 来ContentType。该字段的通常名称是“content_type”。
为您的模型提供一个字段,该字段可以存储您要与之相关的模型中的主键值。对于大多数模型,这意味着一个 PositiveIntegerField。该字段的通常名称是“object_id”。
给你的模型a GenericForeignKey,并传递上述两个字段的名称。如果这些字段名为“content_type”和“object_id”,则可以省略 - 这些是GenericForeignKey将要查找的默认字段名称 。
for_concrete_model¶
如果False,该字段将能够引用代理模型。默认是True。这反映了这个for_concrete_model论点 get_for_model()。 主键类型兼容性 “object_id”字段不必与相关模型上的主键字段具有相同的类型,但它们的主键值必须通过其get_db_prep_value()方法可强制化为与“object_id”字段相同的类型 。 例如,如果要允许与具有任一IntegerField或 CharField主键字段的模型的泛型关系 ,则可以使用CharField模型上的“object_id”字段,因为整数可以通过强制转换为字符串get_db_prep_value()。 为了获得最大的灵活性,您可以使用 TextField没有定义最大长度的a,但是这可能会导致严重的性能损失,具体取决于您的数据库后端。 对于哪种字段类型最好,没有一种通用的解决方案。您应该评估您希望指向的模型,并确定哪种解决方案对您的用例最有效。 序列化ContentType对象的引用 如果fixtures从实现泛型关系的模型中序列化数据(例如,生成时 ),则应该使用自然键来唯一标识相关ContentType 对象。请参阅自然键和 更多信息。dumpdata --natural-foreign 这将启用类似于用于正常的API的API ForeignKey; 每个TaggedItem都有一个content_object返回与其相关的对象的字段,您也可以在创建时使用该字段或使用它TaggedItem: >>> from django.contrib.auth.models import User
>>> guido = User.objects.get(username='Guido')
>>> t = TaggedItem(content_object=guido, tag='bdfl')
>>> t.save()
>>> t.content_object
<User: Guido>
如果删除了相关对象,则content_type和object_id字段仍将设置为其原始值并GenericForeignKey返回 None: >>> guido.delete()
>>> t.content_object # returns None
由于方式GenericForeignKey 实现,你不能直接使用的过滤器等领域(filter() 以及exclude()通过数据库API,例如)。由于 GenericForeignKey不正常的领域对象,这些例子不工作: # This will fail
>>> TaggedItem.objects.filter(content_object=guido)
# This will also fail
>>> TaggedItem.objects.get(content_object=guido)
同样,GenericForeignKeys没有出现在ModelForms中。 反向泛型关系¶
类GenericRelation¶
related_query_name¶
默认情况下,相关对象与该对象的关系不存在。设置related_query_name创建从相关对象回到此关系的关系。这允许从相关对象查询和过滤。 如果您知道最常使用的模型,还可以添加“反向”通用关系以启用其他API。例如: from django.contrib.contenttypes.fields import GenericRelation
from django.db import models class Bookmark(models.Model):
url = models.URLField()
tags = GenericRelation(TaggedItem)
Bookmark每个实例都有一个tags属性,可以用来检索它们的关联TaggedItems: >>> b = Bookmark(url='https://www.djangoproject.com/')
>>> b.save()
>>> t1 = TaggedItem(content_object=b, tag='django')
>>> t1.save()
>>> t2 = TaggedItem(content_object=b, tag='python')
>>> t2.save()
>>> b.tags.all()
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
GenericRelation使用 related_query_nameset 定义允许从相关对象查询: tags = GenericRelation(TaggedItem, related_query_name='bookmark')
这使得过滤,排序等查询操作上的Bookmark 来自TaggedItem: >>> # Get all tags belonging to bookmarks containing `django` in the url
>>> TaggedItem.objects.filter(bookmark__url__contains='django')
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
当然,如果您不添加related_query_name,您可以手动执行相同类型的查找: >>> bookmarks = Bookmark.objects.filter(url__contains='django')
>>> bookmark_type = ContentType.objects.get_for_model(Bookmark)
>>> TaggedItem.objects.filter(content_type__pk=bookmark_type.id, object_id__in=bookmarks)
<QuerySet [<TaggedItem: django>, <TaggedItem: python>]>
就像GenericForeignKey 接受content-type和object-ID字段的名称一样,也是如此 GenericRelation; 如果具有通用外键的模型对这些字段使用非默认名称,则必须在设置字段时传递字段的名称 GenericRelation。例如,如果TaggedItem上面提到的模型使用了命名的字段content_type_fk并 object_primary_key创建了它的通用外键,那么 GenericRelation需要像这样定义返回它: tags = GenericRelation(
TaggedItem,
content_type_field='content_type_fk',
object_id_field='object_primary_key',
)
另请注意,如果删除具有a的对象,则 GenericRelation任何GenericForeignKey 指向该对象的对象也将被删除。在上面的示例中,这意味着如果Bookmark删除了某个对象,TaggedItem则会同时删除指向该对象的任何对象。 不同ForeignKey, GenericForeignKey不接受on_delete自定义此行为的参数; 如果需要,您可以通过不使用来避免级联删除 GenericRelation,并且可以通过pre_delete 信号提供替代行为。 通用关系和聚合¶
Django的数据库聚合API适用于 GenericRelation。例如,您可以找出所有书签的标签数量: >>> Bookmark.objects.aggregate(Count('tags'))
{'tags__count': 3}
表格中的通用关系¶
该django.contrib.contenttypes.forms模块提供: BaseGenericInlineFormSet
一个formset工厂,generic_inlineformset_factory()用于 GenericForeignKey。
类BaseGenericInlineFormSet¶
generic_inlineformset_factory(model,form = ModelForm,formset = BaseGenericInlineFormSet,ct_field =“content_type”,fk_field =“object_id”,fields = None,exclude = None,extra = 3,can_order = False,can_delete = True,max_num = None,formfield_callback = None,validate_max = False,for_concrete_model = True,min_num = None,validate_min = False)¶
返回一个GenericInlineFormSet使用 modelformset_factory()。 您必须提供ct_field与fk_field他们是否是不同的默认值,content_type并object_id分别。其它参数类似于那些记录 modelformset_factory()和 inlineformset_factory()。 该for_concrete_model参数对应 for_concrete_model 的参数GenericForeignKey。 管理中的通用关系¶
该django.contrib.contenttypes.admin模块提供 GenericTabularInline和 GenericStackedInline(子类 GenericInlineModelAdmin) 这些类和函数支持在表单和管理员中使用通用关系。有关更多信息,请参阅模型formset和 管理文档。 类GenericInlineModelAdmin¶
在GenericInlineModelAdmin 类继承自的所有属性 InlineModelAdmin类。但是,它增加了一些用于处理泛型关系的东西: ct_field¶
ContentType模型上的外键字段的名称 。默认为content_type。 ct_fk_field¶
表示相关对象ID的整数字段的名称。默认为object_id。 类GenericTabularInline¶
类GenericStackedInline¶
GenericInlineModelAdmin分别具有堆叠和表格布局的子类。
11.关于django的content_type表的更多相关文章
- url路由、模板语言、ajax、用django框架创建表
1.后台管理的左侧菜单,默认只有第一个页签下面的选项是显示的,点了别的页签再显示别的页签下面的选项,问题是:点了任何菜单的选项后,左侧菜单又成了第一个页签的选项显示,别的页签隐藏,也就是左侧的菜单刷新 ...
- Django 组件content_type
content type: django内置组件,这个组件帮忙做连表操作(混搭连表) 适用场景:适用于一张表与多张表同时做关联的时候.直接导入就可以使用了. 关联数据库所有的表:可以快速插入数据,并且 ...
- Django之mysql表单操作
在Django之ORM模型中总结过django下mysql表的创建操作,接下来总结mysql表记录操作,包括表记录的增.删.改.查. 1. 添加表记录 class UserInfo(models.Mo ...
- 转载:Django之form表单
转载: 一.使用form类创建一个表单 先定义好一个RegForm类: forms.py from django import forms # 导入forms类 class NameForm(form ...
- Django开发之路 二(django的models表查询)
django的models表查询 一.单表查询 (1) all(): 查询所有结果 # 返回的QuerySet类型 (2) filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 #返 ...
- Django 的ORM 表间操作
Django之ORM表间操作 之前完成了简单的数据库数据增加操作.这次学习更多的表间操作. 单表操作 增加 方式一 b = Book(title="Python基础", pub ...
- Django之form表单详解
构建一个表单 假设你想在你的网站上创建一个简单的表单,以获得用户的名字.你需要类似这样的模板: <form action="/your-name/" method=" ...
- Django生成数据表时报错
Django生成数据表时报错 WARNINGS: ?: (mysql.W002) MySQL Strict Mode is not set for database connection 'defau ...
- Django ORM 多表操作
目录 Django ORM 多表操作 表模型 表关系 创建模型 逆向到表模型 插入数据 ORM 添加数据(添加外键) 一对多(外键 ForeignKey) 一对一 (OneToOneFeild) 多对 ...
随机推荐
- tagged和untagged
tagged和untagged遵循以下五条原则 1. Tagged数据帧 Tagged数据帧 Untagged数据帧 Untagged数据帧 in out in out Tagged端口 原样 ...
- USACO1.6 Superprime Rib
题目传送门 每一个特殊质数都会被从右边切掉,所以除了首位外的其它位数一定都不会是偶数,只能是$1$,$3$,$5$,$7$,$9$ 而每一个特殊质数的首位一定是质数,也就是$2$,$3$,$5$,$7 ...
- 定时任务crontab命令
linux 系统则是由 cron (crond) 这个系统服务来控制的.Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的.另外, 由于用户自己也可以设置计划任务,所以,Li ...
- poj3252(数位dp)(模板)
题目链接:https://vjudge.net/problem/POJ-3252 题意:求[l,r]之间的Round Number数,RN数即化为二进制后0的个数不少于1的个数的数. 思路:之前用组合 ...
- [转帖]Linux网络管理员不得不了解的系统目录/proc/sys/net/(网络配置)
Linux网络管理员不得不了解的系统目录/proc/sys/net/(网络配置) https://blog.csdn.net/u013485792/article/details/76416836 需 ...
- 洛谷 P1073 最优贸易 题解
题面 大家都是两遍SPFA吗?我这里就一遍dp啊: 首先判断对于一个点u,是否可以从一号点走到这里,并且可以从u走到n号点: 对于这样的点我们打上标记: 那么抛出水晶球的点一定是从打上标记的点中选出一 ...
- Android尺寸适配问题
1, 布局与组件大小用dp,文字大小用sp 2,
- Python小白需要知道的 20 个骚操作!
Python小白需要知道的 20 个骚操作! Python 是一个解释型语言,可读性与易用性让它越来越热门.正如 Python 之禅中所述: 优美胜于丑陋,明了胜于晦涩. 在你的日常编码中,以下技巧可 ...
- 字符集详解 ASCII码、Unicode、UTF-8 (转)
认识字符集 对于计算机而言,它仅认识两个0和1,不管是在内存中还是外部存储设备上,我们所看到的文字.图片.视频等等“数据”在计算机中都是已二进制形式存在的.不同字符对应二进制数的规则,就是字符的编码. ...
- 带EFI支持的GRUB2安装全记录
版权归作者所有,任何形式转载请联系作者. 作者:keenshoes(来自豆瓣) 来源:https://www.douban.com/note/210077866/ 关键词:EFIGRUB2efibo ...